From 45bf3bfbb061b87511ece6f50b1aa6c803844a65 Mon Sep 17 00:00:00 2001 From: Kibum Kim Date: Mon, 27 Feb 2012 21:16:50 +0900 Subject: [PATCH] tizen beta release --- 3rdparty/CMakeLists.txt | 63 + 3rdparty/DESCRIPTION | 1 + 3rdparty/fastdelegate/CPOL.html | 251 + 3rdparty/fastdelegate/DESCRIPTION | 1 + 3rdparty/fastdelegate/Demo.cpp | 143 + 3rdparty/fastdelegate/LICENSE | 7 + 3rdparty/minizip/DESCRIPTION | 1 + 3rdparty/minizip/Makefile | 25 + 3rdparty/minizip/MiniZip64_Changes.txt | 6 + 3rdparty/minizip/MiniZip64_info.txt | 74 + 3rdparty/minizip/crypt.h | 131 + 3rdparty/minizip/framework_minizip.h | 8 + 3rdparty/minizip/ioapi.c | 235 + 3rdparty/minizip/ioapi.h | 200 + 3rdparty/minizip/iowin32.c | 389 + 3rdparty/minizip/iowin32.h | 28 + 3rdparty/minizip/make_vms.com | 25 + 3rdparty/minizip/miniunz.c | 648 ++ 3rdparty/minizip/minizip.c | 507 ++ 3rdparty/minizip/mztools.c | 281 + 3rdparty/minizip/mztools.h | 31 + 3rdparty/minizip/unzip.c | 2125 +++++ 3rdparty/minizip/unzip.h | 437 + 3rdparty/minizip/unzip11.zip | Bin 0 -> 51431 bytes 3rdparty/minizip/zip.c | 2004 +++++ 3rdparty/minizip/zip.h | 362 + CMakeLists.txt | 203 + LICENSE | 203 + NOTICE | 1 + bin/run_all.sh | 6 + build/CMakeLists.txt | 33 + build/ace/CMakeLists.txt | 30 + build/ace/dpl-ace-dao-ro.pc.in | 11 + build/ace/dpl-ace-dao-rw.pc.in | 11 + build/ace/dpl-ace.pc.in | 11 + build/core/CMakeLists.txt | 88 + build/core/DESCRIPTION | 2 + build/core/dpl-efl.pc | 11 + build/db/CMakeLists.txt | 68 + build/db/dpl-db-efl.pc | 11 + build/dbus/CMakeLists.txt | 69 + build/dbus/dpl-dbus-efl.pc | 11 + build/event/CMakeLists.txt | 71 + build/event/dpl-event-efl.pc | 11 + build/log/CMakeLists.txt | 65 + build/log/dpl-log-efl.pc | 11 + build/popup/CMakeLists.txt | 68 + build/popup/dpl-popup-efl.pc | 11 + build/rpc/CMakeLists.txt | 74 + build/rpc/dpl-rpc-efl.pc | 11 + build/socket/CMakeLists.txt | 72 + build/socket/dpl-socket-efl.pc | 11 + build/test/CMakeLists.txt | 67 + build/test/dpl-test-efl.pc | 11 + build/utils/CMakeLists.txt | 85 + build/utils/dpl-utils-efl.pc | 11 + build/vcore/CMakeLists.txt | 21 + build/vcore/dpl-vcore.pc.in | 11 + build/widget_dao/CMakeLists.txt | 26 + build/widget_dao/dpl-wrt-dao-ro.pc.in | 12 + build/widget_dao/dpl-wrt-dao-rw.pc.in | 12 + debian/DESCRIPTION | 1 + debian/README.Debian | 6 + debian/changelog | 26 + debian/compat | 1 + debian/control | 83 + debian/copyright | 0 debian/dirs | 0 debian/rules | 84 + debian/wrt-commons-dev.dirs | 5 + debian/wrt-commons-dev.install | 2 + debian/wrt-commons-test.install | 13 + debian/wrt-commons.dirs | 2 + debian/wrt-commons.install | 10 + debian/wrt-commons.postinst | 63 + dir-struct.py | 126 + doc/DESCRIPTION | 1 + doc/doxyfile | 1600 ++++ doc/dpl_programming_guide.docx | Bin 0 -> 250036 bytes doc/dpl_programming_guide.pdf | Bin 0 -> 851605 bytes etc/CMakeLists.txt | 25 + etc/DESCRIPTION | 1 + etc/certificates/CMakeLists.txt | 30 + etc/certificates/tizen.root.preproduction.cert.pem | 60 + etc/certificates/wac.publisherid.pem | 24 + etc/certificates/wac.root.preproduction.pem | 22 + etc/certificates/wac.root.production.pem | 22 + etc/fingerprint_list.xml | 21 + etc/fingerprint_list.xsd | 21 + etc/schema.xsd | 415 + etc/wrt_create_clean_db.sh | 31 + etc/wrt_reset_db.sh | 49 + examples/CMakeLists.txt | 42 + examples/DESCRIPTION | 2 + examples/binary_queue/CMakeLists.txt | 32 + examples/binary_queue/binary_queue.cpp | 62 + examples/copy/CMakeLists.txt | 32 + examples/copy/copy.cpp | 48 + examples/crypto_hash/CMakeLists.txt | 32 + examples/crypto_hash/crypto_hash.cpp | 81 + examples/dbus/client-example/CMakeLists.txt | 24 + examples/dbus/client-example/client-example.cpp | 38 + examples/dbus/server-example/CMakeLists.txt | 26 + examples/dbus/server-example/server-example.cpp | 125 + examples/event_delivery_test/CMakeLists.txt | 34 + .../event_delivery_test/event_delivery_test.cpp | 202 + examples/fake_rpc/CMakeLists.txt | 33 + examples/fake_rpc/fake_rpc.cpp | 281 + examples/metronome/CMakeLists.txt | 38 + examples/metronome/metronome_client.cpp | 114 + examples/metronome/metronome_server.cpp | 167 + examples/rpc/CMakeLists.txt | 32 + examples/rpc/rpc.cpp | 274 + examples/simple/CMakeLists.txt | 32 + examples/simple/simple.cpp | 31 + examples/single_instance/CMakeLists.txt | 32 + examples/single_instance/single_instance.cpp | 55 + examples/socket/CMakeLists.txt | 32 + examples/socket/socket.cpp | 154 + examples/tcpsock/CMakeLists.txt | 32 + examples/tcpsock/tcpsock.cpp | 110 + examples/timed_event/CMakeLists.txt | 32 + examples/timed_event/timed_event.cpp | 101 + modules/CMakeLists.txt | 35 + modules/ace/CMakeLists.txt | 169 + modules/ace/DESCRIPTION | 2 + modules/ace/configuration/UnrestrictedPolicy.xml | 5 + modules/ace/configuration/WACPolicy.xml | 520 ++ modules/ace/configuration/bondixml.xsd | 184 + modules/ace/configuration/config.dtd | 10 + modules/ace/configuration/config.xml | 10 + modules/ace/configuration/demo.xml | 15 + modules/ace/dao/AceDAO.cpp | 368 + modules/ace/dao/AceDAOConversions.cpp | 54 + modules/ace/dao/AceDAOReadOnly.cpp | 338 + modules/ace/dao/AceDAOUtilities.cpp | 189 + modules/ace/dao/AceDatabase.cpp | 25 + modules/ace/dao/BaseAttribute.cpp | 82 + modules/ace/dao/CMakeLists.txt | 110 + modules/ace/dao/PromptModel.cpp | 178 + modules/ace/dao/common_dao_types.cpp | 164 + modules/ace/engine/Attribute.cpp | 913 ++ modules/ace/engine/CombinerImpl.cpp | 445 + modules/ace/engine/Condition.cpp | 265 + modules/ace/engine/ConfigurationManager.cpp | 422 + modules/ace/engine/NodeFactory.cpp | 49 + modules/ace/engine/Policy.cpp | 100 + modules/ace/engine/PolicyEnforcementPoint.cpp | 133 + modules/ace/engine/PolicyEvaluator.cpp | 447 + modules/ace/engine/PolicyInformationPoint.cpp | 198 + modules/ace/engine/Rule.cpp | 90 + modules/ace/engine/Serializer.cpp | 402 + modules/ace/engine/SettingsLogic.cpp | 172 + modules/ace/engine/Subject.cpp | 84 + modules/ace/engine/TreeNode.cpp | 141 + modules/ace/engine/parser.cpp | 744 ++ .../ace/include/dpl/ace-dao-ro/AceDAOConversions.h | 39 + .../ace/include/dpl/ace-dao-ro/AceDAOReadOnly.h | 106 + .../ace/include/dpl/ace-dao-ro/AceDAOUtilities.h | 55 + modules/ace/include/dpl/ace-dao-ro/AceDatabase.h | 56 + modules/ace/include/dpl/ace-dao-ro/BaseAttribute.h | 149 + .../ace/include/dpl/ace-dao-ro/BasePermission.h | 53 + modules/ace/include/dpl/ace-dao-ro/IRequest.h | 38 + .../ace/include/dpl/ace-dao-ro/PreferenceTypes.h | 48 + modules/ace/include/dpl/ace-dao-ro/PromptModel.h | 93 + modules/ace/include/dpl/ace-dao-ro/TimedVerdict.h | 42 + modules/ace/include/dpl/ace-dao-ro/ValidityTypes.h | 40 + modules/ace/include/dpl/ace-dao-ro/VerdictTypes.h | 45 + .../ace/include/dpl/ace-dao-ro/common_dao_types.h | 487 ++ modules/ace/include/dpl/ace-dao-ro/wrt_db_types.h | 38 + modules/ace/include/dpl/ace-dao-rw/AceDAO.h | 111 + .../dpl/ace/AbstractPolicyEnforcementPoint.h | 31 + .../dpl/ace/AbstractPolicyInformationPoint.h | 21 + modules/ace/include/dpl/ace/AbstractTreeElement.h | 46 + .../include/dpl/ace/AsyncVerdictResultListener.h | 33 + modules/ace/include/dpl/ace/Attribute.h | 220 + modules/ace/include/dpl/ace/Combiner.h | 58 + modules/ace/include/dpl/ace/CombinerImpl.h | 86 + modules/ace/include/dpl/ace/Condition.h | 139 + modules/ace/include/dpl/ace/ConfigurationManager.h | 202 + modules/ace/include/dpl/ace/Constants.h | 53 + modules/ace/include/dpl/ace/Effect.h | 44 + modules/ace/include/dpl/ace/NodeFactory.h | 39 + modules/ace/include/dpl/ace/PermissionTriple.h | 48 + modules/ace/include/dpl/ace/Policy.h | 91 + modules/ace/include/dpl/ace/PolicyEffect.h | 47 + .../ace/include/dpl/ace/PolicyEnforcementPoint.h | 95 + modules/ace/include/dpl/ace/PolicyEvaluator.h | 145 + .../ace/include/dpl/ace/PolicyEvaluatorFactory.h | 45 + .../ace/include/dpl/ace/PolicyInformationPoint.h | 105 + modules/ace/include/dpl/ace/PolicyResult.h | 250 + modules/ace/include/dpl/ace/PolicySet.h | 52 + modules/ace/include/dpl/ace/Preference.h | 38 + modules/ace/include/dpl/ace/PromptDecision.h | 41 + modules/ace/include/dpl/ace/Request.h | 87 + modules/ace/include/dpl/ace/Rule.h | 85 + modules/ace/include/dpl/ace/Serializer.h | 129 + modules/ace/include/dpl/ace/SettingsLogic.h | 88 + modules/ace/include/dpl/ace/Subject.h | 77 + modules/ace/include/dpl/ace/TestTimer.h | 41 + modules/ace/include/dpl/ace/TreeNode.h | 135 + modules/ace/include/dpl/ace/UserDecision.h | 37 + modules/ace/include/dpl/ace/Verdict.h | 39 + modules/ace/include/dpl/ace/WRT_INTERFACE.h | 144 + modules/ace/include/dpl/ace/WidgetUsageModel.h | 97 + modules/ace/include/dpl/ace/acf_consts.h | 41 + modules/ace/include/dpl/ace/parser.h | 107 + modules/ace/orm/ace_db | 60 + modules/ace/orm/ace_db_definitions | 6 + modules/ace/orm/ace_db_sql_generator.h | 27 + modules/ace/orm/gen_db_md5.sh | 19 + modules/ace/orm/orm_generator_ace.h | 24 + modules/ace/orm/version_db | 5 + modules/core/DESCRIPTION | 1 + modules/core/config.cmake | 159 + modules/core/include/DESCRIPTION | 2 + .../dpl/3rdparty/fastdelegate/FastDelegate.h | 2108 +++++ .../dpl/3rdparty/fastdelegate/FastDelegateBind.h | 243 + modules/core/include/dpl/abstract_input.h | 59 + modules/core/include/dpl/abstract_input_output.h | 40 + modules/core/include/dpl/abstract_output.h | 60 + modules/core/include/dpl/abstract_waitable_input.h | 42 + .../include/dpl/abstract_waitable_input_adapter.h | 49 + .../include/dpl/abstract_waitable_input_output.h | 41 + .../dpl/abstract_waitable_input_output_adapter.h | 42 + .../core/include/dpl/abstract_waitable_output.h | 42 + .../include/dpl/abstract_waitable_output_adapter.h | 49 + modules/core/include/dpl/address.h | 61 + modules/core/include/dpl/aligned.h | 27 + modules/core/include/dpl/application.h | 106 + modules/core/include/dpl/apply.h | 199 + modules/core/include/dpl/assert.h | 37 + modules/core/include/dpl/atomic.h | 50 + modules/core/include/dpl/binary_queue.h | 284 + modules/core/include/dpl/bool_operator.h | 38 + modules/core/include/dpl/char_traits.h | 39 + modules/core/include/dpl/colors.h | 85 + modules/core/include/dpl/copy.h | 55 + modules/core/include/dpl/enable_shared_from_this.h | 72 + modules/core/include/dpl/errno_string.h | 36 + modules/core/include/dpl/exception.h | 357 + modules/core/include/dpl/fast_delegate.h | 38 + modules/core/include/dpl/file_input.h | 63 + modules/core/include/dpl/file_input_mapping.h | 73 + modules/core/include/dpl/file_output.h | 63 + modules/core/include/dpl/foreach.h | 66 + modules/core/include/dpl/framework_appcore.h | 23 + modules/core/include/dpl/framework_efl.h | 25 + modules/core/include/dpl/framework_vconf.h | 24 + modules/core/include/dpl/generic_event.h | 514 ++ modules/core/include/dpl/lexical_cast.h | 44 + modules/core/include/dpl/main.h | 86 + modules/core/include/dpl/mutex.h | 68 + modules/core/include/dpl/named_base_pipe.h | 49 + modules/core/include/dpl/named_input_pipe.h | 64 + modules/core/include/dpl/named_output_pipe.h | 64 + modules/core/include/dpl/noncopyable.h | 39 + modules/core/include/dpl/noreturn.h | 27 + modules/core/include/dpl/once.h | 46 + modules/core/include/dpl/optional.h | 175 + modules/core/include/dpl/optional_typedefs.h | 33 + modules/core/include/dpl/preprocessor.h | 29 + modules/core/include/dpl/read_write_mutex.h | 82 + modules/core/include/dpl/recursive_mutex.h | 69 + modules/core/include/dpl/scoped_array.h | 60 + modules/core/include/dpl/scoped_close.h | 72 + modules/core/include/dpl/scoped_fclose.h | 74 + modules/core/include/dpl/scoped_free.h | 52 + modules/core/include/dpl/scoped_gpointer.h | 79 + modules/core/include/dpl/scoped_ptr.h | 65 + modules/core/include/dpl/scoped_resource.h | 79 + modules/core/include/dpl/semaphore.h | 125 + modules/core/include/dpl/serialization.h | 291 + modules/core/include/dpl/shared_ptr.h | 282 + modules/core/include/dpl/single_instance.h | 56 + modules/core/include/dpl/singleton.h | 106 + modules/core/include/dpl/singleton_impl.h | 80 + modules/core/include/dpl/singleton_safe_impl.h | 66 + modules/core/include/dpl/sstream.h | 42 + modules/core/include/dpl/string.h | 110 + modules/core/include/dpl/task.h | 535 ++ modules/core/include/dpl/task_list.h | 58 + modules/core/include/dpl/thread.h | 389 + modules/core/include/dpl/type_list.h | 162 + modules/core/include/dpl/union_cast.h | 45 + modules/core/include/dpl/unused.h | 27 + modules/core/include/dpl/waitable_event.h | 62 + modules/core/include/dpl/waitable_handle.h | 106 + .../include/dpl/waitable_handle_watch_support.h | 153 + modules/core/include/dpl/workaround.h | 43 + modules/core/include/dpl/zip_input.h | 254 + modules/core/src/DESCRIPTION | 1 + .../core/src/abstract_waitable_input_adapter.cpp | 43 + .../src/abstract_waitable_input_output_adapter.cpp | 33 + .../core/src/abstract_waitable_output_adapter.cpp | 43 + modules/core/src/address.cpp | 71 + modules/core/src/application.cpp | 194 + modules/core/src/apply.cpp | 30 + modules/core/src/assert.cpp | 61 + modules/core/src/atomic.cpp | 55 + modules/core/src/binary_queue.cpp | 310 + modules/core/src/char_traits.cpp | 31 + modules/core/src/colors.cpp | 83 + modules/core/src/copy.cpp | 140 + modules/core/src/errno_string.cpp | 103 + modules/core/src/exception.cpp | 52 + modules/core/src/fast_delegate.cpp | 22 + modules/core/src/file_input.cpp | 140 + modules/core/src/file_input_mapping.cpp | 110 + modules/core/src/file_output.cpp | 120 + modules/core/src/generic_event.cpp | 30 + modules/core/src/lexical_cast.cpp | 30 + modules/core/src/main.cpp | 484 ++ modules/core/src/mutex.cpp | 96 + modules/core/src/named_base_pipe.cpp | 60 + modules/core/src/named_input_pipe.cpp | 121 + modules/core/src/named_output_pipe.cpp | 101 + modules/core/src/noncopyable.cpp | 33 + modules/core/src/once.cpp | 45 + modules/core/src/read_write_mutex.cpp | 80 + modules/core/src/recursive_mutex.cpp | 67 + modules/core/src/semaphore.cpp | 240 + modules/core/src/serialization.cpp | 30 + modules/core/src/single_instance.cpp | 119 + modules/core/src/singleton.cpp | 30 + modules/core/src/string.cpp | 255 + modules/core/src/task.cpp | 31 + modules/core/src/task_list.cpp | 111 + modules/core/src/thread.cpp | 559 ++ modules/core/src/type_list.cpp | 30 + modules/core/src/union_cast.cpp | 30 + modules/core/src/waitable_event.cpp | 69 + modules/core/src/waitable_handle.cpp | 153 + modules/core/src/waitable_handle_watch_support.cpp | 346 + modules/core/src/zip_input.cpp | 622 ++ modules/db/config.cmake | 45 + .../include/dpl/db/naive_synchronization_object.h | 48 + modules/db/include/dpl/db/orm.h | 704 ++ modules/db/include/dpl/db/orm_generator.h | 380 + modules/db/include/dpl/db/orm_interface.h | 51 + modules/db/include/dpl/db/orm_macros.h | 33 + modules/db/include/dpl/db/sql_connection.h | 482 ++ .../db/include/dpl/db/thread_database_support.h | 303 + modules/db/src/naive_synchronization_object.cpp | 42 + modules/db/src/orm.cpp | 102 + modules/db/src/sql_connection.cpp | 862 ++ modules/db/src/thread_database_support.cpp | 23 + modules/dbus/config.cmake | 55 + modules/dbus/include/dpl/dbus/connection.h | 211 + modules/dbus/include/dpl/dbus/dbus_client.h | 240 + .../dbus/include/dpl/dbus/dbus_deserialization.h | 219 + .../include/dpl/dbus/dbus_interface_dispatcher.h | 107 + modules/dbus/include/dpl/dbus/dbus_serialization.h | 158 + .../include/dpl/dbus/dbus_server_deserialization.h | 181 + .../include/dpl/dbus/dbus_server_serialization.h | 98 + modules/dbus/include/dpl/dbus/dbus_signature.h | 262 + modules/dbus/include/dpl/dbus/dispatcher.h | 101 + modules/dbus/include/dpl/dbus/exception.h | 49 + modules/dbus/include/dpl/dbus/interface.h | 115 + modules/dbus/include/dpl/dbus/method_proxy.h | 232 + modules/dbus/include/dpl/dbus/object.h | 74 + modules/dbus/include/dpl/dbus/object_proxy.h | 87 + modules/dbus/include/dpl/dbus/server.h | 95 + modules/dbus/src/connection.cpp | 293 + modules/dbus/src/dispatcher.cpp | 54 + modules/dbus/src/interface.cpp | 204 + modules/dbus/src/object.cpp | 50 + modules/dbus/src/object_proxy.cpp | 44 + modules/dbus/src/server.cpp | 120 + modules/event/config.cmake | 63 + .../event/include/dpl/event/abstract_event_call.h | 56 + .../include/dpl/event/abstract_event_dispatcher.h | 68 + modules/event/include/dpl/event/controller.h | 149 + modules/event/include/dpl/event/event_delivery.h | 338 + .../include/dpl/event/event_delivery_detail.h | 306 + .../include/dpl/event/event_delivery_injector.h | 81 + .../include/dpl/event/event_delivery_messages.h | 923 ++ modules/event/include/dpl/event/event_listener.h | 51 + modules/event/include/dpl/event/event_support.h | 788 ++ .../event/include/dpl/event/generic_event_call.h | 101 + .../include/dpl/event/inter_context_delegate.h | 397 + .../include/dpl/event/main_event_dispatcher.h | 120 + modules/event/include/dpl/event/model.h | 52 + .../event/include/dpl/event/model_bind_to_dao.h | 77 + modules/event/include/dpl/event/nested_loop.h | 111 + modules/event/include/dpl/event/property.h | 522 ++ .../include/dpl/event/thread_event_dispatcher.h | 59 + modules/event/src/abstract_event_call.cpp | 38 + modules/event/src/abstract_event_dispatcher.cpp | 38 + modules/event/src/controller.cpp | 30 + modules/event/src/event_delivery.cpp | 50 + modules/event/src/event_delivery_detail.cpp | 568 ++ modules/event/src/event_listener.cpp | 30 + modules/event/src/event_support.cpp | 43 + modules/event/src/generic_event_call.cpp | 30 + modules/event/src/inter_context_delegate.cpp | 30 + modules/event/src/main_event_dispatcher.cpp | 332 + modules/event/src/model.cpp | 32 + modules/event/src/nested_loop.cpp | 120 + modules/event/src/thread_event_dispatcher.cpp | 106 + modules/localization/config.cmake | 38 + .../include/dpl/localization/localization_utils.h | 90 + .../dpl/localization/w3c_file_localization.h | 60 + modules/localization/src/localization_utils.cpp | 155 + modules/localization/src/w3c_file_localization.cpp | 315 + modules/log/config.cmake | 41 + .../log/include/dpl/log/abstract_log_provider.h | 48 + modules/log/include/dpl/log/dlog_log_provider.h | 58 + modules/log/include/dpl/log/log.h | 159 + .../log/include/dpl/log/old_style_log_provider.h | 58 + modules/log/src/abstract_log_provider.cpp | 35 + modules/log/src/dlog_log_provider.cpp | 82 + modules/log/src/log.cpp | 194 + modules/log/src/old_style_log_provider.cpp | 115 + modules/popup/DESCRIPTION | 2 + modules/popup/config.cmake | 42 + modules/popup/include/dpl/popup/evas_object.h | 661 ++ modules/popup/include/dpl/popup/popup.h | 74 + modules/popup/include/dpl/popup/popup_controller.h | 217 + modules/popup/include/dpl/popup/popup_manager.h | 114 + modules/popup/include/dpl/popup/popup_object.h | 175 + modules/popup/include/dpl/popup/popup_renderer.h | 129 + modules/popup/src/evas_object.cpp | 240 + modules/popup/src/popup_controller.cpp | 112 + modules/popup/src/popup_manager.cpp | 59 + modules/popup/src/popup_renderer.cpp | 414 + modules/rpc/config.cmake | 52 + .../rpc/include/dpl/rpc/abstract_rpc_connection.h | 98 + .../rpc/include/dpl/rpc/abstract_rpc_connector.h | 54 + .../rpc/include/dpl/rpc/generic_rpc_connection.h | 63 + .../include/dpl/rpc/generic_socket_rpc_client.h | 176 + .../dpl/rpc/generic_socket_rpc_connection.h | 47 + .../include/dpl/rpc/generic_socket_rpc_server.h | 183 + modules/rpc/include/dpl/rpc/rpc_function.h | 213 + .../rpc/include/dpl/rpc/unix_socket_rpc_client.h | 47 + .../include/dpl/rpc/unix_socket_rpc_connection.h | 44 + .../rpc/include/dpl/rpc/unix_socket_rpc_server.h | 47 + modules/rpc/src/abstract_rpc_connection.cpp | 30 + modules/rpc/src/abstract_rpc_connector.cpp | 30 + modules/rpc/src/generic_rpc_connection.cpp | 220 + modules/rpc/src/generic_socket_rpc_client.cpp | 30 + modules/rpc/src/generic_socket_rpc_connection.cpp | 30 + modules/rpc/src/generic_socket_rpc_server.cpp | 30 + modules/rpc/src/unix_socket_rpc_client.cpp | 43 + modules/rpc/src/unix_socket_rpc_connection.cpp | 33 + modules/rpc/src/unix_socket_rpc_server.cpp | 44 + modules/socket/config.cmake | 40 + .../socket/include/dpl/socket/abstract_socket.h | 126 + modules/socket/include/dpl/socket/generic_socket.h | 835 ++ modules/socket/include/dpl/socket/unix_socket.h | 79 + ...itable_input_output_execution_context_support.h | 102 + modules/socket/src/generic_socket.cpp | 30 + modules/socket/src/unix_socket.cpp | 108 + ...able_input_output_execution_context_support.cpp | 288 + modules/test/config.cmake | 38 + .../test/include/dpl/test/test_results_collector.h | 91 + modules/test/include/dpl/test/test_runner.h | 218 + modules/test/src/test_results_collector.cpp | 529 ++ modules/test/src/test_runner.cpp | 433 + modules/utils/config.cmake | 50 + modules/utils/include/file_utils.h | 54 + modules/utils/include/folder_size.h | 39 + modules/utils/include/mime_type_utils.h | 57 + modules/utils/include/warp_iri.h | 71 + modules/utils/include/widget_version.h | 118 + modules/utils/include/wrt_global_settings.h | 31 + .../utils/include/wrt_global_settings_internal.h | 35 + modules/utils/include/wrt_utility.h | 170 + modules/utils/src/file_utils.cpp | 158 + modules/utils/src/folder_size.cpp | 166 + modules/utils/src/mime_type_utils.cpp | 150 + modules/utils/src/warp_iri.cpp | 212 + modules/utils/src/widget_version.cpp | 371 + modules/utils/src/wrt_global_settings.cpp | 54 + modules/utils/src/wrt_global_settings_internal.cpp | 44 + modules/utils/src/wrt_utility.cpp | 362 + modules/vcore/CMakeLists.txt | 30 + modules/vcore/src/CMakeLists.txt | 132 + modules/vcore/src/orm/DESCRIPTION | 1 + modules/vcore/src/orm/gen_db_md5.sh | 5 + modules/vcore/src/orm/orm_generator_vcore.h | 24 + modules/vcore/src/orm/vcore_db | 20 + modules/vcore/src/orm/vcore_db_definitions | 6 + modules/vcore/src/orm/vcore_db_sql_generator.h | 21 + modules/vcore/src/orm/version_db | 5 + modules/vcore/src/vcore/Base64.cpp | 209 + modules/vcore/src/vcore/Base64.h | 83 + modules/vcore/src/vcore/CRL.cpp | 500 ++ modules/vcore/src/vcore/CRL.h | 195 + modules/vcore/src/vcore/CachedCRL.cpp | 165 + modules/vcore/src/vcore/CachedCRL.h | 69 + modules/vcore/src/vcore/CachedOCSP.cpp | 192 + modules/vcore/src/vcore/CachedOCSP.h | 65 + modules/vcore/src/vcore/CertStoreType.h | 67 + modules/vcore/src/vcore/Certificate.cpp | 493 ++ modules/vcore/src/vcore/Certificate.h | 152 + modules/vcore/src/vcore/CertificateCacheDAO.cpp | 275 + modules/vcore/src/vcore/CertificateCacheDAO.h | 107 + modules/vcore/src/vcore/CertificateCollection.cpp | 266 + modules/vcore/src/vcore/CertificateCollection.h | 179 + .../vcore/src/vcore/CertificateConfigReader.cpp | 138 + modules/vcore/src/vcore/CertificateConfigReader.h | 71 + modules/vcore/src/vcore/CertificateIdentifier.h | 73 + modules/vcore/src/vcore/CertificateLoader.cpp | 725 ++ modules/vcore/src/vcore/CertificateLoader.h | 110 + modules/vcore/src/vcore/CertificateStorage.h | 26 + modules/vcore/src/vcore/CertificateVerifier.cpp | 132 + modules/vcore/src/vcore/CertificateVerifier.h | 85 + modules/vcore/src/vcore/Config.cpp | 20 + modules/vcore/src/vcore/Config.h | 92 + modules/vcore/src/vcore/Database.cpp | 43 + modules/vcore/src/vcore/Database.h | 66 + modules/vcore/src/vcore/DeveloperModeValidator.cpp | 97 + modules/vcore/src/vcore/DeveloperModeValidator.h | 58 + modules/vcore/src/vcore/IAbstractResponseCache.h | 47 + modules/vcore/src/vcore/OCSP.cpp | 538 ++ modules/vcore/src/vcore/OCSP.h | 242 + modules/vcore/src/vcore/OCSPCertMgrUtil.cpp | 180 + modules/vcore/src/vcore/OCSPCertMgrUtil.h | 43 + modules/vcore/src/vcore/OCSPUtil.c | 92 + modules/vcore/src/vcore/ParserSchema.h | 199 + modules/vcore/src/vcore/ReferenceValidator.cpp | 119 + modules/vcore/src/vcore/ReferenceValidator.h | 60 + modules/vcore/src/vcore/RevocationCheckerBase.cpp | 68 + modules/vcore/src/vcore/RevocationCheckerBase.h | 44 + modules/vcore/src/vcore/SSLContainers.h | 183 + modules/vcore/src/vcore/SaxReader.cpp | 308 + modules/vcore/src/vcore/SaxReader.h | 151 + modules/vcore/src/vcore/SignatureData.h | 186 + modules/vcore/src/vcore/SignatureFinder.cpp | 87 + modules/vcore/src/vcore/SignatureFinder.h | 85 + modules/vcore/src/vcore/SignatureReader.cpp | 582 ++ modules/vcore/src/vcore/SignatureReader.h | 122 + modules/vcore/src/vcore/SignatureValidator.cpp | 265 + modules/vcore/src/vcore/SignatureValidator.h | 73 + modules/vcore/src/vcore/SoupMessageSendAsync.cpp | 15 + modules/vcore/src/vcore/SoupMessageSendAsync.h | 172 + modules/vcore/src/vcore/SoupMessageSendBase.cpp | 108 + modules/vcore/src/vcore/SoupMessageSendBase.h | 117 + modules/vcore/src/vcore/SoupMessageSendSync.cpp | 94 + modules/vcore/src/vcore/SoupMessageSendSync.h | 42 + modules/vcore/src/vcore/VCore.cpp | 71 + modules/vcore/src/vcore/VCore.h | 58 + modules/vcore/src/vcore/VCorePrivate.h | 35 + modules/vcore/src/vcore/ValidatorCommon.h | 102 + modules/vcore/src/vcore/ValidatorFactories.cpp | 49 + modules/vcore/src/vcore/ValidatorFactories.h | 36 + modules/vcore/src/vcore/VerificationStatus.cpp | 51 + modules/vcore/src/vcore/VerificationStatus.h | 111 + modules/vcore/src/vcore/WacOrigin.cpp | 151 + modules/vcore/src/vcore/WacOrigin.h | 56 + modules/vcore/src/vcore/XmlsecAdapter.cpp | 397 + modules/vcore/src/vcore/XmlsecAdapter.h | 135 + modules/vcore/src/vcore/scoped_gpointer.h | 76 + modules/widget_dao/CMakeLists.txt | 142 + modules/widget_dao/dao/WrtDatabase.cpp | 62 + modules/widget_dao/dao/bind_to_dao.h | 73 + modules/widget_dao/dao/common_dao_types.cpp | 164 + modules/widget_dao/dao/config_parser_data.cpp | 424 + modules/widget_dao/dao/feature_dao.cpp | 163 + modules/widget_dao/dao/feature_dao_read_only.cpp | 322 + modules/widget_dao/dao/global_config.cpp | 39 + modules/widget_dao/dao/global_dao.cpp | 471 + modules/widget_dao/dao/global_dao_read_only.cpp | 396 + modules/widget_dao/dao/path_builder.cpp | 129 + modules/widget_dao/dao/plugin_dao.cpp | 220 + modules/widget_dao/dao/plugin_dao_read_only.cpp | 428 + modules/widget_dao/dao/property_dao.cpp | 154 + modules/widget_dao/dao/property_dao_read_only.cpp | 172 + modules/widget_dao/dao/webruntime_database.cpp | 25 + modules/widget_dao/dao/widget_dao.cpp | 600 ++ modules/widget_dao/dao/widget_dao_read_only.cpp | 1094 +++ .../include/dpl/wrt-dao-ro/WrtDatabase.h | 40 + .../include/dpl/wrt-dao-ro/common_dao_types.h | 487 ++ .../include/dpl/wrt-dao-ro/config_parser_data.h | 252 + .../include/dpl/wrt-dao-ro/feature_dao_read_only.h | 81 + .../include/dpl/wrt-dao-ro/feature_model.h | 64 + .../include/dpl/wrt-dao-ro/global_config.h | 249 + .../include/dpl/wrt-dao-ro/global_dao_read_only.h | 157 + .../include/dpl/wrt-dao-ro/path_builder.h | 57 + .../include/dpl/wrt-dao-ro/plugin_dao_read_only.h | 121 + .../dpl/wrt-dao-ro/property_dao_read_only.h | 81 + .../include/dpl/wrt-dao-ro/webruntime_database.h | 50 + .../include/dpl/wrt-dao-ro/widget_config.h | 76 + .../include/dpl/wrt-dao-ro/widget_dao_read_only.h | 709 ++ .../include/dpl/wrt-dao-rw/feature_dao.h | 40 + .../widget_dao/include/dpl/wrt-dao-rw/global_dao.h | 113 + .../widget_dao/include/dpl/wrt-dao-rw/plugin_dao.h | 61 + .../include/dpl/wrt-dao-rw/property_dao.h | 53 + .../widget_dao/include/dpl/wrt-dao-rw/widget_dao.h | 146 + modules/widget_dao/orm/gen_db_md5.sh | 19 + modules/widget_dao/orm/iana_db | 8957 ++++++++++++++++++++ modules/widget_dao/orm/orm_generator_wrt.h | 24 + modules/widget_dao/orm/version_db | 5 + modules/widget_dao/orm/wrt_db | 359 + modules/widget_dao/orm/wrt_db_definitions | 7 + modules/widget_dao/orm/wrt_db_sql_generator.h | 27 + packaging/dpl.spec | 79 + tests/CMakeLists.txt | 38 + tests/ace/AttributeSetter.cpp | 19 + tests/ace/AttributeSetter.h | 125 + tests/ace/CMakeLists.txt | 75 + tests/ace/Interfaces.cpp | 75 + tests/ace/Interfaces.h | 70 + tests/ace/PEPSingleton.cpp | 26 + tests/ace/PEPSingleton.h | 31 + tests/ace/TestSuite01.cpp | 986 +++ tests/ace/TestSuite02.cpp | 913 ++ tests/ace/TestSuite03.cpp | 533 ++ tests/ace/TestSuite04.cpp | 627 ++ tests/ace/TestSuite05.cpp | 620 ++ tests/ace/TestSuite06.cpp | 1610 ++++ tests/ace/TestSuite07.cpp | 287 + tests/ace/ace_tests.cpp | 75 + tests/ace/loop_control.cpp | 75 + tests/ace/loop_control.h | 42 + tests/ace/test-configuration/CMTest/CMakeLists.txt | 26 + .../CMTest/active/CMakeLists.txt | 21 + .../test-configuration/CMTest/active/bondixml.dtd | 199 + .../CMTest/active/pms_general-test.xml | 2510 ++++++ tests/ace/test-configuration/CMTest/pms_config.xml | 10 + .../test-configuration/CMTest/pms_general-test.xml | 2510 ++++++ .../ace/test-configuration/CMTest/policyTest1.xml | 2510 ++++++ .../ace/test-configuration/CMTest/policyTest2.xml | 2510 ++++++ .../ace/test-configuration/CMTest/policyTest3.xml | 2510 ++++++ tests/ace/test-configuration/CMakeLists.txt | 40 + .../ace/test-configuration/attr_policy-example.xml | 50 + .../test-configuration/attr_policy-example1.xml | 97 + .../test-configuration/attr_policy-example2.xml | 55 + .../test-configuration/attr_policy-example3.xml | 128 + .../test-configuration/attr_policy-example4.xml | 102 + .../test-configuration/attr_policy-example5.xml | 43 + .../test-configuration/attr_policy-example6.xml | 41 + .../test-configuration/attr_policy-example7.xml | 54 + .../test-configuration/attr_policy-example8.xml | 55 + tests/ace/test-configuration/attre_config.xml | 17 + tests/ace/test-configuration/general-test.xml | 2621 ++++++ tests/ace/test-configuration/interceptpolicy.xml | 495 ++ .../ace/test-configuration/old_policy-example.xml | 50 + tests/ace/test-configuration/policy-example.xml | 95 + tests/ace/test-configuration/policy-example2.xml | 50 + tests/ace/test-configuration/policy-example3.xml | 50 + .../test-configuration/policy-test-gsettings.xml | 41 + tests/ace/test-configuration/policy-test.xml | 41 + tests/ace/test-configuration/policy-wac-2.0.xml | 103 + tests/ace/test-configuration/policy_example.xml | 2407 ++++++ .../test-configuration/reproduce-abort-test.xml | 26 + tests/ace/test-configuration/undefined-test.xml | 1075 +++ tests/core/CMakeLists.txt | 90 + tests/core/DESCRIPTION | 2 + tests/core/data/sample.zip | Bin 0 -> 174 bytes tests/core/main.cpp | 28 + tests/core/test_address.cpp | 50 + tests/core/test_binary_queue.cpp | 357 + tests/core/test_fast_delegate.cpp | 248 + tests/core/test_foreach.cpp | 118 + tests/core/test_log_unhandled_exception.cpp | 74 + tests/core/test_once.cpp | 103 + tests/core/test_scoped_array.cpp | 62 + tests/core/test_scoped_close.cpp | 27 + tests/core/test_scoped_fclose.cpp | 70 + tests/core/test_scoped_free.cpp | 53 + tests/core/test_scoped_ptr.cpp | 59 + tests/core/test_semaphore.cpp | 85 + tests/core/test_serialization.cpp | 259 + tests/core/test_shared_ptr.cpp | 85 + tests/core/test_string.cpp | 384 + tests/core/test_task.cpp | 93 + tests/core/test_thread.cpp | 100 + tests/core/test_type_list.cpp | 41 + tests/core/test_zip_input.cpp | 98 + tests/db/CMakeLists.txt | 76 + tests/db/main.cpp | 28 + tests/db/orm/CMakeLists.txt | 9 + tests/db/orm/dpl_orm_test.db | Bin 0 -> 16384 bytes tests/db/orm/dpl_orm_test_db | 38 + tests/db/orm/dpl_orm_test_db.sql | 38 + tests/db/orm/dpl_orm_test_db_definitions | 5 + tests/db/orm/dpl_orm_test_db_sql_generator.h | 27 + tests/db/orm/generator_dpl_orm_test.h | 24 + tests/db/test_orm.cpp | 712 ++ tests/db/test_sql_connection.cpp | 154 + tests/dbus/CMakeLists.txt | 98 + tests/dbus/data/org.tizen.DBusTestService.service | 3 + tests/dbus/dbus_test.cpp | 117 + tests/dbus/dbus_test.h | 90 + tests/dbus/loop_control.cpp | 75 + tests/dbus/loop_control.h | 42 + tests/dbus/main.cpp | 35 + tests/dbus/test_cases.cpp | 188 + tests/dbus/test_service.cpp | 93 + tests/event/CMakeLists.txt | 70 + tests/event/main.cpp | 28 + tests/event/test_controller.cpp | 331 + tests/event/test_event_support.cpp | 122 + tests/event/test_ic_delegate.cpp | 568 ++ tests/event/test_property.cpp | 111 + tests/localization/CMakeLists.txt | 76 + tests/localization/files/CMakeLists.txt | 19 + tests/localization/files/one | 0 tests/localization/files/two | 0 .../dpl/wrt-dao-ro/common_dao_types.h | 487 ++ .../mockup_include/dpl/wrt-dao-rw/widget_dao.h | 230 + tests/localization/mockup_src/widget_dao.cpp | 107 + tests/localization/test_localization.cpp | 28 + tests/localization/test_suite01.cpp | 103 + tests/vcore/CMakeLists.txt | 129 + tests/vcore/TestCRL.cpp | 94 + tests/vcore/TestCRL.h | 33 + tests/vcore/TestCases.cpp | 1325 +++ tests/vcore/TestEnv.cpp | 96 + tests/vcore/TestEnv.h | 23 + tests/vcore/certificate-generator/create_certs.sh | 94 + .../certificate-generator/demoCA.init/cacert.pem | 60 + .../certificate-generator/demoCA.init/careq.pem | 11 + .../certificate-generator/demoCA.init/index.txt | 1 + .../demoCA.init/index.txt.attr | 1 + .../demoCA.init/index.txt.old | 0 .../demoCA.init/newcerts/00.pem | 60 + .../demoCA.init/private/cakey.pem | 18 + .../vcore/certificate-generator/demoCA.init/serial | 1 + .../certificate-generator/demoCA.init/serial.old | 1 + .../dpl-tests-vcore-ocsp-server.sh | 19 + tests/vcore/certificate-generator/openssl.cnf | 327 + tests/vcore/test-cases/keys/CAbundle.crt | 3677 ++++++++ tests/vcore/test-cases/keys/README | 2 + tests/vcore/test-cases/keys/filip_rsa_cert.pem | 62 + tests/vcore/test-cases/keys/filip_rsa_key.pem | 18 + tests/vcore/test-cases/keys/magda_dsa_cert.pem | 90 + tests/vcore/test-cases/keys/magda_dsa_key.pem | 12 + .../test-cases/keys/ocsp_level0deprecated.crt | 31 + tests/vcore/test-cases/keys/ocsp_level1.crt | 29 + tests/vcore/test-cases/keys/ocsp_level2.crt | 29 + tests/vcore/test-cases/keys/ocsp_rootca.crt | 18 + tests/vcore/test-cases/keys/operator.root.cert.pem | 66 + .../vcore/test-cases/keys/operator.second.cert.pem | 64 + .../vcore/test-cases/keys/operator.second.key.pem | 18 + tests/vcore/test-cases/keys/operator.second.p12 | Bin 0 -> 2043 bytes tests/vcore/test-cases/keys/root_cacert.pem | 64 + tests/vcore/test-cases/keys/root_cakey.pem | 18 + tests/vcore/test-cases/widget/author-signature.xml | 66 + tests/vcore/test-cases/widget/config.xml | 6 + tests/vcore/test-cases/widget/index.html | 4 + tests/vcore/test-cases/widget/signature1.xml | 62 + tests/vcore/test-cases/widget/signature22.xml | 66 + tests/vcore/vcore_tests.cpp | 37 + 746 files changed, 131312 insertions(+) create mode 100644 3rdparty/CMakeLists.txt create mode 100644 3rdparty/DESCRIPTION create mode 100644 3rdparty/fastdelegate/CPOL.html create mode 100644 3rdparty/fastdelegate/DESCRIPTION create mode 100644 3rdparty/fastdelegate/Demo.cpp create mode 100644 3rdparty/fastdelegate/LICENSE create mode 100644 3rdparty/minizip/DESCRIPTION create mode 100644 3rdparty/minizip/Makefile create mode 100644 3rdparty/minizip/MiniZip64_Changes.txt create mode 100644 3rdparty/minizip/MiniZip64_info.txt create mode 100644 3rdparty/minizip/crypt.h create mode 100644 3rdparty/minizip/framework_minizip.h create mode 100644 3rdparty/minizip/ioapi.c create mode 100644 3rdparty/minizip/ioapi.h create mode 100644 3rdparty/minizip/iowin32.c create mode 100644 3rdparty/minizip/iowin32.h create mode 100644 3rdparty/minizip/make_vms.com create mode 100644 3rdparty/minizip/miniunz.c create mode 100644 3rdparty/minizip/minizip.c create mode 100644 3rdparty/minizip/mztools.c create mode 100644 3rdparty/minizip/mztools.h create mode 100644 3rdparty/minizip/unzip.c create mode 100644 3rdparty/minizip/unzip.h create mode 100644 3rdparty/minizip/unzip11.zip create mode 100644 3rdparty/minizip/zip.c create mode 100644 3rdparty/minizip/zip.h create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 NOTICE create mode 100755 bin/run_all.sh create mode 100644 build/CMakeLists.txt create mode 100644 build/ace/CMakeLists.txt create mode 100644 build/ace/dpl-ace-dao-ro.pc.in create mode 100644 build/ace/dpl-ace-dao-rw.pc.in create mode 100644 build/ace/dpl-ace.pc.in create mode 100644 build/core/CMakeLists.txt create mode 100644 build/core/DESCRIPTION create mode 100644 build/core/dpl-efl.pc create mode 100644 build/db/CMakeLists.txt create mode 100644 build/db/dpl-db-efl.pc create mode 100644 build/dbus/CMakeLists.txt create mode 100644 build/dbus/dpl-dbus-efl.pc create mode 100644 build/event/CMakeLists.txt create mode 100644 build/event/dpl-event-efl.pc create mode 100644 build/log/CMakeLists.txt create mode 100644 build/log/dpl-log-efl.pc create mode 100644 build/popup/CMakeLists.txt create mode 100644 build/popup/dpl-popup-efl.pc create mode 100644 build/rpc/CMakeLists.txt create mode 100644 build/rpc/dpl-rpc-efl.pc create mode 100644 build/socket/CMakeLists.txt create mode 100644 build/socket/dpl-socket-efl.pc create mode 100644 build/test/CMakeLists.txt create mode 100644 build/test/dpl-test-efl.pc create mode 100644 build/utils/CMakeLists.txt create mode 100644 build/utils/dpl-utils-efl.pc create mode 100644 build/vcore/CMakeLists.txt create mode 100644 build/vcore/dpl-vcore.pc.in create mode 100644 build/widget_dao/CMakeLists.txt create mode 100644 build/widget_dao/dpl-wrt-dao-ro.pc.in create mode 100644 build/widget_dao/dpl-wrt-dao-rw.pc.in create mode 100644 debian/DESCRIPTION create mode 100644 debian/README.Debian create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/dirs create mode 100755 debian/rules create mode 100644 debian/wrt-commons-dev.dirs create mode 100644 debian/wrt-commons-dev.install create mode 100644 debian/wrt-commons-test.install create mode 100644 debian/wrt-commons.dirs create mode 100644 debian/wrt-commons.install create mode 100755 debian/wrt-commons.postinst create mode 100755 dir-struct.py create mode 100644 doc/DESCRIPTION create mode 100644 doc/doxyfile create mode 100644 doc/dpl_programming_guide.docx create mode 100644 doc/dpl_programming_guide.pdf create mode 100644 etc/CMakeLists.txt create mode 100644 etc/DESCRIPTION create mode 100644 etc/certificates/CMakeLists.txt create mode 100644 etc/certificates/tizen.root.preproduction.cert.pem create mode 100644 etc/certificates/wac.publisherid.pem create mode 100644 etc/certificates/wac.root.preproduction.pem create mode 100644 etc/certificates/wac.root.production.pem create mode 100644 etc/fingerprint_list.xml create mode 100644 etc/fingerprint_list.xsd create mode 100644 etc/schema.xsd create mode 100755 etc/wrt_create_clean_db.sh create mode 100755 etc/wrt_reset_db.sh create mode 100644 examples/CMakeLists.txt create mode 100644 examples/DESCRIPTION create mode 100644 examples/binary_queue/CMakeLists.txt create mode 100644 examples/binary_queue/binary_queue.cpp create mode 100644 examples/copy/CMakeLists.txt create mode 100644 examples/copy/copy.cpp create mode 100644 examples/crypto_hash/CMakeLists.txt create mode 100644 examples/crypto_hash/crypto_hash.cpp create mode 100644 examples/dbus/client-example/CMakeLists.txt create mode 100644 examples/dbus/client-example/client-example.cpp create mode 100644 examples/dbus/server-example/CMakeLists.txt create mode 100644 examples/dbus/server-example/server-example.cpp create mode 100644 examples/event_delivery_test/CMakeLists.txt create mode 100644 examples/event_delivery_test/event_delivery_test.cpp create mode 100644 examples/fake_rpc/CMakeLists.txt create mode 100644 examples/fake_rpc/fake_rpc.cpp create mode 100644 examples/metronome/CMakeLists.txt create mode 100644 examples/metronome/metronome_client.cpp create mode 100644 examples/metronome/metronome_server.cpp create mode 100644 examples/rpc/CMakeLists.txt create mode 100644 examples/rpc/rpc.cpp create mode 100644 examples/simple/CMakeLists.txt create mode 100644 examples/simple/simple.cpp create mode 100644 examples/single_instance/CMakeLists.txt create mode 100644 examples/single_instance/single_instance.cpp create mode 100644 examples/socket/CMakeLists.txt create mode 100644 examples/socket/socket.cpp create mode 100644 examples/tcpsock/CMakeLists.txt create mode 100644 examples/tcpsock/tcpsock.cpp create mode 100644 examples/timed_event/CMakeLists.txt create mode 100644 examples/timed_event/timed_event.cpp create mode 100644 modules/CMakeLists.txt create mode 100644 modules/ace/CMakeLists.txt create mode 100644 modules/ace/DESCRIPTION create mode 100644 modules/ace/configuration/UnrestrictedPolicy.xml create mode 100644 modules/ace/configuration/WACPolicy.xml create mode 100644 modules/ace/configuration/bondixml.xsd create mode 100644 modules/ace/configuration/config.dtd create mode 100644 modules/ace/configuration/config.xml create mode 100644 modules/ace/configuration/demo.xml create mode 100644 modules/ace/dao/AceDAO.cpp create mode 100644 modules/ace/dao/AceDAOConversions.cpp create mode 100644 modules/ace/dao/AceDAOReadOnly.cpp create mode 100644 modules/ace/dao/AceDAOUtilities.cpp create mode 100644 modules/ace/dao/AceDatabase.cpp create mode 100644 modules/ace/dao/BaseAttribute.cpp create mode 100644 modules/ace/dao/CMakeLists.txt create mode 100644 modules/ace/dao/PromptModel.cpp create mode 100644 modules/ace/dao/common_dao_types.cpp create mode 100644 modules/ace/engine/Attribute.cpp create mode 100644 modules/ace/engine/CombinerImpl.cpp create mode 100644 modules/ace/engine/Condition.cpp create mode 100644 modules/ace/engine/ConfigurationManager.cpp create mode 100644 modules/ace/engine/NodeFactory.cpp create mode 100644 modules/ace/engine/Policy.cpp create mode 100644 modules/ace/engine/PolicyEnforcementPoint.cpp create mode 100644 modules/ace/engine/PolicyEvaluator.cpp create mode 100644 modules/ace/engine/PolicyInformationPoint.cpp create mode 100644 modules/ace/engine/Rule.cpp create mode 100644 modules/ace/engine/Serializer.cpp create mode 100644 modules/ace/engine/SettingsLogic.cpp create mode 100644 modules/ace/engine/Subject.cpp create mode 100644 modules/ace/engine/TreeNode.cpp create mode 100644 modules/ace/engine/parser.cpp create mode 100644 modules/ace/include/dpl/ace-dao-ro/AceDAOConversions.h create mode 100644 modules/ace/include/dpl/ace-dao-ro/AceDAOReadOnly.h create mode 100644 modules/ace/include/dpl/ace-dao-ro/AceDAOUtilities.h create mode 100644 modules/ace/include/dpl/ace-dao-ro/AceDatabase.h create mode 100644 modules/ace/include/dpl/ace-dao-ro/BaseAttribute.h create mode 100644 modules/ace/include/dpl/ace-dao-ro/BasePermission.h create mode 100644 modules/ace/include/dpl/ace-dao-ro/IRequest.h create mode 100644 modules/ace/include/dpl/ace-dao-ro/PreferenceTypes.h create mode 100644 modules/ace/include/dpl/ace-dao-ro/PromptModel.h create mode 100644 modules/ace/include/dpl/ace-dao-ro/TimedVerdict.h create mode 100644 modules/ace/include/dpl/ace-dao-ro/ValidityTypes.h create mode 100644 modules/ace/include/dpl/ace-dao-ro/VerdictTypes.h create mode 100644 modules/ace/include/dpl/ace-dao-ro/common_dao_types.h create mode 100644 modules/ace/include/dpl/ace-dao-ro/wrt_db_types.h create mode 100644 modules/ace/include/dpl/ace-dao-rw/AceDAO.h create mode 100644 modules/ace/include/dpl/ace/AbstractPolicyEnforcementPoint.h create mode 100644 modules/ace/include/dpl/ace/AbstractPolicyInformationPoint.h create mode 100644 modules/ace/include/dpl/ace/AbstractTreeElement.h create mode 100644 modules/ace/include/dpl/ace/AsyncVerdictResultListener.h create mode 100644 modules/ace/include/dpl/ace/Attribute.h create mode 100644 modules/ace/include/dpl/ace/Combiner.h create mode 100644 modules/ace/include/dpl/ace/CombinerImpl.h create mode 100644 modules/ace/include/dpl/ace/Condition.h create mode 100644 modules/ace/include/dpl/ace/ConfigurationManager.h create mode 100644 modules/ace/include/dpl/ace/Constants.h create mode 100644 modules/ace/include/dpl/ace/Effect.h create mode 100644 modules/ace/include/dpl/ace/NodeFactory.h create mode 100644 modules/ace/include/dpl/ace/PermissionTriple.h create mode 100644 modules/ace/include/dpl/ace/Policy.h create mode 100644 modules/ace/include/dpl/ace/PolicyEffect.h create mode 100644 modules/ace/include/dpl/ace/PolicyEnforcementPoint.h create mode 100644 modules/ace/include/dpl/ace/PolicyEvaluator.h create mode 100644 modules/ace/include/dpl/ace/PolicyEvaluatorFactory.h create mode 100644 modules/ace/include/dpl/ace/PolicyInformationPoint.h create mode 100644 modules/ace/include/dpl/ace/PolicyResult.h create mode 100644 modules/ace/include/dpl/ace/PolicySet.h create mode 100644 modules/ace/include/dpl/ace/Preference.h create mode 100644 modules/ace/include/dpl/ace/PromptDecision.h create mode 100644 modules/ace/include/dpl/ace/Request.h create mode 100644 modules/ace/include/dpl/ace/Rule.h create mode 100644 modules/ace/include/dpl/ace/Serializer.h create mode 100644 modules/ace/include/dpl/ace/SettingsLogic.h create mode 100644 modules/ace/include/dpl/ace/Subject.h create mode 100644 modules/ace/include/dpl/ace/TestTimer.h create mode 100644 modules/ace/include/dpl/ace/TreeNode.h create mode 100644 modules/ace/include/dpl/ace/UserDecision.h create mode 100644 modules/ace/include/dpl/ace/Verdict.h create mode 100644 modules/ace/include/dpl/ace/WRT_INTERFACE.h create mode 100644 modules/ace/include/dpl/ace/WidgetUsageModel.h create mode 100644 modules/ace/include/dpl/ace/acf_consts.h create mode 100644 modules/ace/include/dpl/ace/parser.h create mode 100644 modules/ace/orm/ace_db create mode 100644 modules/ace/orm/ace_db_definitions create mode 100644 modules/ace/orm/ace_db_sql_generator.h create mode 100755 modules/ace/orm/gen_db_md5.sh create mode 100644 modules/ace/orm/orm_generator_ace.h create mode 100644 modules/ace/orm/version_db create mode 100644 modules/core/DESCRIPTION create mode 100644 modules/core/config.cmake create mode 100644 modules/core/include/DESCRIPTION create mode 100644 modules/core/include/dpl/3rdparty/fastdelegate/FastDelegate.h create mode 100644 modules/core/include/dpl/3rdparty/fastdelegate/FastDelegateBind.h create mode 100644 modules/core/include/dpl/abstract_input.h create mode 100644 modules/core/include/dpl/abstract_input_output.h create mode 100644 modules/core/include/dpl/abstract_output.h create mode 100644 modules/core/include/dpl/abstract_waitable_input.h create mode 100644 modules/core/include/dpl/abstract_waitable_input_adapter.h create mode 100644 modules/core/include/dpl/abstract_waitable_input_output.h create mode 100644 modules/core/include/dpl/abstract_waitable_input_output_adapter.h create mode 100644 modules/core/include/dpl/abstract_waitable_output.h create mode 100644 modules/core/include/dpl/abstract_waitable_output_adapter.h create mode 100644 modules/core/include/dpl/address.h create mode 100644 modules/core/include/dpl/aligned.h create mode 100644 modules/core/include/dpl/application.h create mode 100644 modules/core/include/dpl/apply.h create mode 100644 modules/core/include/dpl/assert.h create mode 100644 modules/core/include/dpl/atomic.h create mode 100644 modules/core/include/dpl/binary_queue.h create mode 100644 modules/core/include/dpl/bool_operator.h create mode 100644 modules/core/include/dpl/char_traits.h create mode 100644 modules/core/include/dpl/colors.h create mode 100644 modules/core/include/dpl/copy.h create mode 100644 modules/core/include/dpl/enable_shared_from_this.h create mode 100644 modules/core/include/dpl/errno_string.h create mode 100644 modules/core/include/dpl/exception.h create mode 100644 modules/core/include/dpl/fast_delegate.h create mode 100644 modules/core/include/dpl/file_input.h create mode 100644 modules/core/include/dpl/file_input_mapping.h create mode 100644 modules/core/include/dpl/file_output.h create mode 100644 modules/core/include/dpl/foreach.h create mode 100644 modules/core/include/dpl/framework_appcore.h create mode 100644 modules/core/include/dpl/framework_efl.h create mode 100644 modules/core/include/dpl/framework_vconf.h create mode 100644 modules/core/include/dpl/generic_event.h create mode 100644 modules/core/include/dpl/lexical_cast.h create mode 100644 modules/core/include/dpl/main.h create mode 100644 modules/core/include/dpl/mutex.h create mode 100644 modules/core/include/dpl/named_base_pipe.h create mode 100644 modules/core/include/dpl/named_input_pipe.h create mode 100644 modules/core/include/dpl/named_output_pipe.h create mode 100644 modules/core/include/dpl/noncopyable.h create mode 100644 modules/core/include/dpl/noreturn.h create mode 100644 modules/core/include/dpl/once.h create mode 100644 modules/core/include/dpl/optional.h create mode 100644 modules/core/include/dpl/optional_typedefs.h create mode 100644 modules/core/include/dpl/preprocessor.h create mode 100644 modules/core/include/dpl/read_write_mutex.h create mode 100644 modules/core/include/dpl/recursive_mutex.h create mode 100644 modules/core/include/dpl/scoped_array.h create mode 100644 modules/core/include/dpl/scoped_close.h create mode 100644 modules/core/include/dpl/scoped_fclose.h create mode 100644 modules/core/include/dpl/scoped_free.h create mode 100644 modules/core/include/dpl/scoped_gpointer.h create mode 100644 modules/core/include/dpl/scoped_ptr.h create mode 100644 modules/core/include/dpl/scoped_resource.h create mode 100644 modules/core/include/dpl/semaphore.h create mode 100644 modules/core/include/dpl/serialization.h create mode 100644 modules/core/include/dpl/shared_ptr.h create mode 100644 modules/core/include/dpl/single_instance.h create mode 100644 modules/core/include/dpl/singleton.h create mode 100644 modules/core/include/dpl/singleton_impl.h create mode 100644 modules/core/include/dpl/singleton_safe_impl.h create mode 100644 modules/core/include/dpl/sstream.h create mode 100644 modules/core/include/dpl/string.h create mode 100644 modules/core/include/dpl/task.h create mode 100644 modules/core/include/dpl/task_list.h create mode 100644 modules/core/include/dpl/thread.h create mode 100644 modules/core/include/dpl/type_list.h create mode 100644 modules/core/include/dpl/union_cast.h create mode 100644 modules/core/include/dpl/unused.h create mode 100644 modules/core/include/dpl/waitable_event.h create mode 100644 modules/core/include/dpl/waitable_handle.h create mode 100644 modules/core/include/dpl/waitable_handle_watch_support.h create mode 100644 modules/core/include/dpl/workaround.h create mode 100644 modules/core/include/dpl/zip_input.h create mode 100644 modules/core/src/DESCRIPTION create mode 100644 modules/core/src/abstract_waitable_input_adapter.cpp create mode 100644 modules/core/src/abstract_waitable_input_output_adapter.cpp create mode 100644 modules/core/src/abstract_waitable_output_adapter.cpp create mode 100644 modules/core/src/address.cpp create mode 100644 modules/core/src/application.cpp create mode 100644 modules/core/src/apply.cpp create mode 100644 modules/core/src/assert.cpp create mode 100644 modules/core/src/atomic.cpp create mode 100644 modules/core/src/binary_queue.cpp create mode 100644 modules/core/src/char_traits.cpp create mode 100644 modules/core/src/colors.cpp create mode 100644 modules/core/src/copy.cpp create mode 100644 modules/core/src/errno_string.cpp create mode 100644 modules/core/src/exception.cpp create mode 100644 modules/core/src/fast_delegate.cpp create mode 100644 modules/core/src/file_input.cpp create mode 100644 modules/core/src/file_input_mapping.cpp create mode 100644 modules/core/src/file_output.cpp create mode 100644 modules/core/src/generic_event.cpp create mode 100644 modules/core/src/lexical_cast.cpp create mode 100644 modules/core/src/main.cpp create mode 100644 modules/core/src/mutex.cpp create mode 100644 modules/core/src/named_base_pipe.cpp create mode 100644 modules/core/src/named_input_pipe.cpp create mode 100644 modules/core/src/named_output_pipe.cpp create mode 100644 modules/core/src/noncopyable.cpp create mode 100644 modules/core/src/once.cpp create mode 100644 modules/core/src/read_write_mutex.cpp create mode 100644 modules/core/src/recursive_mutex.cpp create mode 100644 modules/core/src/semaphore.cpp create mode 100644 modules/core/src/serialization.cpp create mode 100644 modules/core/src/single_instance.cpp create mode 100644 modules/core/src/singleton.cpp create mode 100644 modules/core/src/string.cpp create mode 100644 modules/core/src/task.cpp create mode 100644 modules/core/src/task_list.cpp create mode 100644 modules/core/src/thread.cpp create mode 100644 modules/core/src/type_list.cpp create mode 100644 modules/core/src/union_cast.cpp create mode 100644 modules/core/src/waitable_event.cpp create mode 100644 modules/core/src/waitable_handle.cpp create mode 100644 modules/core/src/waitable_handle_watch_support.cpp create mode 100644 modules/core/src/zip_input.cpp create mode 100644 modules/db/config.cmake create mode 100644 modules/db/include/dpl/db/naive_synchronization_object.h create mode 100644 modules/db/include/dpl/db/orm.h create mode 100644 modules/db/include/dpl/db/orm_generator.h create mode 100644 modules/db/include/dpl/db/orm_interface.h create mode 100644 modules/db/include/dpl/db/orm_macros.h create mode 100644 modules/db/include/dpl/db/sql_connection.h create mode 100644 modules/db/include/dpl/db/thread_database_support.h create mode 100644 modules/db/src/naive_synchronization_object.cpp create mode 100644 modules/db/src/orm.cpp create mode 100644 modules/db/src/sql_connection.cpp create mode 100644 modules/db/src/thread_database_support.cpp create mode 100644 modules/dbus/config.cmake create mode 100644 modules/dbus/include/dpl/dbus/connection.h create mode 100644 modules/dbus/include/dpl/dbus/dbus_client.h create mode 100644 modules/dbus/include/dpl/dbus/dbus_deserialization.h create mode 100644 modules/dbus/include/dpl/dbus/dbus_interface_dispatcher.h create mode 100644 modules/dbus/include/dpl/dbus/dbus_serialization.h create mode 100644 modules/dbus/include/dpl/dbus/dbus_server_deserialization.h create mode 100644 modules/dbus/include/dpl/dbus/dbus_server_serialization.h create mode 100644 modules/dbus/include/dpl/dbus/dbus_signature.h create mode 100644 modules/dbus/include/dpl/dbus/dispatcher.h create mode 100644 modules/dbus/include/dpl/dbus/exception.h create mode 100644 modules/dbus/include/dpl/dbus/interface.h create mode 100644 modules/dbus/include/dpl/dbus/method_proxy.h create mode 100644 modules/dbus/include/dpl/dbus/object.h create mode 100644 modules/dbus/include/dpl/dbus/object_proxy.h create mode 100644 modules/dbus/include/dpl/dbus/server.h create mode 100644 modules/dbus/src/connection.cpp create mode 100644 modules/dbus/src/dispatcher.cpp create mode 100644 modules/dbus/src/interface.cpp create mode 100644 modules/dbus/src/object.cpp create mode 100644 modules/dbus/src/object_proxy.cpp create mode 100644 modules/dbus/src/server.cpp create mode 100644 modules/event/config.cmake create mode 100644 modules/event/include/dpl/event/abstract_event_call.h create mode 100644 modules/event/include/dpl/event/abstract_event_dispatcher.h create mode 100644 modules/event/include/dpl/event/controller.h create mode 100644 modules/event/include/dpl/event/event_delivery.h create mode 100644 modules/event/include/dpl/event/event_delivery_detail.h create mode 100644 modules/event/include/dpl/event/event_delivery_injector.h create mode 100644 modules/event/include/dpl/event/event_delivery_messages.h create mode 100644 modules/event/include/dpl/event/event_listener.h create mode 100644 modules/event/include/dpl/event/event_support.h create mode 100644 modules/event/include/dpl/event/generic_event_call.h create mode 100644 modules/event/include/dpl/event/inter_context_delegate.h create mode 100644 modules/event/include/dpl/event/main_event_dispatcher.h create mode 100644 modules/event/include/dpl/event/model.h create mode 100644 modules/event/include/dpl/event/model_bind_to_dao.h create mode 100644 modules/event/include/dpl/event/nested_loop.h create mode 100644 modules/event/include/dpl/event/property.h create mode 100644 modules/event/include/dpl/event/thread_event_dispatcher.h create mode 100644 modules/event/src/abstract_event_call.cpp create mode 100644 modules/event/src/abstract_event_dispatcher.cpp create mode 100644 modules/event/src/controller.cpp create mode 100644 modules/event/src/event_delivery.cpp create mode 100644 modules/event/src/event_delivery_detail.cpp create mode 100644 modules/event/src/event_listener.cpp create mode 100644 modules/event/src/event_support.cpp create mode 100644 modules/event/src/generic_event_call.cpp create mode 100644 modules/event/src/inter_context_delegate.cpp create mode 100644 modules/event/src/main_event_dispatcher.cpp create mode 100644 modules/event/src/model.cpp create mode 100644 modules/event/src/nested_loop.cpp create mode 100644 modules/event/src/thread_event_dispatcher.cpp create mode 100644 modules/localization/config.cmake create mode 100644 modules/localization/include/dpl/localization/localization_utils.h create mode 100644 modules/localization/include/dpl/localization/w3c_file_localization.h create mode 100644 modules/localization/src/localization_utils.cpp create mode 100644 modules/localization/src/w3c_file_localization.cpp create mode 100644 modules/log/config.cmake create mode 100644 modules/log/include/dpl/log/abstract_log_provider.h create mode 100644 modules/log/include/dpl/log/dlog_log_provider.h create mode 100644 modules/log/include/dpl/log/log.h create mode 100644 modules/log/include/dpl/log/old_style_log_provider.h create mode 100644 modules/log/src/abstract_log_provider.cpp create mode 100644 modules/log/src/dlog_log_provider.cpp create mode 100644 modules/log/src/log.cpp create mode 100644 modules/log/src/old_style_log_provider.cpp create mode 100644 modules/popup/DESCRIPTION create mode 100644 modules/popup/config.cmake create mode 100644 modules/popup/include/dpl/popup/evas_object.h create mode 100644 modules/popup/include/dpl/popup/popup.h create mode 100644 modules/popup/include/dpl/popup/popup_controller.h create mode 100644 modules/popup/include/dpl/popup/popup_manager.h create mode 100644 modules/popup/include/dpl/popup/popup_object.h create mode 100644 modules/popup/include/dpl/popup/popup_renderer.h create mode 100644 modules/popup/src/evas_object.cpp create mode 100644 modules/popup/src/popup_controller.cpp create mode 100644 modules/popup/src/popup_manager.cpp create mode 100644 modules/popup/src/popup_renderer.cpp create mode 100644 modules/rpc/config.cmake create mode 100644 modules/rpc/include/dpl/rpc/abstract_rpc_connection.h create mode 100644 modules/rpc/include/dpl/rpc/abstract_rpc_connector.h create mode 100644 modules/rpc/include/dpl/rpc/generic_rpc_connection.h create mode 100644 modules/rpc/include/dpl/rpc/generic_socket_rpc_client.h create mode 100644 modules/rpc/include/dpl/rpc/generic_socket_rpc_connection.h create mode 100644 modules/rpc/include/dpl/rpc/generic_socket_rpc_server.h create mode 100644 modules/rpc/include/dpl/rpc/rpc_function.h create mode 100644 modules/rpc/include/dpl/rpc/unix_socket_rpc_client.h create mode 100644 modules/rpc/include/dpl/rpc/unix_socket_rpc_connection.h create mode 100644 modules/rpc/include/dpl/rpc/unix_socket_rpc_server.h create mode 100644 modules/rpc/src/abstract_rpc_connection.cpp create mode 100644 modules/rpc/src/abstract_rpc_connector.cpp create mode 100644 modules/rpc/src/generic_rpc_connection.cpp create mode 100644 modules/rpc/src/generic_socket_rpc_client.cpp create mode 100644 modules/rpc/src/generic_socket_rpc_connection.cpp create mode 100644 modules/rpc/src/generic_socket_rpc_server.cpp create mode 100644 modules/rpc/src/unix_socket_rpc_client.cpp create mode 100644 modules/rpc/src/unix_socket_rpc_connection.cpp create mode 100644 modules/rpc/src/unix_socket_rpc_server.cpp create mode 100644 modules/socket/config.cmake create mode 100644 modules/socket/include/dpl/socket/abstract_socket.h create mode 100644 modules/socket/include/dpl/socket/generic_socket.h create mode 100644 modules/socket/include/dpl/socket/unix_socket.h create mode 100644 modules/socket/include/dpl/socket/waitable_input_output_execution_context_support.h create mode 100644 modules/socket/src/generic_socket.cpp create mode 100644 modules/socket/src/unix_socket.cpp create mode 100644 modules/socket/src/waitable_input_output_execution_context_support.cpp create mode 100644 modules/test/config.cmake create mode 100644 modules/test/include/dpl/test/test_results_collector.h create mode 100644 modules/test/include/dpl/test/test_runner.h create mode 100644 modules/test/src/test_results_collector.cpp create mode 100644 modules/test/src/test_runner.cpp create mode 100644 modules/utils/config.cmake create mode 100644 modules/utils/include/file_utils.h create mode 100644 modules/utils/include/folder_size.h create mode 100644 modules/utils/include/mime_type_utils.h create mode 100644 modules/utils/include/warp_iri.h create mode 100644 modules/utils/include/widget_version.h create mode 100644 modules/utils/include/wrt_global_settings.h create mode 100644 modules/utils/include/wrt_global_settings_internal.h create mode 100644 modules/utils/include/wrt_utility.h create mode 100644 modules/utils/src/file_utils.cpp create mode 100644 modules/utils/src/folder_size.cpp create mode 100644 modules/utils/src/mime_type_utils.cpp create mode 100644 modules/utils/src/warp_iri.cpp create mode 100644 modules/utils/src/widget_version.cpp create mode 100644 modules/utils/src/wrt_global_settings.cpp create mode 100644 modules/utils/src/wrt_global_settings_internal.cpp create mode 100644 modules/utils/src/wrt_utility.cpp create mode 100644 modules/vcore/CMakeLists.txt create mode 100644 modules/vcore/src/CMakeLists.txt create mode 100644 modules/vcore/src/orm/DESCRIPTION create mode 100755 modules/vcore/src/orm/gen_db_md5.sh create mode 100644 modules/vcore/src/orm/orm_generator_vcore.h create mode 100644 modules/vcore/src/orm/vcore_db create mode 100644 modules/vcore/src/orm/vcore_db_definitions create mode 100644 modules/vcore/src/orm/vcore_db_sql_generator.h create mode 100644 modules/vcore/src/orm/version_db create mode 100644 modules/vcore/src/vcore/Base64.cpp create mode 100644 modules/vcore/src/vcore/Base64.h create mode 100644 modules/vcore/src/vcore/CRL.cpp create mode 100644 modules/vcore/src/vcore/CRL.h create mode 100644 modules/vcore/src/vcore/CachedCRL.cpp create mode 100644 modules/vcore/src/vcore/CachedCRL.h create mode 100644 modules/vcore/src/vcore/CachedOCSP.cpp create mode 100644 modules/vcore/src/vcore/CachedOCSP.h create mode 100644 modules/vcore/src/vcore/CertStoreType.h create mode 100644 modules/vcore/src/vcore/Certificate.cpp create mode 100644 modules/vcore/src/vcore/Certificate.h create mode 100644 modules/vcore/src/vcore/CertificateCacheDAO.cpp create mode 100644 modules/vcore/src/vcore/CertificateCacheDAO.h create mode 100644 modules/vcore/src/vcore/CertificateCollection.cpp create mode 100644 modules/vcore/src/vcore/CertificateCollection.h create mode 100644 modules/vcore/src/vcore/CertificateConfigReader.cpp create mode 100644 modules/vcore/src/vcore/CertificateConfigReader.h create mode 100644 modules/vcore/src/vcore/CertificateIdentifier.h create mode 100644 modules/vcore/src/vcore/CertificateLoader.cpp create mode 100644 modules/vcore/src/vcore/CertificateLoader.h create mode 100644 modules/vcore/src/vcore/CertificateStorage.h create mode 100644 modules/vcore/src/vcore/CertificateVerifier.cpp create mode 100644 modules/vcore/src/vcore/CertificateVerifier.h create mode 100644 modules/vcore/src/vcore/Config.cpp create mode 100644 modules/vcore/src/vcore/Config.h create mode 100644 modules/vcore/src/vcore/Database.cpp create mode 100644 modules/vcore/src/vcore/Database.h create mode 100644 modules/vcore/src/vcore/DeveloperModeValidator.cpp create mode 100644 modules/vcore/src/vcore/DeveloperModeValidator.h create mode 100644 modules/vcore/src/vcore/IAbstractResponseCache.h create mode 100644 modules/vcore/src/vcore/OCSP.cpp create mode 100644 modules/vcore/src/vcore/OCSP.h create mode 100644 modules/vcore/src/vcore/OCSPCertMgrUtil.cpp create mode 100644 modules/vcore/src/vcore/OCSPCertMgrUtil.h create mode 100644 modules/vcore/src/vcore/OCSPUtil.c create mode 100644 modules/vcore/src/vcore/ParserSchema.h create mode 100644 modules/vcore/src/vcore/ReferenceValidator.cpp create mode 100644 modules/vcore/src/vcore/ReferenceValidator.h create mode 100644 modules/vcore/src/vcore/RevocationCheckerBase.cpp create mode 100644 modules/vcore/src/vcore/RevocationCheckerBase.h create mode 100644 modules/vcore/src/vcore/SSLContainers.h create mode 100644 modules/vcore/src/vcore/SaxReader.cpp create mode 100644 modules/vcore/src/vcore/SaxReader.h create mode 100644 modules/vcore/src/vcore/SignatureData.h create mode 100644 modules/vcore/src/vcore/SignatureFinder.cpp create mode 100644 modules/vcore/src/vcore/SignatureFinder.h create mode 100644 modules/vcore/src/vcore/SignatureReader.cpp create mode 100644 modules/vcore/src/vcore/SignatureReader.h create mode 100644 modules/vcore/src/vcore/SignatureValidator.cpp create mode 100644 modules/vcore/src/vcore/SignatureValidator.h create mode 100644 modules/vcore/src/vcore/SoupMessageSendAsync.cpp create mode 100644 modules/vcore/src/vcore/SoupMessageSendAsync.h create mode 100644 modules/vcore/src/vcore/SoupMessageSendBase.cpp create mode 100644 modules/vcore/src/vcore/SoupMessageSendBase.h create mode 100644 modules/vcore/src/vcore/SoupMessageSendSync.cpp create mode 100644 modules/vcore/src/vcore/SoupMessageSendSync.h create mode 100644 modules/vcore/src/vcore/VCore.cpp create mode 100644 modules/vcore/src/vcore/VCore.h create mode 100644 modules/vcore/src/vcore/VCorePrivate.h create mode 100644 modules/vcore/src/vcore/ValidatorCommon.h create mode 100644 modules/vcore/src/vcore/ValidatorFactories.cpp create mode 100644 modules/vcore/src/vcore/ValidatorFactories.h create mode 100644 modules/vcore/src/vcore/VerificationStatus.cpp create mode 100644 modules/vcore/src/vcore/VerificationStatus.h create mode 100644 modules/vcore/src/vcore/WacOrigin.cpp create mode 100644 modules/vcore/src/vcore/WacOrigin.h create mode 100644 modules/vcore/src/vcore/XmlsecAdapter.cpp create mode 100644 modules/vcore/src/vcore/XmlsecAdapter.h create mode 100644 modules/vcore/src/vcore/scoped_gpointer.h create mode 100644 modules/widget_dao/CMakeLists.txt create mode 100644 modules/widget_dao/dao/WrtDatabase.cpp create mode 100644 modules/widget_dao/dao/bind_to_dao.h create mode 100644 modules/widget_dao/dao/common_dao_types.cpp create mode 100644 modules/widget_dao/dao/config_parser_data.cpp create mode 100644 modules/widget_dao/dao/feature_dao.cpp create mode 100644 modules/widget_dao/dao/feature_dao_read_only.cpp create mode 100644 modules/widget_dao/dao/global_config.cpp create mode 100644 modules/widget_dao/dao/global_dao.cpp create mode 100644 modules/widget_dao/dao/global_dao_read_only.cpp create mode 100644 modules/widget_dao/dao/path_builder.cpp create mode 100644 modules/widget_dao/dao/plugin_dao.cpp create mode 100644 modules/widget_dao/dao/plugin_dao_read_only.cpp create mode 100644 modules/widget_dao/dao/property_dao.cpp create mode 100644 modules/widget_dao/dao/property_dao_read_only.cpp create mode 100644 modules/widget_dao/dao/webruntime_database.cpp create mode 100644 modules/widget_dao/dao/widget_dao.cpp create mode 100644 modules/widget_dao/dao/widget_dao_read_only.cpp create mode 100644 modules/widget_dao/include/dpl/wrt-dao-ro/WrtDatabase.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-ro/common_dao_types.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-ro/config_parser_data.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-ro/feature_dao_read_only.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-ro/feature_model.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-ro/global_config.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-ro/global_dao_read_only.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-ro/path_builder.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-ro/plugin_dao_read_only.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-ro/property_dao_read_only.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-ro/webruntime_database.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-ro/widget_config.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-ro/widget_dao_read_only.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-rw/feature_dao.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-rw/global_dao.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-rw/plugin_dao.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-rw/property_dao.h create mode 100644 modules/widget_dao/include/dpl/wrt-dao-rw/widget_dao.h create mode 100755 modules/widget_dao/orm/gen_db_md5.sh create mode 100644 modules/widget_dao/orm/iana_db create mode 100644 modules/widget_dao/orm/orm_generator_wrt.h create mode 100644 modules/widget_dao/orm/version_db create mode 100644 modules/widget_dao/orm/wrt_db create mode 100644 modules/widget_dao/orm/wrt_db_definitions create mode 100644 modules/widget_dao/orm/wrt_db_sql_generator.h create mode 100644 packaging/dpl.spec create mode 100644 tests/CMakeLists.txt create mode 100644 tests/ace/AttributeSetter.cpp create mode 100644 tests/ace/AttributeSetter.h create mode 100644 tests/ace/CMakeLists.txt create mode 100644 tests/ace/Interfaces.cpp create mode 100644 tests/ace/Interfaces.h create mode 100644 tests/ace/PEPSingleton.cpp create mode 100644 tests/ace/PEPSingleton.h create mode 100644 tests/ace/TestSuite01.cpp create mode 100644 tests/ace/TestSuite02.cpp create mode 100644 tests/ace/TestSuite03.cpp create mode 100644 tests/ace/TestSuite04.cpp create mode 100644 tests/ace/TestSuite05.cpp create mode 100644 tests/ace/TestSuite06.cpp create mode 100644 tests/ace/TestSuite07.cpp create mode 100644 tests/ace/ace_tests.cpp create mode 100644 tests/ace/loop_control.cpp create mode 100644 tests/ace/loop_control.h create mode 100644 tests/ace/test-configuration/CMTest/CMakeLists.txt create mode 100644 tests/ace/test-configuration/CMTest/active/CMakeLists.txt create mode 100644 tests/ace/test-configuration/CMTest/active/bondixml.dtd create mode 100644 tests/ace/test-configuration/CMTest/active/pms_general-test.xml create mode 100644 tests/ace/test-configuration/CMTest/pms_config.xml create mode 100644 tests/ace/test-configuration/CMTest/pms_general-test.xml create mode 100644 tests/ace/test-configuration/CMTest/policyTest1.xml create mode 100644 tests/ace/test-configuration/CMTest/policyTest2.xml create mode 100644 tests/ace/test-configuration/CMTest/policyTest3.xml create mode 100644 tests/ace/test-configuration/CMakeLists.txt create mode 100644 tests/ace/test-configuration/attr_policy-example.xml create mode 100644 tests/ace/test-configuration/attr_policy-example1.xml create mode 100644 tests/ace/test-configuration/attr_policy-example2.xml create mode 100644 tests/ace/test-configuration/attr_policy-example3.xml create mode 100644 tests/ace/test-configuration/attr_policy-example4.xml create mode 100644 tests/ace/test-configuration/attr_policy-example5.xml create mode 100644 tests/ace/test-configuration/attr_policy-example6.xml create mode 100644 tests/ace/test-configuration/attr_policy-example7.xml create mode 100644 tests/ace/test-configuration/attr_policy-example8.xml create mode 100644 tests/ace/test-configuration/attre_config.xml create mode 100644 tests/ace/test-configuration/general-test.xml create mode 100644 tests/ace/test-configuration/interceptpolicy.xml create mode 100644 tests/ace/test-configuration/old_policy-example.xml create mode 100644 tests/ace/test-configuration/policy-example.xml create mode 100644 tests/ace/test-configuration/policy-example2.xml create mode 100644 tests/ace/test-configuration/policy-example3.xml create mode 100644 tests/ace/test-configuration/policy-test-gsettings.xml create mode 100644 tests/ace/test-configuration/policy-test.xml create mode 100644 tests/ace/test-configuration/policy-wac-2.0.xml create mode 100644 tests/ace/test-configuration/policy_example.xml create mode 100644 tests/ace/test-configuration/reproduce-abort-test.xml create mode 100644 tests/ace/test-configuration/undefined-test.xml create mode 100644 tests/core/CMakeLists.txt create mode 100644 tests/core/DESCRIPTION create mode 100644 tests/core/data/sample.zip create mode 100644 tests/core/main.cpp create mode 100644 tests/core/test_address.cpp create mode 100644 tests/core/test_binary_queue.cpp create mode 100644 tests/core/test_fast_delegate.cpp create mode 100644 tests/core/test_foreach.cpp create mode 100644 tests/core/test_log_unhandled_exception.cpp create mode 100644 tests/core/test_once.cpp create mode 100644 tests/core/test_scoped_array.cpp create mode 100644 tests/core/test_scoped_close.cpp create mode 100644 tests/core/test_scoped_fclose.cpp create mode 100644 tests/core/test_scoped_free.cpp create mode 100644 tests/core/test_scoped_ptr.cpp create mode 100644 tests/core/test_semaphore.cpp create mode 100644 tests/core/test_serialization.cpp create mode 100644 tests/core/test_shared_ptr.cpp create mode 100644 tests/core/test_string.cpp create mode 100644 tests/core/test_task.cpp create mode 100644 tests/core/test_thread.cpp create mode 100644 tests/core/test_type_list.cpp create mode 100644 tests/core/test_zip_input.cpp create mode 100644 tests/db/CMakeLists.txt create mode 100644 tests/db/main.cpp create mode 100644 tests/db/orm/CMakeLists.txt create mode 100644 tests/db/orm/dpl_orm_test.db create mode 100644 tests/db/orm/dpl_orm_test_db create mode 100644 tests/db/orm/dpl_orm_test_db.sql create mode 100644 tests/db/orm/dpl_orm_test_db_definitions create mode 100644 tests/db/orm/dpl_orm_test_db_sql_generator.h create mode 100644 tests/db/orm/generator_dpl_orm_test.h create mode 100644 tests/db/test_orm.cpp create mode 100644 tests/db/test_sql_connection.cpp create mode 100644 tests/dbus/CMakeLists.txt create mode 100644 tests/dbus/data/org.tizen.DBusTestService.service create mode 100644 tests/dbus/dbus_test.cpp create mode 100644 tests/dbus/dbus_test.h create mode 100644 tests/dbus/loop_control.cpp create mode 100644 tests/dbus/loop_control.h create mode 100644 tests/dbus/main.cpp create mode 100644 tests/dbus/test_cases.cpp create mode 100644 tests/dbus/test_service.cpp create mode 100644 tests/event/CMakeLists.txt create mode 100644 tests/event/main.cpp create mode 100644 tests/event/test_controller.cpp create mode 100644 tests/event/test_event_support.cpp create mode 100644 tests/event/test_ic_delegate.cpp create mode 100644 tests/event/test_property.cpp create mode 100644 tests/localization/CMakeLists.txt create mode 100644 tests/localization/files/CMakeLists.txt create mode 100644 tests/localization/files/one create mode 100644 tests/localization/files/two create mode 100644 tests/localization/mockup_include/dpl/wrt-dao-ro/common_dao_types.h create mode 100644 tests/localization/mockup_include/dpl/wrt-dao-rw/widget_dao.h create mode 100644 tests/localization/mockup_src/widget_dao.cpp create mode 100644 tests/localization/test_localization.cpp create mode 100644 tests/localization/test_suite01.cpp create mode 100644 tests/vcore/CMakeLists.txt create mode 100644 tests/vcore/TestCRL.cpp create mode 100644 tests/vcore/TestCRL.h create mode 100644 tests/vcore/TestCases.cpp create mode 100644 tests/vcore/TestEnv.cpp create mode 100644 tests/vcore/TestEnv.h create mode 100755 tests/vcore/certificate-generator/create_certs.sh create mode 100644 tests/vcore/certificate-generator/demoCA.init/cacert.pem create mode 100644 tests/vcore/certificate-generator/demoCA.init/careq.pem create mode 100644 tests/vcore/certificate-generator/demoCA.init/index.txt create mode 100644 tests/vcore/certificate-generator/demoCA.init/index.txt.attr create mode 100644 tests/vcore/certificate-generator/demoCA.init/index.txt.old create mode 100644 tests/vcore/certificate-generator/demoCA.init/newcerts/00.pem create mode 100644 tests/vcore/certificate-generator/demoCA.init/private/cakey.pem create mode 100644 tests/vcore/certificate-generator/demoCA.init/serial create mode 100644 tests/vcore/certificate-generator/demoCA.init/serial.old create mode 100644 tests/vcore/certificate-generator/dpl-tests-vcore-ocsp-server.sh create mode 100644 tests/vcore/certificate-generator/openssl.cnf create mode 100644 tests/vcore/test-cases/keys/CAbundle.crt create mode 100644 tests/vcore/test-cases/keys/README create mode 100644 tests/vcore/test-cases/keys/filip_rsa_cert.pem create mode 100644 tests/vcore/test-cases/keys/filip_rsa_key.pem create mode 100644 tests/vcore/test-cases/keys/magda_dsa_cert.pem create mode 100644 tests/vcore/test-cases/keys/magda_dsa_key.pem create mode 100644 tests/vcore/test-cases/keys/ocsp_level0deprecated.crt create mode 100644 tests/vcore/test-cases/keys/ocsp_level1.crt create mode 100644 tests/vcore/test-cases/keys/ocsp_level2.crt create mode 100644 tests/vcore/test-cases/keys/ocsp_rootca.crt create mode 100644 tests/vcore/test-cases/keys/operator.root.cert.pem create mode 100644 tests/vcore/test-cases/keys/operator.second.cert.pem create mode 100644 tests/vcore/test-cases/keys/operator.second.key.pem create mode 100644 tests/vcore/test-cases/keys/operator.second.p12 create mode 100644 tests/vcore/test-cases/keys/root_cacert.pem create mode 100644 tests/vcore/test-cases/keys/root_cakey.pem create mode 100644 tests/vcore/test-cases/widget/author-signature.xml create mode 100755 tests/vcore/test-cases/widget/config.xml create mode 100755 tests/vcore/test-cases/widget/index.html create mode 100644 tests/vcore/test-cases/widget/signature1.xml create mode 100644 tests/vcore/test-cases/widget/signature22.xml create mode 100644 tests/vcore/vcore_tests.cpp diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt new file mode 100644 index 0000000..4783a46 --- /dev/null +++ b/3rdparty/CMakeLists.txt @@ -0,0 +1,63 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# + +# Check required modules +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(SYS_3RDPARTY REQUIRED) + +# Set 3rd party sources +SET(DPL_3RDPARTY_SOURCES + # Minizip + ${PROJECT_SOURCE_DIR}/3rdparty/minizip/ioapi.c +# ${PROJECT_SOURCE_DIR}/3rdparty/minizip/iowin32.c + ${PROJECT_SOURCE_DIR}/3rdparty/minizip/miniunz.c + ${PROJECT_SOURCE_DIR}/3rdparty/minizip/minizip.c + ${PROJECT_SOURCE_DIR}/3rdparty/minizip/mztools.c + ${PROJECT_SOURCE_DIR}/3rdparty/minizip/unzip.c + ${PROJECT_SOURCE_DIR}/3rdparty/minizip/zip.c) + +# Set 3rd party headers +SET(DPL_3RDPARTY_HEADERS + # Minizip + ${PROJECT_SOURCE_DIR}/3rdparty/minizip/crypt.h + ${PROJECT_SOURCE_DIR}/3rdparty/minizip/ioapi.h +# ${PROJECT_SOURCE_DIR}/3rdparty/minizip/iowin32.h + ${PROJECT_SOURCE_DIR}/3rdparty/minizip/mztools.h + ${PROJECT_SOURCE_DIR}/3rdparty/minizip/unzip.h + ${PROJECT_SOURCE_DIR}/3rdparty/minizip/zip.h) + +# Set 3rd party include directory +SET(DPL_3RDPARTY_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/3rdparty) + +# Set name of binaries being created +SET(TARGET_3RDPARTY "lib3rdparty") + +# Add 3rd party include directories +INCLUDE_DIRECTORIES(${DPL_3RDPARTY_INCLUDE_DIR}) + +# Add system include files +INCLUDE_DIRECTORIES(${SYS_3RDPARTY_INCLUDE_DIRS}) +LINK_DIRECTORIES(${SYS_3RDPARTY_LIBRARY_DIRS}) + +# Build shared library +ADD_LIBRARY(${TARGET_3RDPARTY} STATIC ${DPL_3RDPARTY_SOURCES}) +TARGET_LINK_LIBRARIES(${TARGET_3RDPARTY} ${SYS_3RDPARTY_LIBRARIES} z) + diff --git a/3rdparty/DESCRIPTION b/3rdparty/DESCRIPTION new file mode 100644 index 0000000..6f383f3 --- /dev/null +++ b/3rdparty/DESCRIPTION @@ -0,0 +1 @@ +Third-party libraries diff --git a/3rdparty/fastdelegate/CPOL.html b/3rdparty/fastdelegate/CPOL.html new file mode 100644 index 0000000..e035a60 --- /dev/null +++ b/3rdparty/fastdelegate/CPOL.html @@ -0,0 +1,251 @@ + + +The Code Project Open License (COPL) + + + + +

The Code Project Open License (CPOL) 1.02

+
+ +
+
+ +

Preamble

+

+ This License governs Your use of the Work. This License is intended to allow developers + to use the Source Code and Executable Files provided as part of the Work in any + application in any form. +

+

+ The main points subject to the terms of the License are:

+
    +
  • Source Code and Executable Files can be used in commercial applications;
  • +
  • Source Code and Executable Files can be redistributed; and
  • +
  • Source Code can be modified to create derivative works.
  • +
  • No claim of suitability, guarantee, or any warranty whatsoever is provided. The software is + provided "as-is".
  • +
  • The Article accompanying the Work may not be distributed or republished without the + Author's consent
  • +
+ +

+ This License is entered between You, the individual or other entity reading or otherwise + making use of the Work licensed pursuant to this License and the individual or other + entity which offers the Work under the terms of this License ("Author").

+ +

License

+

+ THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CODE PROJECT OPEN + LICENSE ("LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE + LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT + LAW IS PROHIBITED.

+

+ BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HEREIN, YOU ACCEPT AND AGREE TO BE + BOUND BY THE TERMS OF THIS LICENSE. THE AUTHOR GRANTS YOU THE RIGHTS CONTAINED HEREIN + IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. IF YOU DO NOT + AGREE TO ACCEPT AND BE BOUND BY THE TERMS OF THIS LICENSE, YOU CANNOT MAKE ANY + USE OF THE WORK.

+ +
    +
  1. Definitions. + +
      +
    1. "Articles" means, collectively, all articles written by Author + which describes how the Source Code and Executable Files for the Work may be used + by a user.
    2. +
    3. "Author" means the individual or entity that offers the Work under the terms + of this License.
    4. +
    5. "Derivative Work" means a work based upon the Work or upon the + Work and other pre-existing works.
    6. +
    7. "Executable Files" refer to the executables, binary files, configuration + and any required data files included in the Work.
    8. +
    9. "Publisher" means the provider of the website, magazine, CD-ROM, DVD or other + medium from or by which the Work is obtained by You.
    10. +
    11. "Source Code" refers to the collection of source code and configuration files + used to create the Executable Files.
    12. +
    13. "Standard Version" refers to such a Work if it has not been modified, or + has been modified in accordance with the consent of the Author, such consent being + in the full discretion of the Author.
    14. +
    15. "Work" refers to the collection of files distributed by the Publisher, including + the Source Code, Executable Files, binaries, data files, documentation, whitepapers + and the Articles.
    16. +
    17. "You" is you, an individual or entity wishing to use the Work and exercise + your rights under this License. +
    18. +
    +
  2. + +
  3. Fair Use/Fair Use Rights. Nothing in this License is intended to + reduce, limit, or restrict any rights arising from fair use, fair dealing, first + sale or other limitations on the exclusive rights of the copyright owner under copyright + law or other applicable laws. +
  4. + +
  5. License Grant. Subject to the terms and conditions of this License, + the Author hereby grants You a worldwide, royalty-free, non-exclusive, perpetual + (for the duration of the applicable copyright) license to exercise the rights in + the Work as stated below: + +
      +
    1. You may use the standard version of the Source Code or Executable Files in Your + own applications.
    2. +
    3. You may apply bug fixes, portability fixes and other modifications obtained from + the Public Domain or from the Author. A Work modified in such a way shall still + be considered the standard version and will be subject to this License.
    4. +
    5. You may otherwise modify Your copy of this Work (excluding the Articles) in any + way to create a Derivative Work, provided that You insert a prominent notice in + each changed file stating how, when and where You changed that file.
    6. +
    7. You may distribute the standard version of the Executable Files and Source Code + or Derivative Work in aggregate with other (possibly commercial) programs as part + of a larger (possibly commercial) software distribution.
    8. +
    9. The Articles discussing the Work published in any form by the author may not be + distributed or republished without the Author's consent. The author retains + copyright to any such Articles. You may use the Executable Files and Source Code + pursuant to this License but you may not repost or republish or otherwise distribute + or make available the Articles, without the prior written consent of the Author.
    10. +
    + + Any subroutines or modules supplied by You and linked into the Source Code or Executable + Files this Work shall not be considered part of this Work and will not be subject + to the terms of this License. +
  6. + +
  7. Patent License. Subject to the terms and conditions of this License, + each Author hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, + irrevocable (except as stated in this section) patent license to make, have made, use, import, + and otherwise transfer the Work.
  8. + +
  9. Restrictions. The license granted in Section 3 above is expressly + made subject to and limited by the following restrictions: + +
      +
    1. You agree not to remove any of the original copyright, patent, trademark, and + attribution notices and associated disclaimers that may appear in the Source Code + or Executable Files.
    2. +
    3. You agree not to advertise or in any way imply that this Work is a product of Your + own.
    4. +
    5. The name of the Author may not be used to endorse or promote products derived from + the Work without the prior written consent of the Author.
    6. +
    7. You agree not to sell, lease, or rent any part of the Work. This does not restrict + you from including the Work or any part of the Work inside a larger software + distribution that itself is being sold. The Work by itself, though, cannot be sold, + leased or rented.
    8. +
    9. You may distribute the Executable Files and Source Code only under the terms of + this License, and You must include a copy of, or the Uniform Resource Identifier + for, this License with every copy of the Executable Files or Source Code You distribute + and ensure that anyone receiving such Executable Files and Source Code agrees that + the terms of this License apply to such Executable Files and/or Source Code. You + may not offer or impose any terms on the Work that alter or restrict the terms of + this License or the recipients' exercise of the rights granted hereunder. You + may not sublicense the Work. You must keep intact all notices that refer to this + License and to the disclaimer of warranties. You may not distribute the Executable + Files or Source Code with any technological measures that control access or use + of the Work in a manner inconsistent with the terms of this License.
    10. +
    11. You agree not to use the Work for illegal, immoral or improper purposes, or on pages + containing illegal, immoral or improper material. The Work is subject to applicable + export laws. You agree to comply with all such laws and regulations that may apply + to the Work after Your receipt of the Work. +
    12. +
    +
  10. + +
  11. Representations, Warranties and Disclaimer. THIS WORK IS PROVIDED + "AS IS", "WHERE IS" AND "AS AVAILABLE", WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES + OR CONDITIONS OR GUARANTEES. YOU, THE USER, ASSUME ALL RISK IN ITS USE, INCLUDING + COPYRIGHT INFRINGEMENT, PATENT INFRINGEMENT, SUITABILITY, ETC. AUTHOR EXPRESSLY + DISCLAIMS ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES OR CONDITIONS, INCLUDING + WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY + OR FITNESS FOR A PARTICULAR PURPOSE, OR ANY WARRANTY OF TITLE OR NON-INFRINGEMENT, + OR THAT THE WORK (OR ANY PORTION THEREOF) IS CORRECT, USEFUL, BUG-FREE OR FREE OF + VIRUSES. YOU MUST PASS THIS DISCLAIMER ON WHENEVER YOU DISTRIBUTE THE WORK OR DERIVATIVE + WORKS. +
  12. + +
  13. Indemnity. You agree to defend, indemnify and hold harmless the Author and + the Publisher from and against any claims, suits, losses, damages, liabilities, + costs, and expenses (including reasonable legal or attorneys’ fees) resulting from + or relating to any use of the Work by You. +
  14. + +
  15. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE + LAW, IN NO EVENT WILL THE AUTHOR OR THE PUBLISHER BE LIABLE TO YOU ON ANY LEGAL + THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES + ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK OR OTHERWISE, EVEN IF THE AUTHOR + OR THE PUBLISHER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +
  16. + +
  17. Termination. + +
      +
    1. This License and the rights granted hereunder will terminate automatically upon + any breach by You of any term of this License. Individuals or entities who have + received Derivative Works from You under this License, however, will not have their + licenses terminated provided such individuals or entities remain in full compliance + with those licenses. Sections 1, 2, 6, 7, 8, 9, 10 and 11 will survive any termination + of this License.
    2. + +
    3. If You bring a copyright, trademark, patent or any other infringement claim against + any contributor over infringements You claim are made by the Work, your License + from such contributor to the Work ends automatically.
    4. + +
    5. Subject to the above terms and conditions, this License is perpetual (for the duration + of the applicable copyright in the Work). Notwithstanding the above, the Author + reserves the right to release the Work under different license terms or to stop + distributing the Work at any time; provided, however that any such election will + not serve to withdraw this License (or any other license that has been, or is required + to be, granted under the terms of this License), and this License will continue + in full force and effect unless terminated as stated above. +
    6. +
    +
  18. + +
  19. Publisher. The parties hereby confirm that the Publisher shall + not, under any circumstances, be responsible for and shall not have any liability + in respect of the subject matter of this License. The Publisher makes no warranty + whatsoever in connection with the Work and shall not be liable to You or any party + on any legal theory for any damages whatsoever, including without limitation any + general, special, incidental or consequential damages arising in connection to this + license. The Publisher reserves the right to cease making the Work available to + You at any time without notice
  20. + +
  21. Miscellaneous + +
      +
    1. This License shall be governed by the laws of the location of the head office of + the Author or if the Author is an individual, the laws of location of the principal + place of residence of the Author.
    2. +
    3. If any provision of this License is invalid or unenforceable under applicable law, + it shall not affect the validity or enforceability of the remainder of the terms + of this License, and without further action by the parties to this License, such + provision shall be reformed to the minimum extent necessary to make such provision + valid and enforceable.
    4. +
    5. No term or provision of this License shall be deemed waived and no breach consented + to unless such waiver or consent shall be in writing and signed by the party to + be charged with such waiver or consent.
    6. +
    7. This License constitutes the entire agreement between the parties with respect to + the Work licensed herein. There are no understandings, agreements or representations + with respect to the Work not specified herein. The Author shall not be bound by + any additional provisions that may appear in any communication from You. This License + may not be modified without the mutual written agreement of the Author and You. +
    8. +
    + +
  22. +
+ +
+
+ + + diff --git a/3rdparty/fastdelegate/DESCRIPTION b/3rdparty/fastdelegate/DESCRIPTION new file mode 100644 index 0000000..85409db --- /dev/null +++ b/3rdparty/fastdelegate/DESCRIPTION @@ -0,0 +1 @@ +Support for delegate constructs diff --git a/3rdparty/fastdelegate/Demo.cpp b/3rdparty/fastdelegate/Demo.cpp new file mode 100644 index 0000000..2b4d6f6 --- /dev/null +++ b/3rdparty/fastdelegate/Demo.cpp @@ -0,0 +1,143 @@ +#include +#include "FastDelegate.h" +// Demonstrate the syntax for FastDelegates. +// -Don Clugston, May 2004. +// It's a really boring example, but it shows the most important cases. + +// Declare some functions of varying complexity... +void SimpleStaticFunction(int num, char *str) { + printf("In SimpleStaticFunction. Num=%d, str = %s\n", num, str); +} + +void SimpleVoidFunction() { + printf("In SimpleVoidFunction with no parameters.\n"); +} + +class CBaseClass { +protected: + char *m_name; +public: + CBaseClass(char *name) : m_name(name) {}; + void SimpleMemberFunction(int num, char *str) { + printf("In SimpleMemberFunction in %s. Num=%d, str = %s\n", m_name, num, str); } + int SimpleMemberFunctionReturnsInt(int num, char *str) { + printf("In SimpleMemberFunction in %s. Num=%d, str = %s\n", m_name, num, str); return -1; } + void ConstMemberFunction(int num, char *str) const { + printf("In ConstMemberFunction in %s. Num=%d, str = %s\n", m_name, num, str); } + virtual void SimpleVirtualFunction(int num, char *str) { + printf("In SimpleVirtualFunction in %s. Num=%d, str = %s\n", m_name, num, str); } + static void StaticMemberFunction(int num, char *str) { + printf("In StaticMemberFunction. Num=%d, str =%s\n", num, str); } +}; + +class COtherClass { + double rubbish; // to ensure this class has non-zero size. +public: + virtual void UnusedVirtualFunction(void) { } + virtual void TrickyVirtualFunction(int num, char *str)=0; +}; + +class VeryBigClass { + int letsMakeThingsComplicated[400]; +}; + +// This declaration ensures that we get a convoluted class heirarchy. +class CDerivedClass : public VeryBigClass, virtual public COtherClass, virtual public CBaseClass +{ + double m_somemember[8]; +public: + CDerivedClass() : CBaseClass("Base of Derived") { m_somemember[0]=1.2345; } + void SimpleDerivedFunction(int num, char *str) { printf("In SimpleDerived. num=%d\n", num); } + virtual void AnotherUnusedVirtualFunction(int num, char *str) {} + virtual void TrickyVirtualFunction(int num, char *str) { + printf("In Derived TrickyMemberFunction. Num=%d, str = %s\n", num, str); + } +}; + +using namespace fastdelegate; + +int main(void) +{ + // Delegates with up to 8 parameters are supported. + // Here's the case for a void function. + // We declare a delegate and attach it to SimpleVoidFunction() + printf("-- FastDelegate demo --\nA no-parameter delegate is declared using FastDelegate0\n\n"); + + FastDelegate0<> noparameterdelegate(&SimpleVoidFunction); + + noparameterdelegate(); // invoke the delegate - this calls SimpleVoidFunction() + + printf("\n-- Examples using two-parameter delegates (int, char *) --\n\n"); + + // By default, the return value is void. + typedef FastDelegate2 MyDelegate; + + // If you want to have a non-void return value, put it at the end. + typedef FastDelegate2 IntMyDelegate; + + + MyDelegate funclist[12]; // delegates are initialized to empty + CBaseClass a("Base A"); + CBaseClass b("Base B"); + CDerivedClass d; + CDerivedClass c; + + IntMyDelegate newdeleg; + newdeleg = MakeDelegate(&a, &CBaseClass::SimpleMemberFunctionReturnsInt); + + // Binding a simple member function + funclist[0].bind(&a, &CBaseClass::SimpleMemberFunction); + + // You can also bind static (free) functions + funclist[1].bind(&SimpleStaticFunction); + // and static member functions + funclist[2].bind(&CBaseClass::StaticMemberFunction); + // and const member functions (these only need a const class pointer). + funclist[11].bind( (const CBaseClass *)&a, &CBaseClass::ConstMemberFunction); + funclist[3].bind( &a, &CBaseClass::ConstMemberFunction); + // and virtual member functions + funclist[4].bind(&b, &CBaseClass::SimpleVirtualFunction); + + // You can also use the = operator. For static functions, a fastdelegate + // looks identical to a simple function pointer. + funclist[5] = &CBaseClass::StaticMemberFunction; + + // The weird rule about the class of derived member function pointers is avoided. + // For MSVC, you can use &CDerivedClass::SimpleVirtualFunction here, but DMC will complain. + // Note that as well as .bind(), you can also use the MakeDelegate() + // global function. + funclist[6] = MakeDelegate(&d, &CBaseClass::SimpleVirtualFunction); + + // The worst case is an abstract virtual function of a virtually-derived class + // with at least one non-virtual base class. This is a VERY obscure situation, + // which you're unlikely to encounter in the real world. + // FastDelegate versions prior to 1.3 had problems with this case on VC6. + // Now, it works without problems on all compilers. + funclist[7].bind(&c, &CDerivedClass::TrickyVirtualFunction); + // BUT... in such cases you should be using the base class as an + // interface, anyway. + funclist[8].bind(&c, &COtherClass::TrickyVirtualFunction); + // Calling a function that was first declared in the derived class is straightforward + funclist[9] = MakeDelegate(&c, &CDerivedClass::SimpleDerivedFunction); + + // You can also bind directly using the constructor + MyDelegate dg(&b, &CBaseClass::SimpleVirtualFunction); + + char *msg = "Looking for equal delegate"; + for (int i=0; i<12; i++) { + printf("%d :", i); + // The == and != operators are provided + // Note that they work even for inline functions. + if (funclist[i]==dg) { msg = "Found equal delegate"; }; + // operator ! can be used to test for an empty delegate + // You can also use the .empty() member function. + if (!funclist[i]) { + printf("Delegate is empty\n"); + } else { + // Invocation generates optimal assembly code. + funclist[i](i, msg); + }; + } + return 0; +} + diff --git a/3rdparty/fastdelegate/LICENSE b/3rdparty/fastdelegate/LICENSE new file mode 100644 index 0000000..1db661f --- /dev/null +++ b/3rdparty/fastdelegate/LICENSE @@ -0,0 +1,7 @@ +License + +The source code attached to this article is released into the public domain. +You may use it for any purpose. Frankly, writing the article was about ten +times as much work as writing the code. Of course, if you create great software +using the code, I would be interested to hear about it. And submissions are always welcome. + diff --git a/3rdparty/minizip/DESCRIPTION b/3rdparty/minizip/DESCRIPTION new file mode 100644 index 0000000..d699bf1 --- /dev/null +++ b/3rdparty/minizip/DESCRIPTION @@ -0,0 +1 @@ +Minizip - ZIP file format support diff --git a/3rdparty/minizip/Makefile b/3rdparty/minizip/Makefile new file mode 100644 index 0000000..84eaad2 --- /dev/null +++ b/3rdparty/minizip/Makefile @@ -0,0 +1,25 @@ +CC=cc +CFLAGS=-O -I../.. + +UNZ_OBJS = miniunz.o unzip.o ioapi.o ../../libz.a +ZIP_OBJS = minizip.o zip.o ioapi.o ../../libz.a + +.c.o: + $(CC) -c $(CFLAGS) $*.c + +all: miniunz minizip + +miniunz: $(UNZ_OBJS) + $(CC) $(CFLAGS) -o $@ $(UNZ_OBJS) + +minizip: $(ZIP_OBJS) + $(CC) $(CFLAGS) -o $@ $(ZIP_OBJS) + +test: miniunz minizip + ./minizip test readme.txt + ./miniunz -l test.zip + mv readme.txt readme.old + ./miniunz test.zip + +clean: + /bin/rm -f *.o *~ minizip miniunz diff --git a/3rdparty/minizip/MiniZip64_Changes.txt b/3rdparty/minizip/MiniZip64_Changes.txt new file mode 100644 index 0000000..13a1bd9 --- /dev/null +++ b/3rdparty/minizip/MiniZip64_Changes.txt @@ -0,0 +1,6 @@ + +MiniZip 1.1 was derrived from MiniZip at version 1.01f + +Change in 1.0 (Okt 2009) + - **TODO - Add history** + diff --git a/3rdparty/minizip/MiniZip64_info.txt b/3rdparty/minizip/MiniZip64_info.txt new file mode 100644 index 0000000..57d7152 --- /dev/null +++ b/3rdparty/minizip/MiniZip64_info.txt @@ -0,0 +1,74 @@ +MiniZip - Copyright (c) 1998-2010 - by Gilles Vollant - version 1.1 64 bits from Mathias Svensson + +Introduction +--------------------- +MiniZip 1.1 is built from MiniZip 1.0 by Gilles Vollant ( http://www.winimage.com/zLibDll/minizip.html ) + +When adding ZIP64 support into minizip it would result into risk of breaking compatibility with minizip 1.0. +All possible work was done for compatibility. + + +Background +--------------------- +When adding ZIP64 support Mathias Svensson found that Even Rouault have added ZIP64 +support for unzip.c into minizip for a open source project called gdal ( http://www.gdal.org/ ) + +That was used as a starting point. And after that ZIP64 support was added to zip.c +some refactoring and code cleanup was also done. + + +Changed from MiniZip 1.0 to MiniZip 1.1 +--------------------------------------- +* Added ZIP64 support for unzip ( by Even Rouault ) +* Added ZIP64 support for zip ( by Mathias Svensson ) +* Reverted some changed that Even Rouault did. +* Bunch of patches received from Gulles Vollant that he received for MiniZip from various users. +* Added unzip patch for BZIP Compression method (patch create by Daniel Borca) +* Added BZIP Compress method for zip +* Did some refactoring and code cleanup + + +Credits + + Gilles Vollant - Original MiniZip author + Even Rouault - ZIP64 unzip Support + Daniel Borca - BZip Compression method support in unzip + Mathias Svensson - ZIP64 zip support + Mathias Svensson - BZip Compression method support in zip + + Resources + + ZipLayout http://result42.com/projects/ZipFileLayout + Command line tool for Windows that shows the layout and information of the headers in a zip archive. + Used when debugging and validating the creation of zip files using MiniZip64 + + + ZIP App Note http://www.pkware.com/documents/casestudies/APPNOTE.TXT + Zip File specification + + +Notes. + * To be able to use BZip compression method in zip64.c or unzip64.c the BZIP2 lib is needed and HAVE_BZIP2 need to be defined. + +License +---------------------------------------------------------- + Condition of use and distribution are the same than zlib : + + 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. + +---------------------------------------------------------- + diff --git a/3rdparty/minizip/crypt.h b/3rdparty/minizip/crypt.h new file mode 100644 index 0000000..a01d08d --- /dev/null +++ b/3rdparty/minizip/crypt.h @@ -0,0 +1,131 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(const char* passwd, /* password string */ + unsigned char* buf, /* where to write header */ + int bufSize, + unsigned long* pkeys, + const unsigned long* pcrc_32_tab, + unsigned long crcForCrypting) +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/3rdparty/minizip/framework_minizip.h b/3rdparty/minizip/framework_minizip.h new file mode 100644 index 0000000..dd4b5c5 --- /dev/null +++ b/3rdparty/minizip/framework_minizip.h @@ -0,0 +1,8 @@ +/* + * @file framework_minizip.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the forward header file for minizip library + */ +#pragma GCC system_header +#include diff --git a/3rdparty/minizip/ioapi.c b/3rdparty/minizip/ioapi.c new file mode 100644 index 0000000..49958f6 --- /dev/null +++ b/3rdparty/minizip/ioapi.c @@ -0,0 +1,235 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#if (defined(_WIN32)) + #define _CRT_SECURE_NO_WARNINGS +#endif + +#include "ioapi.h" + +voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) +{ + if (pfilefunc->zfile_func64.zopen64_file != NULL) + return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); + else + { + return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode); + } +} + +long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); + else + { + uLong offsetTruncated = (uLong)offset; + if (offsetTruncated != offset) + return -1; + else + return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); + } +} + +ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); + else + { + uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); + if ((tell_uLong) == ((uLong)-1)) + return (ZPOS64_T)-1; + else + return tell_uLong; + } +} + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) +{ + p_filefunc64_32->zfile_func64.zopen64_file = NULL; + p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; + p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; + p_filefunc64_32->zfile_func64.ztell64_file = NULL; + p_filefunc64_32->zfile_func64.zseek64_file = NULL; + p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; + p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; + p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; +} + + + +static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode)); +static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size)); +static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream)); +static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); +static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); + +static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + +static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen64((const char*)filename, mode_fopen); + return file; +} + + +static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + + +static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret; + ret = ftello64((FILE *)stream); + return ret; +} + +static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + if (fseek((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + return ret; +} + +static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + + if(fseeko64((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + + return ret; +} + + +static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = fopen64_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell64_file = ftell64_file_func; + pzlib_filefunc_def->zseek64_file = fseek64_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/3rdparty/minizip/ioapi.h b/3rdparty/minizip/ioapi.h new file mode 100644 index 0000000..8309c4c --- /dev/null +++ b/3rdparty/minizip/ioapi.h @@ -0,0 +1,200 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + + Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) + Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. + More if/def section may be needed to support other platforms + Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. + (but you should use iowin32.c for windows instead) + +*/ + +#ifndef _ZLIBIOAPI64_H +#define _ZLIBIOAPI64_H + +#if (!defined(_WIN32)) && (!defined(WIN32)) + + // Linux needs this to support file operation on files larger then 4+GB + // But might need better if/def to select just the platforms that needs them. + + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif +#endif + +#include +#include +#include "zlib.h" + +#if defined(USE_FILE32API) +#define fopen64 fopen +#define ftello64 ftell +#define fseeko64 fseek +#else +#ifdef _MSC_VER + #define fopen64 fopen + #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) + #define ftello64 _ftelli64 + #define fseeko64 _fseeki64 + #else // old MSC + #define ftello64 ftell + #define fseeko64 fseek + #endif +#endif +#endif + +/* +#ifndef ZPOS64_T + #ifdef _WIN32 + #define ZPOS64_T fpos_t + #else + #include + #define ZPOS64_T uint64_t + #endif +#endif +*/ + +#ifdef HAVE_MINIZIP64_CONF_H +#include "mz64conf.h" +#endif + +/* a type choosen by DEFINE */ +#ifdef HAVE_64BIT_INT_CUSTOM +typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; +#else +#ifdef HAS_STDINT_H +#include "stdint.h" +typedef uint64_t ZPOS64_T; +#else + + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 ZPOS64_T; +#else +typedef unsigned long long int ZPOS64_T; +#endif +#endif +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) + #define ZCALLBACK CALLBACK + #else + #define ZCALLBACK + #endif +#endif + + + + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); + + +/* here is the "old" 32 bits structure structure */ +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + +typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); + +typedef struct zlib_filefunc64_def_s +{ + open64_file_func zopen64_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell64_file_func ztell64_file; + seek64_file_func zseek64_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc64_def; + +void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +/* now internal definition, only for zip.c and unzip.h */ +typedef struct zlib_filefunc64_32_def_s +{ + zlib_filefunc64_def zfile_func64; + open_file_func zopen32_file; + tell_file_func ztell32_file; + seek_file_func zseek32_file; +} zlib_filefunc64_32_def; + + +#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +//#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) +//#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) +#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) + +voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); +long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); +ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); + +#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) +#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) +#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rdparty/minizip/iowin32.c b/3rdparty/minizip/iowin32.c new file mode 100644 index 0000000..6a2a883 --- /dev/null +++ b/3rdparty/minizip/iowin32.c @@ -0,0 +1,389 @@ +/* iowin32.c -- IO base function header for compress/uncompress .zip + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#include + +#include "zlib.h" +#include "ioapi.h" +#include "iowin32.h" + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE (0xFFFFFFFF) +#endif + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +voidpf ZCALLBACK win32_open_file_func OF((voidpf opaque, const char* filename, int mode)); +uLong ZCALLBACK win32_read_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +uLong ZCALLBACK win32_write_file_func OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +ZPOS64_T ZCALLBACK win32_tell64_file_func OF((voidpf opaque, voidpf stream)); +long ZCALLBACK win32_seek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +int ZCALLBACK win32_close_file_func OF((voidpf opaque, voidpf stream)); +int ZCALLBACK win32_error_file_func OF((voidpf opaque, voidpf stream)); + +typedef struct +{ + HANDLE hf; + int error; +} WIN32FILE_IOWIN; + + +static void win32_translate_open_mode(int mode, + DWORD* lpdwDesiredAccess, + DWORD* lpdwCreationDisposition, + DWORD* lpdwShareMode, + DWORD* lpdwFlagsAndAttributes) +{ + *lpdwDesiredAccess = *lpdwShareMode = *lpdwFlagsAndAttributes = *lpdwCreationDisposition = 0; + + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + { + *lpdwDesiredAccess = GENERIC_READ; + *lpdwCreationDisposition = OPEN_EXISTING; + *lpdwShareMode = FILE_SHARE_READ; + } + else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + { + *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + *lpdwCreationDisposition = OPEN_EXISTING; + } + else if (mode & ZLIB_FILEFUNC_MODE_CREATE) + { + *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + *lpdwCreationDisposition = CREATE_ALWAYS; + } +} + +static voidpf win32_build_iowin(HANDLE hFile) +{ + voidpf ret=NULL; + + if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + WIN32FILE_IOWIN w32fiow; + w32fiow.hf = hFile; + w32fiow.error = 0; + ret = malloc(sizeof(WIN32FILE_IOWIN)); + + if (ret==NULL) + CloseHandle(hFile); + else + *((WIN32FILE_IOWIN*)ret) = w32fiow; + } + return ret; +} + +voidpf ZCALLBACK win32_open64_file_func (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open64_file_funcA (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFileA((LPCSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open64_file_funcW (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFileW((LPCWSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open_file_func (voidpf opaque,const char* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); + + return win32_build_iowin(hFile); +} + + +uLong ZCALLBACK win32_read_file_func (voidpf opaque, voidpf stream, void* buf,uLong size) +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + + if (hFile != NULL) + { + if (!ReadFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + } + + return ret; +} + + +uLong ZCALLBACK win32_write_file_func (voidpf opaque,voidpf stream,const void* buf,uLong size) +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + + if (hFile != NULL) + { + if (!WriteFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + } + + return ret; +} + +long ZCALLBACK win32_tell_file_func (voidpf opaque,voidpf stream) +{ + long ret=-1; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + DWORD dwSet = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); + if (dwSet == INVALID_SET_FILE_POINTER) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=(long)dwSet; + } + return ret; +} + +ZPOS64_T ZCALLBACK win32_tell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret= (ZPOS64_T)-1; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream)->hf; + + if (hFile) + { + LARGE_INTEGER li; + li.QuadPart = 0; + li.u.LowPart = SetFilePointer(hFile, li.u.LowPart, &li.u.HighPart, FILE_CURRENT); + if ( (li.LowPart == 0xFFFFFFFF) && (GetLastError() != NO_ERROR)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = (ZPOS64_T)-1; + } + else + ret=li.QuadPart; + } + return ret; +} + + +long ZCALLBACK win32_seek_file_func (voidpf opaque,voidpf stream,uLong offset,int origin) +{ + DWORD dwMoveMethod=0xFFFFFFFF; + HANDLE hFile = NULL; + + long ret=-1; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + dwMoveMethod = FILE_CURRENT; + break; + case ZLIB_FILEFUNC_SEEK_END : + dwMoveMethod = FILE_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + dwMoveMethod = FILE_BEGIN; + break; + default: return -1; + } + + if (hFile != NULL) + { + DWORD dwSet = SetFilePointer(hFile, offset, NULL, dwMoveMethod); + if (dwSet == INVALID_SET_FILE_POINTER) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=0; + } + return ret; +} + +long ZCALLBACK win32_seek64_file_func (voidpf opaque, voidpf stream,ZPOS64_T offset,int origin) +{ + DWORD dwMoveMethod=0xFFFFFFFF; + HANDLE hFile = NULL; + long ret=-1; + + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream)->hf; + + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + dwMoveMethod = FILE_CURRENT; + break; + case ZLIB_FILEFUNC_SEEK_END : + dwMoveMethod = FILE_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + dwMoveMethod = FILE_BEGIN; + break; + default: return -1; + } + + if (hFile) + { + LARGE_INTEGER* li = (LARGE_INTEGER*)&offset; + DWORD dwSet = SetFilePointer(hFile, li->u.LowPart, &li->u.HighPart, dwMoveMethod); + if (dwSet == INVALID_SET_FILE_POINTER) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=0; + } + return ret; +} + +int ZCALLBACK win32_close_file_func (voidpf opaque, voidpf stream) +{ + int ret=-1; + + if (stream!=NULL) + { + HANDLE hFile; + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + CloseHandle(hFile); + ret=0; + } + free(stream); + } + return ret; +} + +int ZCALLBACK win32_error_file_func (voidpf opaque,voidpf stream) +{ + int ret=-1; + if (stream!=NULL) + { + ret = ((WIN32FILE_IOWIN*)stream) -> error; + } + return ret; +} + +void fill_win32_filefunc (zlib_filefunc_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen_file = win32_open_file_func; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell_file = win32_tell_file_func; + pzlib_filefunc_def->zseek_file = win32_seek_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_win32_filefunc64(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_func; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + + +void fill_win32_filefunc64A(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_funcA; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + + +void fill_win32_filefunc64W(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_funcW; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/3rdparty/minizip/iowin32.h b/3rdparty/minizip/iowin32.h new file mode 100644 index 0000000..0ca0969 --- /dev/null +++ b/3rdparty/minizip/iowin32.h @@ -0,0 +1,28 @@ +/* iowin32.h -- IO base function header for compress/uncompress .zip + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); +void fill_win32_filefunc64 OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_win32_filefunc64A OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_win32_filefunc64W OF((zlib_filefunc64_def* pzlib_filefunc_def)); + +#ifdef __cplusplus +} +#endif diff --git a/3rdparty/minizip/make_vms.com b/3rdparty/minizip/make_vms.com new file mode 100644 index 0000000..9ac13a9 --- /dev/null +++ b/3rdparty/minizip/make_vms.com @@ -0,0 +1,25 @@ +$ if f$search("ioapi.h_orig") .eqs. "" then copy ioapi.h ioapi.h_orig +$ open/write zdef vmsdefs.h +$ copy sys$input: zdef +$ deck +#define unix +#define fill_zlib_filefunc64_32_def_from_filefunc32 fillzffunc64from +#define Write_Zip64EndOfCentralDirectoryLocator Write_Zip64EoDLocator +#define Write_Zip64EndOfCentralDirectoryRecord Write_Zip64EoDRecord +#define Write_EndOfCentralDirectoryRecord Write_EoDRecord +$ eod +$ close zdef +$ copy vmsdefs.h,ioapi.h_orig ioapi.h +$ cc/include=[--]/prefix=all ioapi.c +$ cc/include=[--]/prefix=all miniunz.c +$ cc/include=[--]/prefix=all unzip.c +$ cc/include=[--]/prefix=all minizip.c +$ cc/include=[--]/prefix=all zip.c +$ link miniunz,unzip,ioapi,[--]libz.olb/lib +$ link minizip,zip,ioapi,[--]libz.olb/lib +$ mcr []minizip test minizip_info.txt +$ mcr []miniunz -l test.zip +$ rename minizip_info.txt; minizip_info.txt_old +$ mcr []miniunz test.zip +$ delete test.zip;* +$exit diff --git a/3rdparty/minizip/miniunz.c b/3rdparty/minizip/miniunz.c new file mode 100644 index 0000000..9ed009f --- /dev/null +++ b/3rdparty/minizip/miniunz.c @@ -0,0 +1,648 @@ +/* + miniunz.c + Version 1.1, February 14h, 2010 + sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) +*/ + +#ifndef _WIN32 + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef unix +# include +# include +#else +# include +# include +#endif + +#include "unzip.h" + +#define CASESENSITIVITY (0) +#define WRITEBUFFERSIZE (8192) +#define MAXFILENAME (256) + +#ifdef _WIN32 +#define USEWIN32IOAPI +#include "iowin32.h" +#endif +/* + mini unzip, demo of unzip package + + usage : + Usage : miniunz [-exvlo] file.zip [file_to_extract] [-d extractdir] + + list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT + if it exists +*/ + + +/* change_file_date : change the date/time of a file + filename : the filename of the file where date/time must be modified + dosdate : the new date at the MSDos format (4 bytes) + tmu_date : the SAME new date at the tm_unz format */ +void change_file_date(filename,dosdate,tmu_date) + const char *filename; + uLong dosdate; + tm_unz tmu_date; +{ +#ifdef _WIN32 + HANDLE hFile; + FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite; + + hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE, + 0,NULL,OPEN_EXISTING,0,NULL); + GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite); + DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal); + LocalFileTimeToFileTime(&ftLocal,&ftm); + SetFileTime(hFile,&ftm,&ftLastAcc,&ftm); + CloseHandle(hFile); +#else +#ifdef unix + struct utimbuf ut; + struct tm newdate; + newdate.tm_sec = tmu_date.tm_sec; + newdate.tm_min=tmu_date.tm_min; + newdate.tm_hour=tmu_date.tm_hour; + newdate.tm_mday=tmu_date.tm_mday; + newdate.tm_mon=tmu_date.tm_mon; + if (tmu_date.tm_year > 1900) + newdate.tm_year=tmu_date.tm_year - 1900; + else + newdate.tm_year=tmu_date.tm_year ; + newdate.tm_isdst=-1; + + ut.actime=ut.modtime=mktime(&newdate); + utime(filename,&ut); +#endif +#endif +} + + +/* mymkdir and change_file_date are not 100 % portable + As I don't know well Unix, I wait feedback for the unix portion */ + +int mymkdir(dirname) + const char* dirname; +{ + int ret=0; +#ifdef _WIN32 + ret = _mkdir(dirname); +#else +#ifdef unix + ret = mkdir (dirname,0775); +#endif +#endif + return ret; +} + +int makedir (newdir) + char *newdir; +{ + char *buffer ; + char *p; + int len = (int)strlen(newdir); + + if (len <= 0) + return 0; + + buffer = (char*)malloc(len+1); + if (buffer==NULL) + { + printf("Error allocating memory\n"); + return UNZ_INTERNALERROR; + } + strcpy(buffer,newdir); + + if (buffer[len-1] == '/') { + buffer[len-1] = '\0'; + } + if (mymkdir(buffer) == 0) + { + free(buffer); + return 1; + } + + p = buffer+1; + while (1) + { + char hold; + + while(*p && *p != '\\' && *p != '/') + p++; + hold = *p; + *p = 0; + if ((mymkdir(buffer) == -1) && (errno == ENOENT)) + { + printf("couldn't create directory %s\n",buffer); + free(buffer); + return 0; + } + if (hold == 0) + break; + *p++ = hold; + } + free(buffer); + return 1; +} + +void do_banner() +{ + printf("MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant\n"); + printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n"); +} + +void do_help() +{ + printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]\n\n" \ + " -e Extract without pathname (junk paths)\n" \ + " -x Extract with pathname\n" \ + " -v list files\n" \ + " -l list files\n" \ + " -d directory to extract into\n" \ + " -o overwrite files without prompting\n" \ + " -p extract crypted file using password\n\n"); +} + +void Display64BitsSize(ZPOS64_T n, int size_char) +{ + /* to avoid compatibility problem , we do here the conversion */ + char number[21]; + int offset=19; + int pos_string = 19; + number[20]=0; + for (;;) { + number[offset]=(char)((n%10)+'0'); + if (number[offset] != '0') + pos_string=offset; + n/=10; + if (offset==0) + break; + offset--; + } + { + int size_display_string = 19-pos_string; + while (size_char > size_display_string) + { + size_char--; + printf(" "); + } + } + + printf("%s",&number[pos_string]); +} + +int do_list(uf) + unzFile uf; +{ + uLong i; + unz_global_info64 gi; + int err; + + err = unzGetGlobalInfo64(uf,&gi); + if (err!=UNZ_OK) + printf("error %d with zipfile in unzGetGlobalInfo \n",err); + printf(" Length Method Size Ratio Date Time CRC-32 Name\n"); + printf(" ------ ------ ---- ----- ---- ---- ------ ----\n"); + for (i=0;i0) + ratio = (uLong)((file_info.compressed_size*100)/file_info.uncompressed_size); + + /* display a '*' if the file is crypted */ + if ((file_info.flag & 1) != 0) + charCrypt='*'; + + if (file_info.compression_method==0) + string_method="Stored"; + else + if (file_info.compression_method==Z_DEFLATED) + { + uInt iLevel=(uInt)((file_info.flag & 0x6)/2); + if (iLevel==0) + string_method="Defl:N"; + else if (iLevel==1) + string_method="Defl:X"; + else if ((iLevel==2) || (iLevel==3)) + string_method="Defl:F"; /* 2:fast , 3 : extra fast*/ + } + else + if (file_info.compression_method==Z_BZIP2ED) + { + string_method="BZip2 "; + } + else + string_method="Unkn. "; + + Display64BitsSize(file_info.uncompressed_size,7); + printf(" %6s%c",string_method,charCrypt); + Display64BitsSize(file_info.compressed_size,7); + printf(" %3lu%% %2.2lu-%2.2lu-%2.2lu %2.2lu:%2.2lu %8.8lx %s\n", + ratio, + (uLong)file_info.tmu_date.tm_mon + 1, + (uLong)file_info.tmu_date.tm_mday, + (uLong)file_info.tmu_date.tm_year % 100, + (uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min, + (uLong)file_info.crc,filename_inzip); + if ((i+1)='a') && (rep<='z')) + rep -= 0x20; + } + while ((rep!='Y') && (rep!='N') && (rep!='A')); + } + + if (rep == 'N') + skip = 1; + + if (rep == 'A') + *popt_overwrite=1; + } + + if ((skip==0) && (err==UNZ_OK)) + { + fout=fopen64(write_filename,"wb"); + + /* some zipfile don't contain directory alone before file */ + if ((fout==NULL) && ((*popt_extract_without_path)==0) && + (filename_withoutpath!=(char*)filename_inzip)) + { + char c=*(filename_withoutpath-1); + *(filename_withoutpath-1)='\0'; + makedir(write_filename); + *(filename_withoutpath-1)=c; + fout=fopen64(write_filename,"wb"); + } + + if (fout==NULL) + { + printf("error opening %s\n",write_filename); + } + } + + if (fout!=NULL) + { + printf(" extracting: %s\n",write_filename); + + do + { + err = unzReadCurrentFile(uf,buf,size_buf); + if (err<0) + { + printf("error %d with zipfile in unzReadCurrentFile\n",err); + break; + } + if (err>0) + if (fwrite(buf,err,1,fout)!=1) + { + printf("error in writing extracted file\n"); + err=UNZ_ERRNO; + break; + } + } + while (err>0); + if (fout) + fclose(fout); + + if (err==0) + change_file_date(write_filename,file_info.dosDate, + file_info.tmu_date); + } + + if (err==UNZ_OK) + { + err = unzCloseCurrentFile (uf); + if (err!=UNZ_OK) + { + printf("error %d with zipfile in unzCloseCurrentFile\n",err); + } + } + else + unzCloseCurrentFile(uf); /* don't lose the error */ + } + + free(buf); + return err; +} + + +int do_extract(uf,opt_extract_without_path,opt_overwrite,password) + unzFile uf; + int opt_extract_without_path; + int opt_overwrite; + const char* password; +{ + uLong i; + unz_global_info64 gi; + int err; + FILE* fout=NULL; + + err = unzGetGlobalInfo64(uf,&gi); + if (err!=UNZ_OK) + printf("error %d with zipfile in unzGetGlobalInfo \n",err); + + for (i=0;i +#include +#include +#include +#include +#include + +#ifdef unix +# include +# include +# include +# include +#else +# include +# include +#endif + +#include "zip.h" + +#ifdef _WIN32 + #define USEWIN32IOAPI + #include "iowin32.h" +#endif + + + +#define WRITEBUFFERSIZE (16384) +#define MAXFILENAME (256) + +#ifdef _WIN32 +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + int ret = 0; + { + FILETIME ftLocal; + HANDLE hFind; + WIN32_FIND_DATAA ff32; + + hFind = FindFirstFileA(f,&ff32); + if (hFind != INVALID_HANDLE_VALUE) + { + FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal); + FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0); + FindClose(hFind); + ret = 1; + } + } + return ret; +} +#else +#ifdef unix +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + int ret=0; + struct stat s; /* results of stat() */ + struct tm* filedate; + time_t tm_t=0; + + if (strcmp(f,"-")!=0) + { + char name[MAXFILENAME+1]; + int len = strlen(f); + if (len > MAXFILENAME) + len = MAXFILENAME; + + strncpy(name, f,MAXFILENAME-1); + /* strncpy doesnt append the trailing NULL, of the string is too long. */ + name[ MAXFILENAME ] = '\0'; + + if (name[len - 1] == '/') + name[len - 1] = '\0'; + /* not all systems allow stat'ing a file with / appended */ + if (stat(name,&s)==0) + { + tm_t = s.st_mtime; + ret = 1; + } + } + filedate = localtime(&tm_t); + + tmzip->tm_sec = filedate->tm_sec; + tmzip->tm_min = filedate->tm_min; + tmzip->tm_hour = filedate->tm_hour; + tmzip->tm_mday = filedate->tm_mday; + tmzip->tm_mon = filedate->tm_mon ; + tmzip->tm_year = filedate->tm_year; + + return ret; +} +#else +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + return 0; +} +#endif +#endif + + + + +int check_exist_file(filename) + const char* filename; +{ + FILE* ftestexist; + int ret = 1; + ftestexist = fopen64(filename,"rb"); + if (ftestexist==NULL) + ret = 0; + else + fclose(ftestexist); + return ret; +} + +void do_banner() +{ + printf("MiniZip 1.1, demo of zLib + MiniZip64 package, written by Gilles Vollant\n"); + printf("more info on MiniZip at http://www.winimage.com/zLibDll/minizip.html\n\n"); +} + +void do_help() +{ + printf("Usage : minizip [-o] [-a] [-0 to -9] [-p password] [-j] file.zip [files_to_add]\n\n" \ + " -o Overwrite existing file.zip\n" \ + " -a Append to existing file.zip\n" \ + " -0 Store only\n" \ + " -1 Compress faster\n" \ + " -9 Compress better\n\n" \ + " -j exclude path. store only the file name.\n\n"); +} + +/* calculate the CRC32 of a file, + because to encrypt a file, we need known the CRC32 of the file before */ +int getFileCrc(const char* filenameinzip,void*buf,unsigned long size_buf,unsigned long* result_crc) +{ + unsigned long calculate_crc=0; + int err=ZIP_OK; + FILE * fin = fopen64(filenameinzip,"rb"); + unsigned long size_read = 0; + unsigned long total_read = 0; + if (fin==NULL) + { + err = ZIP_ERRNO; + } + + if (err == ZIP_OK) + do + { + err = ZIP_OK; + size_read = (int)fread(buf,1,size_buf,fin); + if (size_read < size_buf) + if (feof(fin)==0) + { + printf("error in reading %s\n",filenameinzip); + err = ZIP_ERRNO; + } + + if (size_read>0) + calculate_crc = crc32(calculate_crc,buf,size_read); + total_read += size_read; + + } while ((err == ZIP_OK) && (size_read>0)); + + if (fin) + fclose(fin); + + *result_crc=calculate_crc; + printf("file %s crc %lx\n", filenameinzip, calculate_crc); + return err; +} + +int isLargeFile(const char* filename) +{ + int largeFile = 0; + ZPOS64_T pos = 0; + FILE* pFile = fopen64(filename, "rb"); + + if(pFile != NULL) + { + int n = fseeko64(pFile, 0, SEEK_END); + + pos = ftello64(pFile); + + printf("File : %s is %lld bytes\n", filename, pos); + + if(pos >= 0xffffffff) + largeFile = 1; + + fclose(pFile); + } + + return largeFile; +} + +int main(argc,argv) + int argc; + char *argv[]; +{ + int i; + int opt_overwrite=0; + int opt_compress_level=Z_DEFAULT_COMPRESSION; + int opt_exclude_path=0; + int zipfilenamearg = 0; + char filename_try[MAXFILENAME+16]; + int zipok; + int err=0; + int size_buf=0; + void* buf=NULL; + const char* password=NULL; + + + do_banner(); + if (argc==1) + { + do_help(); + return 0; + } + else + { + for (i=1;i='0') && (c<='9')) + opt_compress_level = c-'0'; + if ((c=='j') || (c=='J')) + opt_exclude_path = 1; + + if (((c=='p') || (c=='P')) && (i+1='a') && (rep<='z')) + rep -= 0x20; + } + while ((rep!='Y') && (rep!='N') && (rep!='A')); + if (rep=='N') + zipok = 0; + if (rep=='A') + opt_overwrite = 2; + } + } + + if (zipok==1) + { + zipFile zf; + int errclose; +# ifdef USEWIN32IOAPI + zlib_filefunc64_def ffunc; + fill_win32_filefunc64A(&ffunc); + zf = zipOpen2_64(filename_try,(opt_overwrite==2) ? 2 : 0,NULL,&ffunc); +# else + zf = zipOpen64(filename_try,(opt_overwrite==2) ? 2 : 0); +# endif + + if (zf == NULL) + { + printf("error opening %s\n",filename_try); + err= ZIP_ERRNO; + } + else + printf("creating %s\n",filename_try); + + for (i=zipfilenamearg+1;(i='0') || (argv[i][1]<='9'))) && + (strlen(argv[i]) == 2))) + { + FILE * fin; + int size_read; + const char* filenameinzip = argv[i]; + const char *savefilenameinzip; + zip_fileinfo zi; + unsigned long crcFile=0; + int zip64 = 0; + + zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour = + zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0; + zi.dosDate = 0; + zi.internal_fa = 0; + zi.external_fa = 0; + filetime(filenameinzip,&zi.tmz_date,&zi.dosDate); + +/* + err = zipOpenNewFileInZip(zf,filenameinzip,&zi, + NULL,0,NULL,0,NULL / * comment * /, + (opt_compress_level != 0) ? Z_DEFLATED : 0, + opt_compress_level); +*/ + if ((password != NULL) && (err==ZIP_OK)) + err = getFileCrc(filenameinzip,buf,size_buf,&crcFile); + + zip64 = isLargeFile(filenameinzip); + + /* The path name saved, should not include a leading slash. */ + /*if it did, windows/xp and dynazip couldn't read the zip file. */ + savefilenameinzip = filenameinzip; + while( savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/' ) + { + savefilenameinzip++; + } + + /*should the zip file contain any path at all?*/ + if( opt_exclude_path ) + { + const char *tmpptr; + const char *lastslash = 0; + for( tmpptr = savefilenameinzip; *tmpptr; tmpptr++) + { + if( *tmpptr == '\\' || *tmpptr == '/') + { + lastslash = tmpptr; + } + } + if( lastslash != NULL ) + { + savefilenameinzip = lastslash+1; // base filename follows last slash. + } + } + + /**/ + err = zipOpenNewFileInZip3_64(zf,savefilenameinzip,&zi, + NULL,0,NULL,0,NULL /* comment*/, + (opt_compress_level != 0) ? Z_DEFLATED : 0, + opt_compress_level,0, + /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */ + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + password,crcFile, zip64); + + if (err != ZIP_OK) + printf("error in opening %s in zipfile\n",filenameinzip); + else + { + fin = fopen64(filenameinzip,"rb"); + if (fin==NULL) + { + err=ZIP_ERRNO; + printf("error in opening %s for reading\n",filenameinzip); + } + } + + if (err == ZIP_OK) + do + { + err = ZIP_OK; + size_read = (int)fread(buf,1,size_buf,fin); + if (size_read < size_buf) + if (feof(fin)==0) + { + printf("error in reading %s\n",filenameinzip); + err = ZIP_ERRNO; + } + + if (size_read>0) + { + err = zipWriteInFileInZip (zf,buf,size_read); + if (err<0) + { + printf("error in writing %s in the zipfile\n", + filenameinzip); + } + + } + } while ((err == ZIP_OK) && (size_read>0)); + + if (fin) + fclose(fin); + + if (err<0) + err=ZIP_ERRNO; + else + { + err = zipCloseFileInZip(zf); + if (err!=ZIP_OK) + printf("error in closing %s in the zipfile\n", + filenameinzip); + } + } + } + errclose = zipClose(zf,NULL); + if (errclose != ZIP_OK) + printf("error in closing %s\n",filename_try); + } + else + { + do_help(); + } + + free(buf); + return 0; +} diff --git a/3rdparty/minizip/mztools.c b/3rdparty/minizip/mztools.c new file mode 100644 index 0000000..f9092e6 --- /dev/null +++ b/3rdparty/minizip/mztools.c @@ -0,0 +1,281 @@ +/* + Additional tools for Minizip + Code: Xavier Roche '2004 + License: Same as ZLIB (www.gzip.org) +*/ + +/* Code */ +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#define READ_8(adr) ((unsigned char)*(adr)) +#define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) ) +#define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) ) + +#define WRITE_8(buff, n) do { \ + *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \ +} while(0) +#define WRITE_16(buff, n) do { \ + WRITE_8((unsigned char*)(buff), n); \ + WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \ +} while(0) +#define WRITE_32(buff, n) do { \ + WRITE_16((unsigned char*)(buff), (n) & 0xffff); \ + WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \ +} while(0) + +extern int ZEXPORT unzRepair(file, fileOut, fileOutTmp, nRecovered, bytesRecovered) +const char* file; +const char* fileOut; +const char* fileOutTmp; +uLong* nRecovered; +uLong* bytesRecovered; +{ + int err = Z_OK; + FILE* fpZip = fopen(file, "rb"); + FILE* fpOut = fopen(fileOut, "wb"); + FILE* fpOutCD = fopen(fileOutTmp, "wb"); + if (fpZip != NULL && fpOut != NULL) { + int entries = 0; + uLong totalBytes = 0; + char header[30]; + char filename[256]; + char extra[1024]; + int offset = 0; + int offsetCD = 0; + while ( fread(header, 1, 30, fpZip) == 30 ) { + int currentOffset = offset; + + /* File entry */ + if (READ_32(header) == 0x04034b50) { + unsigned int version = READ_16(header + 4); + unsigned int gpflag = READ_16(header + 6); + unsigned int method = READ_16(header + 8); + unsigned int filetime = READ_16(header + 10); + unsigned int filedate = READ_16(header + 12); + unsigned int crc = READ_32(header + 14); /* crc */ + unsigned int cpsize = READ_32(header + 18); /* compressed size */ + unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */ + unsigned int fnsize = READ_16(header + 26); /* file name length */ + unsigned int extsize = READ_16(header + 28); /* extra field length */ + filename[0] = extra[0] = '\0'; + + /* Header */ + if (fwrite(header, 1, 30, fpOut) == 30) { + offset += 30; + } else { + err = Z_ERRNO; + break; + } + + /* Filename */ + if (fnsize > 0) { + if (fread(filename, 1, fnsize, fpZip) == fnsize) { + if (fwrite(filename, 1, fnsize, fpOut) == fnsize) { + offset += fnsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_STREAM_ERROR; + break; + } + + /* Extra field */ + if (extsize > 0) { + if (fread(extra, 1, extsize, fpZip) == extsize) { + if (fwrite(extra, 1, extsize, fpOut) == extsize) { + offset += extsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } + + /* Data */ + { + int dataSize = cpsize; + if (dataSize == 0) { + dataSize = uncpsize; + } + if (dataSize > 0) { + char* data = malloc(dataSize); + if (data != NULL) { + if ((int)fread(data, 1, dataSize, fpZip) == dataSize) { + if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) { + offset += dataSize; + totalBytes += dataSize; + } else { + err = Z_ERRNO; + } + } else { + err = Z_ERRNO; + } + free(data); + if (err != Z_OK) { + break; + } + } else { + err = Z_MEM_ERROR; + break; + } + } + } + + /* Central directory entry */ + { + char header[46]; + char* comment = ""; + int comsize = (int) strlen(comment); + WRITE_32(header, 0x02014b50); + WRITE_16(header + 4, version); + WRITE_16(header + 6, version); + WRITE_16(header + 8, gpflag); + WRITE_16(header + 10, method); + WRITE_16(header + 12, filetime); + WRITE_16(header + 14, filedate); + WRITE_32(header + 16, crc); + WRITE_32(header + 20, cpsize); + WRITE_32(header + 24, uncpsize); + WRITE_16(header + 28, fnsize); + WRITE_16(header + 30, extsize); + WRITE_16(header + 32, comsize); + WRITE_16(header + 34, 0); /* disk # */ + WRITE_16(header + 36, 0); /* int attrb */ + WRITE_32(header + 38, 0); /* ext attrb */ + WRITE_32(header + 42, currentOffset); + /* Header */ + if (fwrite(header, 1, 46, fpOutCD) == 46) { + offsetCD += 46; + + /* Filename */ + if (fnsize > 0) { + if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) { + offsetCD += fnsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_STREAM_ERROR; + break; + } + + /* Extra field */ + if (extsize > 0) { + if (fwrite(extra, 1, extsize, fpOutCD) == extsize) { + offsetCD += extsize; + } else { + err = Z_ERRNO; + break; + } + } + + /* Comment field */ + if (comsize > 0) { + if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) { + offsetCD += comsize; + } else { + err = Z_ERRNO; + break; + } + } + + + } else { + err = Z_ERRNO; + break; + } + } + + /* Success */ + entries++; + + } else { + break; + } + } + + /* Final central directory */ + { + int entriesZip = entries; + char header[22]; + char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools"; + int comsize = (int) strlen(comment); + if (entriesZip > 0xffff) { + entriesZip = 0xffff; + } + WRITE_32(header, 0x06054b50); + WRITE_16(header + 4, 0); /* disk # */ + WRITE_16(header + 6, 0); /* disk # */ + WRITE_16(header + 8, entriesZip); /* hack */ + WRITE_16(header + 10, entriesZip); /* hack */ + WRITE_32(header + 12, offsetCD); /* size of CD */ + WRITE_32(header + 16, offset); /* offset to CD */ + WRITE_16(header + 20, comsize); /* comment */ + + /* Header */ + if (fwrite(header, 1, 22, fpOutCD) == 22) { + + /* Comment field */ + if (comsize > 0) { + if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) { + err = Z_ERRNO; + } + } + + } else { + err = Z_ERRNO; + } + } + + /* Final merge (file + central directory) */ + fclose(fpOutCD); + if (err == Z_OK) { + fpOutCD = fopen(fileOutTmp, "rb"); + if (fpOutCD != NULL) { + int nRead; + char buffer[8192]; + while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) { + if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) { + err = Z_ERRNO; + break; + } + } + fclose(fpOutCD); + } + } + + /* Close */ + fclose(fpZip); + fclose(fpOut); + + /* Wipe temporary file */ + (void)remove(fileOutTmp); + + /* Number of recovered entries */ + if (err == Z_OK) { + if (nRecovered != NULL) { + *nRecovered = entries; + } + if (bytesRecovered != NULL) { + *bytesRecovered = totalBytes; + } + } + } else { + err = Z_STREAM_ERROR; + } + return err; +} diff --git a/3rdparty/minizip/mztools.h b/3rdparty/minizip/mztools.h new file mode 100644 index 0000000..88b3459 --- /dev/null +++ b/3rdparty/minizip/mztools.h @@ -0,0 +1,31 @@ +/* + Additional tools for Minizip + Code: Xavier Roche '2004 + License: Same as ZLIB (www.gzip.org) +*/ + +#ifndef _zip_tools_H +#define _zip_tools_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#include "unzip.h" + +/* Repair a ZIP file (missing central directory) + file: file to recover + fileOut: output file after recovery + fileOutTmp: temporary file name used for recovery +*/ +extern int ZEXPORT unzRepair(const char* file, + const char* fileOut, + const char* fileOutTmp, + uLong* nRecovered, + uLong* bytesRecovered); + +#endif diff --git a/3rdparty/minizip/unzip.c b/3rdparty/minizip/unzip.c new file mode 100644 index 0000000..7617f41 --- /dev/null +++ b/3rdparty/minizip/unzip.c @@ -0,0 +1,2125 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + + ------------------------------------------------------------------------------------ + Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of + compatibility with older software. The following is from the original crypt.c. + Code woven in by Terry Thorsen 1/2003. + + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html + + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + ------------------------------------------------------------------------------------ + + Changes in unzip.c + + 2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos + 2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* + 2007-2008 - Even Rouault - Remove old C style function prototypes + 2007-2008 - Even Rouault - Add unzip support for ZIP64 + + Copyright (C) 2007-2008 Even Rouault + + + Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). + Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G + should only read the compressed/uncompressed size from the Zip64 format if + the size from normal header was 0xFFFFFFFF + Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant + Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required) + Patch created by Daniel Borca + + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + + Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson + +*/ + + +#include +#include +#include + +#ifndef NOUNCRYPT + #define NOUNCRYPT +#endif + +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info64_internal_s +{ + ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ +} unz_file_info64_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ + ZPOS64_T total_out_64; + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ + ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip64_read_info_s; + + +/* unz64_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + int is64bitOpenFunction; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info64 gi; /* public global information */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + ZPOS64_T num_file; /* number of the current file in the zipfile*/ + ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ + ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ + ZPOS64_T central_pos; /* position of the beginning of the central dir*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info64 cur_file_info; /* public info about the current file in zip*/ + unz_file_info64_internal cur_file_info_internal; /* private info about it*/ + file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; + + int isZip64; + +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz64_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + 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 unz64local_getByte OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unz64local_getShort OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX)); + + +local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX) +{ + ZPOS64_T x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<24; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<32; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<40; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<48; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<56; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, + const char* fileName2, + int iCaseSensitivity) + +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); +local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + + +/* + Locate the Central directory 64 of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream)); + +local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) + return 0; + + /* total number of disks */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + if (uL != 0x06064b50) + return 0; + + return relativeOffset; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +local unzFile unzOpenInternal (const void *path, + zlib_filefunc64_32_def* pzlib_filefunc64_32_def, + int is64bitOpenFunction) +{ + unz64_s us; + unz64_s *s; + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + us.z_filefunc.zseek32_file = NULL; + us.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&us.z_filefunc.zfile_func64); + else + us.z_filefunc = *pzlib_filefunc64_32_def; + us.is64bitOpenFunction = is64bitOpenFunction; + + + + us.filestream = ZOPEN64(us.z_filefunc, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); + if (central_pos) + { + uLong uS; + ZPOS64_T uL64; + + us.isZip64 = 1; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* size of zip64 end of central directory record */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version made by */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version needed to extract */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + us.gi.size_comment = 0; + } + else + { + central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + us.isZip64 = 0; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.gi.number_entry = uL; + + /* total number of entries in the central dir */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + number_entry_CD = uL; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.size_central_dir = uL; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.offset_central_dir = uL; + + /* zipfile comment length */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + } + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE64(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + /* to do : check if number_entry is not truncated */ + pglobal_info32->number_entry = (uLong)s->gi.number_entry; + pglobal_info32->size_comment = s->gi.size_comment; + return UNZ_OK; +} +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm) +{ + ZPOS64_T uDate; + uDate = (ZPOS64_T)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unz64local_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unz64local_GetCurrentFileInfoInternal (unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz64_s* s; + unz_file_info64 file_info; + unz_file_info64_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + uLong uL; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.compressed_size = uL; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.uncompressed_size = uL; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + // relative offset of local header + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info_internal.offset_curfile = uL; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + // Read extrafield + if ((err==UNZ_OK) && (extraField!=NULL)) + { + ZPOS64_T uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + + lSeek += file_info.size_file_extra - (uLong)uSizeRead; + } + else + lSeek += file_info.size_file_extra; + + + if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) + { + uLong acc = 0; + + // since lSeek now points to after the extra field we need to move back + lSeek -= file_info.size_file_extra; + + if (lSeek!=0) + { + if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + while(acc < file_info.size_file_extra) + { + uLong headerId; + uLong dataSize; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) + err=UNZ_ERRNO; + + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { + uLong uL; + + if(file_info.uncompressed_size == (ZPOS64_T)(unsigned long)-1) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.compressed_size == (ZPOS64_T)(unsigned long)-1) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info_internal.offset_curfile == (ZPOS64_T)(unsigned long)-1) + { + /* Relative Header offset */ + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.disk_num_start == (unsigned long)-1) + { + /* Disk Start Number */ + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + } + + } + else + { + if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + + acc += 2 + 2 + dataSize; + } + } + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file, + unz_file_info64 * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + int err; + unz_file_info64 file_info64; + err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); + if (err==UNZ_OK) + { + pfile_info->version = file_info64.version; + pfile_info->version_needed = file_info64.version_needed; + pfile_info->flag = file_info64.flag; + pfile_info->compression_method = file_info64.compression_method; + pfile_info->dosDate = file_info64.dosDate; + pfile_info->crc = file_info64.crc; + + pfile_info->size_filename = file_info64.size_filename; + pfile_info->size_file_extra = file_info64.size_file_extra; + pfile_info->size_file_comment = file_info64.size_file_comment; + + pfile_info->disk_num_start = file_info64.disk_num_start; + pfile_info->internal_fa = file_info64.internal_fa; + pfile_info->external_fa = file_info64.external_fa; + + pfile_info->tmu_date = file_info64.tmu_date, + + + pfile_info->compressed_size = (uLong)file_info64.compressed_size; + pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; + + } + return err; +} +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz64_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info64 cur_file_infoSaved; + unz_file_info64_internal cur_file_info_internalSaved; + ZPOS64_T num_fileSaved; + ZPOS64_T pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo64(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; // offset in file + ZPOS64_T num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) +{ + unz64_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + int err = unzGetFilePos64(file,&file_pos64); + if (err==UNZ_OK) + { + file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; + file_pos->num_of_file = (uLong)file_pos64.num_of_file; + } + return err; +} + +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) +{ + unz64_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + if (file_pos == NULL) + return UNZ_PARAMERROR; + + file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; + file_pos64.num_of_file = file_pos->num_of_file; + return unzGoToFilePos64(file,&file_pos64); +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar, + ZPOS64_T * poffset_local_extrafield, + uInt * psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, + int* level, int raw, const char* password) +{ + int err=UNZ_OK; + uInt iSizeVar; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + ZPOS64_T offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->total_out_64=0; + pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) + { +#ifdef HAVE_BZIP2 + pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; + pfile_in_zip_read_info->bstream.bzfree = (free_func)0; + pfile_in_zip_read_info->bstream.opaque = (voidpf)0; + pfile_in_zip_read_info->bstream.state = (voidpf)0; + + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } +#else + pfile_in_zip_read_info->raw=1; +#endif + } + else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = 0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* 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. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + s->encrypted = 0; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (unzFile file) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char* password) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + s=(unz64_s*)file; + if (file==NULL) + return 0; //UNZ_PARAMERROR; + pfile_in_zip_read_info=s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) + return 0; //UNZ_PARAMERROR; + return pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile; +} + +/** Addition for GDAL : END */ + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + + pfile_in_zip_read_info->bstream.next_in = (char*)pfile_in_zip_read_info->stream.next_in; + pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; + pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; + pfile_in_zip_read_info->bstream.total_in_hi32 = 0; + pfile_in_zip_read_info->bstream.next_out = (char*)pfile_in_zip_read_info->stream.next_out; + pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; + pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; + pfile_in_zip_read_info->bstream.total_out_hi32 = 0; + + uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; + bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out; + + err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); + + uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->bstream.next_in; + pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; + pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; + pfile_in_zip_read_info->stream.next_out = (Bytef*)pfile_in_zip_read_info->bstream.next_out; + pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; + pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; + + if (err==BZ_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=BZ_OK) + break; +#endif + } // end Z_BZIP2ED + else + { + ZPOS64_T uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + ZPOS64_T uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + +extern ZPOS64_T ZEXPORT unztell64 (unzFile file) +{ + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return (ZPOS64_T)-1; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return (ZPOS64_T)-1; + + return pfile_in_zip_read_info->total_out_64; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* +Read extra field from the current file (opened by unzOpenCurrentFile) +This is the local-header version of the extra field (sometimes, there is +more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + uInt read_now; + ZPOS64_T size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + inflateEnd(&pfile_in_zip_read_info->stream); +#ifdef HAVE_BZIP2 + else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) + BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); +#endif + + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) +{ + unz64_s* s; + uLong uReadThis ; + if (file==NULL) + return (int)UNZ_PARAMERROR; + s=(unz64_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) +{ + unz64_s* s; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern uLong ZEXPORT unzGetOffset (unzFile file) +{ + ZPOS64_T offset64; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + offset64 = unzGetOffset64(file); + return (uLong)offset64; +} + +extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) +{ + return unzSetOffset64(file,pos); +} diff --git a/3rdparty/minizip/unzip.h b/3rdparty/minizip/unzip.h new file mode 100644 index 0000000..3183968 --- /dev/null +++ b/3rdparty/minizip/unzip.h @@ -0,0 +1,437 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + 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. + + --------------------------------------------------------------------------------- + + Changes + + See header of unzip64.c + +*/ + +#ifndef _unz64_H +#define _unz64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info64_s +{ + ZPOS64_T number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info64; + +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info64_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + ZPOS64_T compressed_size; /* compressed size 8 bytes */ + ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info64; + +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +extern unzFile ZEXPORT unzOpen64 OF((const void *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. + the "64" function take a const void* pointer, because the path is just the + value passed to the open64_file_func callback. + Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path + is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* + does not describe the reality +*/ + + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, + zlib_filefunc64_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unz64Open, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); + +extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, + unz_global_info64 *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +typedef struct unz64_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ + ZPOS64_T num_of_file; /* # of file */ +} unz64_file_pos; + +extern int ZEXPORT unzGetFilePos64( + unzFile file, + unz64_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos64( + unzFile file, + const unz64_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, + unz_file_info64 *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); + +/** Addition for GDAL : END */ + + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); + +extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz64_H */ diff --git a/3rdparty/minizip/unzip11.zip b/3rdparty/minizip/unzip11.zip new file mode 100644 index 0000000000000000000000000000000000000000..fe55dc355c08fdf14ce9c9cd89fdc32927130889 GIT binary patch literal 51431 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 literal 0 HcmV?d00001 diff --git a/3rdparty/minizip/zip.c b/3rdparty/minizip/zip.c new file mode 100644 index 0000000..3c34fc8 --- /dev/null +++ b/3rdparty/minizip/zip.c @@ -0,0 +1,2004 @@ +/* zip.c -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + Oct-2009 - Mathias Svensson - Remove old C style function prototypes + Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives + Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. + Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data + It is used when recreting zip archive with RAW when deleting items from a zip. + ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed. + Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + +*/ + + +#include +#include +#include +#include +#include "zlib.h" +#include "zip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform depedent */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (64*1024) //(16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + + +// NOT sure that this work on ALL platform +#define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32)) + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif +const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) +#define ZIP64ENDHEADERMAGIC (0x6064b50) +#define ZIP64ENDLOCHEADERMAGIC (0x7064b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignement */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + ZPOS64_T pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralExtra; + uLong size_centralheader; /* size of the central header for cur file */ + uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; + int zip64; /* Add ZIP64 extened information in the extra field */ + ZPOS64_T pos_zip64extrainfo; + ZPOS64_T totalCompressedData; + ZPOS64_T totalUncompressedData; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile64_info; + +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile64_info ci; /* info on the file curretly writing */ + + ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ + ZPOS64_T add_position_when_writting_offset; + ZPOS64_T number_entry; + +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif + +} zip64_internal; + + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(linkedlist_datablock_internal* ldi) +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(linkedlist_data* ll) +{ + ll->first_block = ll->last_block = NULL; +} + +local void free_linkedlist(linkedlist_data* ll) +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} + + +local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;ifilled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + + + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== + Inputs a long in LSB order to the given file + nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) +*/ + +local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)); +local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) +{ + unsigned char buf[8]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte)); +local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + + +local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) +{ + uLong year = (uLong)ptm->tm_year; + if (year>=1980) + year-=1980; + else if (year>=80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + + +/****************************************************************************/ + +local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); + +local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); + + +local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) +{ + ZPOS64_T x; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<24; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<32; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<40; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<48; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<56; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* +Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before +the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + { + // Signature "0x07064b50" Zip64 end of central directory locater + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + } + + if (uPosFound!=0) + break; + } + + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK) + return 0; + + /* total number of disks */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto Zip64 end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + if (uL != 0x06064b50) // signature of 'Zip64 end of central directory' + return 0; + + return relativeOffset; +} + +int LoadCentralDirectoryRecord(zip64_internal* pziinit) +{ + int err=ZIP_OK; + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory */ + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry; + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong VersionMadeBy; + uLong VersionNeeded; + uLong size_comment; + + int hasZIP64Record = 0; + + // check first if we find a ZIP64 record + central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream); + if(central_pos > 0) + { + hasZIP64Record = 1; + } + else if(central_pos == 0) + { + central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream); + } + +/* disable to allow appending to empty ZIP archive + if (central_pos==0) + err=ZIP_ERRNO; +*/ + + if(hasZIP64Record) + { + ZPOS64_T sizeEndOfCentralDirectory; + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* size of zip64 end of central directory record */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version made by */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version needed to extract */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + // TODO.. + // read the comment from the standard central header. + size_comment = 0; + } + else + { + // Read End of central Directory info + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + number_entry = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry = uL; + + /* total number of entries in the central dir */ + number_entry_CD = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry_CD = uL; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + size_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + size_central_dir = uL; + + /* offset of start of central directory with respect to the starting disk number */ + offset_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + offset_central_dir = uL; + + + /* zipfile global comment length */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + } + + if ((central_posz_filefunc, pziinit->filestream); + return ZIP_ERRNO; + } + + if (size_comment>0) + { + pziinit->globalcomment = (char*)ALLOC(size_comment+1); + if (pziinit->globalcomment) + { + size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment); + pziinit->globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); + pziinit->add_position_when_writting_offset = byte_before_the_zipfile; + + { + ZPOS64_T size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + ZPOS64_T read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + + if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this); + + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + pziinit->begin_pos = byte_before_the_zipfile; + pziinit->number_entry = number_entry_CD; + + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + return err; +} + + +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + + +/************************************************************/ +extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) +{ + zip64_internal ziinit; + zip64_internal* zi; + int err=ZIP_OK; + + ziinit.z_filefunc.zseek32_file = NULL; + ziinit.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64); + else + ziinit.z_filefunc = *pzlib_filefunc64_32_def; + + ziinit.filestream = ZOPEN64(ziinit.z_filefunc, + pathname, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + + if (append == APPEND_STATUS_CREATEAFTER) + ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); + + ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writting_offset = 0; + init_linkedlist(&(ziinit.central_dir)); + + + + zi = (zip64_internal*)ALLOC(sizeof(zip64_internal)); + if (zi==NULL) + { + ZCLOSE64(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + // Read and Cache Central Directory Records + err = LoadCentralDirectoryRecord(&ziinit); + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + zlib_filefunc64_32_def_fill.ztell32_file = NULL; + zlib_filefunc64_32_def_fill.zseek32_file = NULL; + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + + + +extern zipFile ZEXPORT zipOpen (const char* pathname, int append) +{ + return zipOpen3((const void*)pathname,append,NULL,NULL); +} + +extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append) +{ + return zipOpen3(pathname,append,NULL,NULL); +} + +int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) +{ + /* write the local header */ + int err; + uInt size_filename = (uInt)strlen(filename); + uInt size_extrafield = size_extrafield_local; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4); + + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + // CRC / Compressed size / Uncompressed size will be filled in later and rewritten later + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + } + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if(zi->ci.zip64) + { + size_extrafield += 20; + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2); + + if ((err==ZIP_OK) && (size_filename > 0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + } + + if ((err==ZIP_OK) && (size_extrafield_local > 0)) + { + if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local) + err = ZIP_ERRNO; + } + + + if ((err==ZIP_OK) && (zi->ci.zip64)) + { + // write the Zip64 extended info + short HeaderID = 1; + short DataSize = 16; + ZPOS64_T CompressedSize = 0; + ZPOS64_T UncompressedSize = 0; + + // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) + zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); + } + + return err; +} + +/* + NOTE. + When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped + before calling this function it can be done with zipRemoveExtraInfoBlock + + It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize + unnecessary allocations. + */ +extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase, int zip64) +{ + zip64_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + +# ifdef NOCRYPT + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + +#ifdef HAVE_BZIP2 + if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED)) + return ZIP_PARAMERROR; +#else + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; +#endif + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else + zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date); + } + + zi->ci.flag = flagBase; + if ((level==8) || (level==9)) + zi->ci.flag |= 2; + if ((level==2)) + zi->ci.flag |= 4; + if ((level==1)) + zi->ci.flag |= 6; + if (password != NULL) + zi->ci.flag |= 1; + + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream); + + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment; + zi->ci.size_centralExtraFree = 32; // Extra space we have reserved in case we need to add ZIP64 extra info data + + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree); + + zi->ci.size_centralExtra = size_extrafield_global; + zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); + zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + if(zi->ci.pos_local_header >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4); + + for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + zi->ci.zip64 = zip64; + zi->ci.totalCompressedData = 0; + zi->ci.totalUncompressedData = 0; + zi->ci.pos_zip64extrainfo = 0; + + err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local); + +#ifdef HAVE_BZIP2 + zi->ci.bstream.avail_in = (uInt)0; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + zi->ci.bstream.total_in_hi32 = 0; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_out_hi32 = 0; + zi->ci.bstream.total_out_lo32 = 0; +#endif + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + zi->ci.stream.data_type = Z_BINARY; + +#ifdef HAVE_BZIP2 + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) +#else + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) +#endif + { + if(zi->ci.method == Z_DEFLATED) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = Z_DEFLATED; + } + else if(zi->ci.method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + // Init BZip stuff here + zi->ci.bstream.bzalloc = 0; + zi->ci.bstream.bzfree = 0; + zi->ci.bstream.opaque = (voidpf)0; + + err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35); + if(err == BZ_OK) + zi->ci.stream_initialised = Z_BZIP2ED; +#endif + } + + } + +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, versionMadeBy, flagBase, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +local int zip64FlushWriteBuffer(zip64_internal* zi) +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;ici.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t); +#endif + } + + if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + + zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data; + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED) + { + zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_in_hi32 = 0; + } + else +#endif + { + zi->ci.totalUncompressedData += zi->ci.stream.total_in; + zi->ci.stream.total_in = 0; + } + + + zi->ci.pos_in_buffered_data = 0; + + return err; +} + +extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len) +{ + zip64_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw)) + { + zi->ci.bstream.next_in = (void*)buf; + zi->ci.bstream.avail_in = len; + err = BZ_RUN_OK; + + while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0)) + { + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + + + if(err != BZ_RUN_OK) + break; + + if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32; +// uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_RUN); + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ; + } + } + + if(err == BZ_RUN_OK) + err = ZIP_OK; + } + else +#endif + { + zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.avail_in = len; + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + if(uTotalOutBefore > zi->ci.stream.total_out) + { + int bBreak = 0; + bBreak++; + } + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + + for (i = 0; i < copy_this; i++) + *(((char*)zi->ci.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + }// while(...) + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32) +{ + return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); +} + +extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32) +{ + zip64_internal* zi; + ZPOS64_T compressed_size; + uLong invalidValue = 0xffffffff; + short datasize = 0; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + } + else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { +#ifdef HAVE_BZIP2 + err = BZ_FINISH_OK; + while (err==BZ_FINISH_OK) + { + uLong uTotalOutBefore; + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.bstream.total_out_lo32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH); + if(err == BZ_STREAM_END) + err = Z_STREAM_END; + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore); + } + + if(err == BZ_FINISH_OK) + err = ZIP_OK; +#endif + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + { + if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + } + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + int tmp_err = deflateEnd(&zi->ci.stream); + if (err == ZIP_OK) + err = tmp_err; + zi->ci.stream_initialised = 0; + } +#ifdef HAVE_BZIP2 + else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream); + if (err==ZIP_OK) + err = tmperr; + zi->ci.stream_initialised = 0; + } +#endif + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = zi->ci.totalUncompressedData; + } + compressed_size = zi->ci.totalCompressedData; + +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + // update Current Item crc and sizes, + if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff) + { + /*version Made by*/ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2); + /*version needed*/ + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2); + + } + + zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + + + if(compressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/ + + /// set internal file attributes field + if (zi->ci.stream.data_type == Z_ASCII) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + + if(uncompressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/ + + // Add ZIP64 extra info field for uncompressed size + if(uncompressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for compressed size + if(compressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for relative offset to local file header of current file + if(zi->ci.pos_local_header >= 0xffffffff) + datasize += 8; + + if(datasize > 0) + { + char* p = NULL; + + if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) + { + // we can not write more data to the buffer that we have room for. + return ZIP_BADZIPFILE; + } + + p = zi->ci.central_header + zi->ci.size_centralheader; + + // Add Extra Information Header for 'ZIP64 information' + zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID + p += 2; + zip64local_putValue_inmemory(p, datasize, 2); // DataSize + p += 2; + + if(uncompressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, uncompressed_size, 8); + p += 8; + } + + if(compressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, compressed_size, 8); + p += 8; + } + + if(zi->ci.pos_local_header >= 0xffffffff) + { + zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8); + p += 8; + } + + // Update how much extra free space we got in the memory buffer + // and increase the centralheader size so the new ZIP64 fields are included + // ( 4 below is the size of HeaderID and DataSize field ) + zi->ci.size_centralExtraFree -= datasize + 4; + zi->ci.size_centralheader += datasize + 4; + + // Update the extra info size field + zi->ci.size_centralExtra += datasize + 4; + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2); + } + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader); + + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + // Update the LocalFileHeader with the new values. + + ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if(uncompressed_size >= 0xffffffff) + { + if(zi->ci.pos_zip64extrainfo > 0) + { + // Update the size in the ZIP64 extended field. + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8); + } + } + else + { + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + } + + if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZip (zipFile file) +{ + return zipCloseFileInZipRaw (file,0,0); +} + +int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) +{ + int err = ZIP_OK; + ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); + + /*num disks*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + /*relative offset*/ + if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8); + + /*total disks*/ /* Do not support spawning of disk so always say 1 here*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4); + + return err; +} + +int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + uLong Zip64DataSize = 44; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); // why ZPOS64_T of this ? + + if (err==ZIP_OK) /* version made by */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* version needed */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); + } + return err; +} +int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + /*signature*/ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + { + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + } + + if (err==ZIP_OK) /* total number of entries in the central dir */ + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff) + { + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); + } + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + } + + return err; +} + +int Write_GlobalComment(zip64_internal* zi, const char* global_comment) +{ + int err = ZIP_OK; + uInt size_global_comment = 0; + + if(global_comment != NULL) + size_global_comment = (uInt)strlen(global_comment); + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if (err == ZIP_OK && size_global_comment > 0) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + } + return err; +} + +extern int ZEXPORT zipClose (zipFile file, const char* global_comment) +{ + zip64_internal* zi; + int err = 0; + uLong size_centraldir = 0; + ZPOS64_T centraldir_pos_inzip; + ZPOS64_T pos; + + if (file == NULL) + return ZIP_PARAMERROR; + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + + centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block) + err = ZIP_ERRNO; + } + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_linkedlist(&(zi->central_dir)); + + pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff) + { + ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); + Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos); + } + + if (err==ZIP_OK) + err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + if(err == ZIP_OK) + err = Write_GlobalComment(zi, global_comment); + + if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} + +extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader) +{ + char* p = pData; + int size = 0; + char* pNewHeader; + char* pTmp; + short header; + short dataSize; + + int retVal = ZIP_OK; + + if(pData == NULL || *dataLen < 4) + return ZIP_PARAMERROR; + + pNewHeader = (char*)ALLOC(*dataLen); + pTmp = pNewHeader; + + while(p < (pData + *dataLen)) + { + header = *(short*)p; + dataSize = *(((short*)p)+1); + + if( header == sHeader ) // Header found. + { + p += dataSize + 4; // skip it. do not copy to temp buffer + } + else + { + // Extra Info block should not be removed, So copy it to the temp buffer. + memcpy(pTmp, p, dataSize + 4); + p += dataSize + 4; + size += dataSize + 4; + } + + } + + if(size < *dataLen) + { + // clean old extra info block. + memset(pData,0, *dataLen); + + // copy the new extra info block over the old + if(size > 0) + memcpy(pData, pNewHeader, size); + + // set the new extra info size + *dataLen = size; + + retVal = ZIP_OK; + } + else + retVal = ZIP_ERRNO; + + TRYFREE(pNewHeader); + + return retVal; +} diff --git a/3rdparty/minizip/zip.h b/3rdparty/minizip/zip.h new file mode 100644 index 0000000..8aaebb6 --- /dev/null +++ b/3rdparty/minizip/zip.h @@ -0,0 +1,362 @@ +/* zip.h -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + 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. + + --------------------------------------------------------------------------- + + Changes + + See header of zip.h + +*/ + +#ifndef _zip12_H +#define _zip12_H + +#ifdef __cplusplus +extern "C" { +#endif + +//#define HAVE_BZIP2 + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); +extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); +/* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc64_def* pzlib_filefunc_def)); + +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); + +extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int zip64)); + +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) + zip64 is set to 1 if a zip64 extended information block should be added to the local file header. + this MUST be '1' if the uncompressed size is >= 0xffffffff. + +*/ + + +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + + +extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int zip64)); +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting)); + +extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + int zip64 + )); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCrypting : crc of file to compress (needed for crypting) + */ + +extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase + )); + + +extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase, + int zip64 + )); +/* + Same than zipOpenNewFileInZip4, except + versionMadeBy : value for Version made by field + flag : value for flag field (compression level info will be added) + */ + + +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); + +extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, + ZPOS64_T uncompressed_size, + uLong crc32)); + +/* + Close the current file in the zipfile, for file opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + + +extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); +/* + zipRemoveExtraInfoBlock - Added by Mathias Svensson + + Remove extra information block from a extra information data for the local file header or central directory header + + It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. + + 0x0001 is the signature header for the ZIP64 extra information blocks + + usage. + Remove ZIP64 Extra information from a central director extra field data + zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); + + Remove ZIP64 Extra information from a Local File Header extra field data + zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip64_H */ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..42a1e50 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,203 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# + +# +# Check minimum CMake version +# +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +# +# Project name +# +PROJECT(dpl) + +# +# Logs +# + + +OPTION(DPL_LOG "DPL logs status" OFF) + +IF(DPL_LOG) + MESSAGE(STATUS "Logging enabled for DPL") + ADD_DEFINITIONS("-DDPL_LOGS_ENABLED") +ELSE(DPL_LOG) + MESSAGE(STATUS "Logging disabled for DPL") +ENDIF(DPL_LOG) +ADD_DEFINITIONS("-DSEPARATED_SINGLETON_IMPLEMENTATION") + +# Gtk +OPTION(DISABLE_GTK "Disable GTK stuff" ON) + +# +# Build type +# + +SET(CMAKE_BUILD_TYPE "Release") + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") +SET(CMAKE_CXX_FLAGS "-O2 -g -fPIC -D_FORTIFY_SOURCE=0") + +# +# CMake settings +# +MESSAGE(STATUS "========================================") +MESSAGE(STATUS "CMAKE_BINARY_DIR: " ${CMAKE_BINARY_DIR}) +MESSAGE(STATUS "CMAKE_CURRENT_BINARY_DIR: " ${CMAKE_CURRENT_BINARY_DIR}) +MESSAGE(STATUS "CMAKE_SOURCE_DIR: " ${CMAKE_SOURCE_DIR}) +MESSAGE(STATUS "CMAKE_CURRENT_SOURCE_DIR: " ${CMAKE_CURRENT_SOURCE_DIR}) +MESSAGE(STATUS "PROJECT_BINARY_DIR: " ${PROJECT_BINARY_DIR}) +MESSAGE(STATUS "PROJECT_SOURCE_DIR: " ${PROJECT_SOURCE_DIR}) +MESSAGE(STATUS "EXECUTABLE_OUTPUT_PATH: " ${EXECUTABLE_OUTPUT_PATH}) +MESSAGE(STATUS "LIBRARY_OUTPUT_PATH: " ${LIBRARY_OUTPUT_PATH}) +MESSAGE(STATUS "CMAKE_MODULE_PATH: " ${CMAKE_MODULE_PATH}) +MESSAGE(STATUS "CMAKE_COMMAND: " ${CMAKE_COMMAND}) +MESSAGE(STATUS "CMAKE_ROOT: " ${CMAKE_ROOT}) +MESSAGE(STATUS "CMAKE_CURRENT_LIST_FILE: " ${CMAKE_CURRENT_LIST_FILE}) +MESSAGE(STATUS "CMAKE_CURRENT_LIST_LINE: " ${CMAKE_CURRENT_LIST_LINE}) +MESSAGE(STATUS "CMAKE_INCLUDE_PATH: " ${CMAKE_INCLUDE_PATH}) +MESSAGE(STATUS "CMAKE_LIBRARY_PATH: " ${CMAKE_LIBRARY_PATH}) +MESSAGE(STATUS "CMAKE_SYSTEM: " ${CMAKE_SYSTEM}) +MESSAGE(STATUS "CMAKE_SYSTEM_NAME: " ${CMAKE_SYSTEM_NAME}) +MESSAGE(STATUS "CMAKE_SYSTEM_VERSION: " ${CMAKE_SYSTEM_VERSION}) +MESSAGE(STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR}) +MESSAGE(STATUS "UNIX: " ${UNIX}) +MESSAGE(STATUS "WIN32: " ${WIN32}) +MESSAGE(STATUS "APPLE: " ${APPLE}) +MESSAGE(STATUS "MINGW: " ${MINGW}) +MESSAGE(STATUS "CYGWIN: " ${CYGWIN}) +MESSAGE(STATUS "BORLAND: " ${BORLAND}) +MESSAGE(STATUS "MSVC: " ${MSVC}) +MESSAGE(STATUS "MSVC_IDE: " ${MSVC_IDE}) +MESSAGE(STATUS "MSVC60: " ${MSVC60}) +MESSAGE(STATUS "MSVC70: " ${MSVC70}) +MESSAGE(STATUS "MSVC71: " ${MSVC71}) +MESSAGE(STATUS "MSVC80: " ${MSVC80}) +MESSAGE(STATUS "CMAKE_COMPILER_2005: " ${CMAKE_COMPILER_2005}) +MESSAGE(STATUS "CMAKE_SKIP_RULE_DEPENDENCY: " ${CMAKE_SKIP_RULE_DEPENDENCY}) +MESSAGE(STATUS "CMAKE_SKIP_INSTALL_ALL_DEPENDENCY: " ${CMAKE_SKIP_INSTALL_ALL_DEPENDENCY}) +MESSAGE(STATUS "CMAKE_SKIP_RPATH: " ${CMAKE_SKIP_RPATH}) +MESSAGE(STATUS "CMAKE_VERBOSE_MAKEFILE: " ${CMAKE_VERBOSE_MAKEFILE}) +MESSAGE(STATUS "CMAKE_SUPPRESS_REGENERATION: " ${CMAKE_SUPPRESS_REGENERATION}) +MESSAGE(STATUS "CMAKE_C_FLAGS: " ${CMAKE_C_FLAGS}) +MESSAGE(STATUS "CMAKE_CXX_FLAGS: " ${CMAKE_CXX_FLAGS}) +MESSAGE(STATUS "CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE}) +MESSAGE(STATUS "BUILD_SHARED_LIBS: " ${BUILD_SHARED_LIBS}) +MESSAGE(STATUS "CMAKE_C_COMPILER: " ${CMAKE_C_COMPILER}) +MESSAGE(STATUS "CMAKE_CXX_COMPILER: " ${CMAKE_CXX_COMPILER}) +MESSAGE(STATUS "CMAKE_COMPILER_IS_GNUCC: " ${CMAKE_COMPILER_IS_GNUCC}) +MESSAGE(STATUS "CMAKE_COMPILER_IS_GNUCXX : " ${CMAKE_COMPILER_IS_GNUCXX}) +MESSAGE(STATUS "CMAKE_AR: " ${CMAKE_AR}) +MESSAGE(STATUS "CMAKE_RANLIB: " ${CMAKE_RANLIB}) +MESSAGE(STATUS "========================================") + +# +# Build 3rd party libraries first +# +ADD_SUBDIRECTORY(3rdparty) + +# +# Compiler flags +# + +ADD_DEFINITIONS("-fvisibility=default") # mark all exported symbols as visible + +# Warnings mode +#ADD_DEFINITIONS("-Werror") # Make all warnings into errors. + +# Warning flags +ADD_DEFINITIONS("-Wall") # Generate all warnings +ADD_DEFINITIONS("-Wextra") # Generate even more extra warnings +ADD_DEFINITIONS("-pedantic") # Accept only pedantic code +#ADD_DEFINITIONS("-Weffc++") # Accept only effective C++ code +ADD_DEFINITIONS("-Wwrite-strings") # Do not accept writing to contant string memory +ADD_DEFINITIONS("-Winit-self") # Do not accept initializing variable with itself +ADD_DEFINITIONS("-Wcast-align") # Do not accept misaligning with casting +ADD_DEFINITIONS("-Wcast-qual") # Do not accept removing qualifiers with casting +#ADD_DEFINITIONS("-Wold-style-cast") # Do not accept old style casting +ADD_DEFINITIONS("-Wpointer-arith") # Warn about void pointer arthimetic +ADD_DEFINITIONS("-Wstrict-aliasing") # Ensure strict aliasing +ADD_DEFINITIONS("-Wuninitialized") # Do not accept uninitialized variables +ADD_DEFINITIONS("-Wmissing-declarations") # Warn about global and non-accesible functions +ADD_DEFINITIONS("-Woverloaded-virtual") # Warn about incidental overiding non-virtual base methods +ADD_DEFINITIONS("-Wnon-virtual-dtor") # Warn about non-virtual destructor +ADD_DEFINITIONS("-Wctor-dtor-privacy") # Warn about useless and non-constructible classes +ADD_DEFINITIONS("-Wlong-long") # Do not allow using long long +#ADD_DEFINITIONS("-Wunreachable-code") # Warn about unreachable code +ADD_DEFINITIONS("-Wfloat-equal") # Do not accept comparing floating points with equal operator +ADD_DEFINITIONS("-Wabi") # Warn about possible ABI problems +ADD_DEFINITIONS("-Wswitch-enum") # Check switch enumeration +ADD_DEFINITIONS("-Wformat=2") # Check printf formatting +ADD_DEFINITIONS("-Wundef") # Warn if an undefined identifier is evaluated in an @if directive. +ADD_DEFINITIONS("-Wshadow") # Warn whenever a local variable shadows another local variable, parameter or global variable or whenever a built-in function is shadowed +ADD_DEFINITIONS("-Wconversion") # Warn for implicit conversions that may alter a value +ADD_DEFINITIONS("-Wlogical-op") # Warn about suspicious uses of logical operators in expressions +#ADD_DEFINITIONS("-Waggregate-return") # Warn if any functions that return structures or unions are defined or called. +ADD_DEFINITIONS("-Wmissing-field-initializers") # Warn if a structure's initializer has some fields missing. +ADD_DEFINITIONS("-Wredundant-decls") # Warn if anything is declared more than once in the same scope, even in cases where multiple declaration is valid and changes nothing. +#ADD_DEFINITIONS("-Wmissing-include-dirs") # Warn if a user-supplied include directory does not exist. +ADD_DEFINITIONS("-Wswitch-default") # Warn whenever a switch statement does not have a default case. +ADD_DEFINITIONS("-Wsync-nand") # Warn when __sync_fetch_and_nand and __sync_nand_and_fetch built-in functions are used. These functions changed semantics in GCC 4.4. +ADD_DEFINITIONS("-Wunused") # All the above -Wunused options combined. +ADD_DEFINITIONS("-Wstrict-overflow=5") # Also warn about cases where the compiler reduces the magnitude of a constant involved in a comparison. +#ADD_DEFINITIONS("-Wunsafe-loop-optimizations") # Warn if the loop cannot be optimized because the compiler could not assume anything on the bounds of the loop indices. With -funsafe-loop-optimizations warn if the compiler made such assumptions. +#ADD_DEFINITIONS("-Wmissing-format-attribute") # Warn about function pointers which might be candidates for format attributes. +#ADD_DEFINITIONS("-Wpadded") # Warn if padding is included in a structure, either to align an element of the structure or to align the whole structure. +#ADD_DEFINITIONS("-Winline") # Warn if a function can not be inlined and it was declared as inline. +ADD_DEFINITIONS("-Wdisabled-optimization") # Warn if a requested optimization pass is disabled. +ADD_DEFINITIONS("-std=c++0x") + +# +# Core library files +# +# Define all core library headers and sources. As detail files +# are usually templated and though recompiled in each file, we +# have to compile full source for each target. +# + +# Set DPL 3rd party include directory +SET(DPL_3RDPARTY_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/3rdparty) + +# Set names of binaries being created +SET(TARGET_DPL_EFL "lib${PROJECT_NAME}-efl") +SET(TARGET_DPL_DBUS_EFL "lib${PROJECT_NAME}-dbus-efl") +SET(TARGET_DPL_DB_EFL "lib${PROJECT_NAME}-db-efl") +SET(TARGET_DPL_EVENT_EFL "lib${PROJECT_NAME}-event-efl") +SET(TARGET_DPL_SOCKET_EFL "lib${PROJECT_NAME}-socket-efl") +SET(TARGET_DPL_RPC_EFL "lib${PROJECT_NAME}-rpc-efl") +SET(TARGET_DPL_TEST_ENGINE_EFL "lib${PROJECT_NAME}-test-efl") +SET(TARGET_DPL_LOG_EFL "lib${PROJECT_NAME}-log-efl") +SET(TARGET_DPL_POPUP "lib${PROJECT_NAME}-popup-efl") +SET(TARGET_WRT_DAO_RW_LIB "dpl-wrt-dao-rw") +SET(TARGET_WRT_DAO_RO_LIB "dpl-wrt-dao-ro") +SET(TARGET_DPL_UTILS_EFL "lib${PROJECT_NAME}-utils-efl") +SET(TARGET_ACE_DAO_RO_LIB "dpl-ace-dao-ro") +SET(TARGET_ACE_DAO_RW_LIB "dpl-ace-dao-rw") +SET(TARGET_ACE_LIB "dpl-ace") +SET(TARGET_VCORE_LIB "dpl-vcore") + +ADD_SUBDIRECTORY(modules) + +ADD_SUBDIRECTORY(build) +ADD_SUBDIRECTORY(tests) +ADD_SUBDIRECTORY(etc) + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..247c97d --- /dev/null +++ b/LICENSE @@ -0,0 +1,203 @@ +Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..ded3804 --- /dev/null +++ b/NOTICE @@ -0,0 +1 @@ +Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. \ No newline at end of file diff --git a/bin/run_all.sh b/bin/run_all.sh new file mode 100755 index 0000000..a245ea7 --- /dev/null +++ b/bin/run_all.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +./dpl-tests-core --output=text +./dpl-tests-dbus --output=text +./dpl-tests-db --output=text +./dpl-tests-event --output=text diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt new file mode 100644 index 0000000..835f856 --- /dev/null +++ b/build/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +ADD_SUBDIRECTORY(core) +ADD_SUBDIRECTORY(dbus) +ADD_SUBDIRECTORY(db) +ADD_SUBDIRECTORY(event) +ADD_SUBDIRECTORY(socket) +ADD_SUBDIRECTORY(rpc) +ADD_SUBDIRECTORY(test) +#ADD_SUBDIRECTORY(log) +ADD_SUBDIRECTORY(vcore) +ADD_SUBDIRECTORY(ace) +ADD_SUBDIRECTORY(widget_dao) +ADD_SUBDIRECTORY(popup) +ADD_SUBDIRECTORY(utils) diff --git a/build/ace/CMakeLists.txt b/build/ace/CMakeLists.txt new file mode 100644 index 0000000..f2ab0b8 --- /dev/null +++ b/build/ace/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) +# @brief +# + +CONFIGURE_FILE(dpl-ace-dao-ro.pc.in dpl-ace-dao-ro.pc @ONLY) +CONFIGURE_FILE(dpl-ace-dao-rw.pc.in dpl-ace-dao-rw.pc @ONLY) +CONFIGURE_FILE(dpl-ace.pc.in dpl-ace.pc @ONLY) +INSTALL(FILES + ${CMAKE_BINARY_DIR}/build/ace/dpl-ace-dao-ro.pc + ${CMAKE_BINARY_DIR}/build/ace/dpl-ace-dao-rw.pc + ${CMAKE_BINARY_DIR}/build/ace/dpl-ace.pc + DESTINATION + lib/pkgconfig + ) + diff --git a/build/ace/dpl-ace-dao-ro.pc.in b/build/ace/dpl-ace-dao-ro.pc.in new file mode 100644 index 0000000..6af8514 --- /dev/null +++ b/build/ace/dpl-ace-dao-ro.pc.in @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include/dpl-efl + +Name: dpl-ace-dao-ro +Description: dpl-ace-dao-ro +Version: @VERSION@ +Requires: dpl-efl openssl +Libs: -ldpl-ace-dao-ro -L${libdir} +Cflags: -I${includedir} diff --git a/build/ace/dpl-ace-dao-rw.pc.in b/build/ace/dpl-ace-dao-rw.pc.in new file mode 100644 index 0000000..fe6f99f --- /dev/null +++ b/build/ace/dpl-ace-dao-rw.pc.in @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include/dpl-elf + +Name: dpl-aco-dao-rw +Description: dpl-ace-dao-rw +Version: @VERSION@ +Requires: dpl-ace-dao-ro +Libs: -ldpl-ace-dao-rw -L${libdir} +Cflags: -I${includedir} diff --git a/build/ace/dpl-ace.pc.in b/build/ace/dpl-ace.pc.in new file mode 100644 index 0000000..45d1278 --- /dev/null +++ b/build/ace/dpl-ace.pc.in @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include/dpl-efl + +Name: dpl-ace +Description: dpl-ace +Version: @VERSION@ +Requires: dpl-efl openssl +Libs: -ldpl-ace -L${libdir} +Cflags: -I${includedir} diff --git a/build/core/CMakeLists.txt b/build/core/CMakeLists.txt new file mode 100644 index 0000000..a6dc980 --- /dev/null +++ b/build/core/CMakeLists.txt @@ -0,0 +1,88 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# + +# Check required modules +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(SYS_EFL + ecore + appcore-efl + openssl + dlog + vconf + libpcrecpp + icu-uc + REQUIRED) + +# Add core include directories +INCLUDE_DIRECTORIES( + ${DPL_LOG_INCLUDE_DIR} + ${DPL_CORE_INCLUDE_DIR} + ${DPL_3RDPARTY_INCLUDE_DIR} + ${SYS_EFL_INCLUDE_DIRS} +) + +LINK_DIRECTORIES(${SYS_EFL_LIBRARY_DIRS}) + +# Base EFL based DPL library +SET(DPL_EFL_LIBRARY "${PROJECT_NAME}-efl") + +# Build shared library +ADD_LIBRARY(${TARGET_DPL_EFL} SHARED + ${DPL_CORE_SOURCES} + ${DPL_LOG_SOURCES} +) + +TARGET_LINK_LIBRARIES(${TARGET_DPL_EFL} ${SYS_EFL_LIBRARIES} "-lrt") + +# Link with 3rd party libraries +LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/3rdparty) + +TARGET_LINK_LIBRARIES(${TARGET_DPL_EFL} lib3rdparty) + +# Target library properties +SET_TARGET_PROPERTIES(${TARGET_DPL_EFL} PROPERTIES + SOVERSION ${VERSION} + CLEAN_DIRECT_OUTPUT 1 + OUTPUT_NAME ${DPL_EFL_LIBRARY}) + +# Install libraries +INSTALL(TARGETS ${TARGET_DPL_EFL} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +# Install detail headers +INSTALL(FILES ${DPL_CORE_EFL_DETAIL_HEADERS} + DESTINATION include/dpl-efl/dpl) + +# Install core headers +INSTALL(FILES ${DPL_CORE_HEADERS} + DESTINATION include/dpl-efl/dpl) + +# Install log headers +INSTALL(FILES ${DPL_LOG_HEADERS} + DESTINATION include/dpl-efl/dpl/log) + +# Install 3rdparty headers +INSTALL(FILES ${DPL_3RDPARTY_HEADERS} + DESTINATION include/dpl-efl/dpl/3rdparty/fastdelegate) + +# Install pkgconfig script +INSTALL(FILES dpl-efl.pc + DESTINATION lib/pkgconfig) diff --git a/build/core/DESCRIPTION b/build/core/DESCRIPTION new file mode 100644 index 0000000..f7f1581 --- /dev/null +++ b/build/core/DESCRIPTION @@ -0,0 +1,2 @@ +!!!options!!! stop +EFL support diff --git a/build/core/dpl-efl.pc b/build/core/dpl-efl.pc new file mode 100644 index 0000000..193fcd4 --- /dev/null +++ b/build/core/dpl-efl.pc @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: dpl-efl +Description: DPL - EFL based +Version: 1.0.0 +Requires: ecore appcore-efl openssl dlog vconf +Libs: -L${libdir} -ldpl-efl +Cflags: -I${includedir}/dpl-efl diff --git a/build/db/CMakeLists.txt b/build/db/CMakeLists.txt new file mode 100644 index 0000000..7426d5b --- /dev/null +++ b/build/db/CMakeLists.txt @@ -0,0 +1,68 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +# Check required modules +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(SYS_EFL_DB + sqlite3 + db-util + REQUIRED) + +# Add core include directories +INCLUDE_DIRECTORIES( + ${DPL_LOG_INCLUDE_DIR} + ${DPL_CORE_INCLUDE_DIR} + ${DPL_DB_INCLUDE_DIR} + ${SYS_EFL_INCLUDE_DIRS}) + +LINK_DIRECTORIES( + ${SYS_EFL_DB_LIBRARY_DIRS} +) + +# Base EFL based DPL library +SET(DPL_EFL_DB_LIBRARY "${PROJECT_NAME}-db-efl") + +# Build shared library + +ADD_LIBRARY(${TARGET_DPL_DB_EFL} SHARED ${DPL_DB_SOURCES}) + +TARGET_LINK_LIBRARIES(${TARGET_DPL_DB_EFL} + ${SYS_EFL_DB_LIBRARIES} + ${TARGET_DPL_EFL} +) + +# Target library properties +SET_TARGET_PROPERTIES(${TARGET_DPL_DB_EFL} PROPERTIES + SOVERSION ${VERSION} + CLEAN_DIRECT_OUTPUT 1 + OUTPUT_NAME ${DPL_EFL_DB_LIBRARY}) + +# Install libraries +INSTALL(TARGETS ${TARGET_DPL_DB_EFL} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +# Install detail headers +INSTALL(FILES ${DPL_DB_HEADERS} + DESTINATION include/dpl-efl/dpl/db) + +# Install pkgconfig script +INSTALL(FILES dpl-db-efl.pc + DESTINATION lib/pkgconfig) diff --git a/build/db/dpl-db-efl.pc b/build/db/dpl-db-efl.pc new file mode 100644 index 0000000..f0dc43e --- /dev/null +++ b/build/db/dpl-db-efl.pc @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: dpl-db-efl +Description: DPL DB - EFL based +Version: 1.0.0 +Requires: dpl-efl sqlite3 db-util +Libs: -L${libdir} -ldpl-db-efl +Cflags: -I${includedir}/dpl-efl diff --git a/build/dbus/CMakeLists.txt b/build/dbus/CMakeLists.txt new file mode 100644 index 0000000..71e883a --- /dev/null +++ b/build/dbus/CMakeLists.txt @@ -0,0 +1,69 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# + +# Check required modules +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(SYS_EFL_DBUS + dbus-1 + REQUIRED) + +# Add core include directories +INCLUDE_DIRECTORIES( + ${DPL_LOG_INCLUDE_DIR} + ${DPL_CORE_INCLUDE_DIR} + ${DPL_DBUS_INCLUDE_DIR} + ${DPL_EVENT_INCLUDE_DIR} + ${SYS_EFL_INCLUDE_DIRS}) + +LINK_DIRECTORIES( + ${SYS_EFL_DBUS_LIBRARY_DIRS} +) + +# Base EFL based DPL library +SET(DPL_EFL_DBUS_LIBRARY "${PROJECT_NAME}-dbus-efl") + +# Build shared library + +ADD_LIBRARY(${TARGET_DPL_DBUS_EFL} SHARED ${DPL_DBUS_SOURCES}) + +TARGET_LINK_LIBRARIES(${TARGET_DPL_DBUS_EFL} + ${SYS_EFL_DBUS_LIBRARIES} + ${TARGET_DPL_EFL} + ${TARGET_DPL_EVENT_EFL} +) + +# Target library properties +SET_TARGET_PROPERTIES(${TARGET_DPL_DBUS_EFL} PROPERTIES + SOVERSION ${VERSION} + CLEAN_DIRECT_OUTPUT 1 + OUTPUT_NAME ${DPL_EFL_DBUS_LIBRARY}) + +# Install libraries +INSTALL(TARGETS ${TARGET_DPL_DBUS_EFL} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +# Install detail headers +INSTALL(FILES ${DPL_DBUS_HEADERS} + DESTINATION include/dpl-efl/dpl/dbus) + +# Install pkgconfig script +INSTALL(FILES dpl-dbus-efl.pc + DESTINATION lib/pkgconfig) diff --git a/build/dbus/dpl-dbus-efl.pc b/build/dbus/dpl-dbus-efl.pc new file mode 100644 index 0000000..6fafa42 --- /dev/null +++ b/build/dbus/dpl-dbus-efl.pc @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: dpl-dbus-efl +Description: DPL DBus - EFL based +Version: 1.0.0 +Requires: dbus-1 dpl-efl dpl-event-efl +Libs: -L${libdir} -ldpl-dbus-efl +Cflags: -I${includedir}/dpl-efl diff --git a/build/event/CMakeLists.txt b/build/event/CMakeLists.txt new file mode 100644 index 0000000..305434a --- /dev/null +++ b/build/event/CMakeLists.txt @@ -0,0 +1,71 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +# Check required modules +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(SYS_EFL_EVENT + ecore + appcore-efl + vconf + heynoti + REQUIRED +) + +# Add core include directories +INCLUDE_DIRECTORIES( + ${DPL_LOG_INCLUDE_DIR} + ${DPL_CORE_INCLUDE_DIR} + ${DPL_EVENT_INCLUDE_DIR} + ${SYS_EFL_EVENT_INCLUDE_DIRS}) + +LINK_DIRECTORIES( + ${SYS_EFL_EVENT_LIBRARY_DIRS} +) + +# Base EFL based DPL library +SET(DPL_EFL_EVENT_LIBRARY "${PROJECT_NAME}-event-efl") + +# Build shared library + +ADD_LIBRARY(${TARGET_DPL_EVENT_EFL} SHARED ${DPL_EVENT_SOURCES}) + +TARGET_LINK_LIBRARIES(${TARGET_DPL_EVENT_EFL} + ${SYS_EFL_EVENT_LIBRARIES} + ${TARGET_DPL_EFL} +) + +# Target library properties +SET_TARGET_PROPERTIES(${TARGET_DPL_EVENT_EFL} PROPERTIES + SOVERSION ${VERSION} + CLEAN_DIRECT_OUTPUT 1 + OUTPUT_NAME ${DPL_EFL_EVENT_LIBRARY}) + +# Install libraries +INSTALL(TARGETS ${TARGET_DPL_EVENT_EFL} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +# Install detail headers +INSTALL(FILES ${DPL_EVENT_HEADERS} + DESTINATION include/dpl-efl/dpl/event) + +# Install pkgconfig script +INSTALL(FILES dpl-event-efl.pc + DESTINATION lib/pkgconfig) diff --git a/build/event/dpl-event-efl.pc b/build/event/dpl-event-efl.pc new file mode 100644 index 0000000..1b9f911 --- /dev/null +++ b/build/event/dpl-event-efl.pc @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: dpl-event-efl +Description: DPL Event - EFL based +Version: 1.0.0 +Requires: dpl-efl ecore appcore-efl vconf heynoti +Libs: -L${libdir} -ldpl-event-efl +Cflags: -I${includedir}/dpl-efl diff --git a/build/log/CMakeLists.txt b/build/log/CMakeLists.txt new file mode 100644 index 0000000..6669aaa --- /dev/null +++ b/build/log/CMakeLists.txt @@ -0,0 +1,65 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +# Check required modules +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(SYS_EFL_LOG + dlog + REQUIRED +) + +# Add core include directories +INCLUDE_DIRECTORIES( + ${DPL_LOG_INCLUDE_DIR} + ${SYS_EFL_LOG_INCLUDE_DIRS} +) + +LINK_DIRECTORIES( + ${SYS_EFL_LOG_LIBRARY_DIRS} +) + +# Base EFL based DPL library +SET(DPL_EFL_LOG_LIBRARY "${PROJECT_NAME}-log-efl") + +# Build shared library + +ADD_LIBRARY(${TARGET_DPL_LOG_EFL} SHARED ${DPL_LOG_SOURCES}) + +TARGET_LINK_LIBRARIES(${TARGET_DPL_LOG_EFL} + ${SYS_EFL_LOG_LIBRARIES} +) + +# Target library properties +SET_TARGET_PROPERTIES(${TARGET_DPL_LOG_EFL} PROPERTIES + SOVERSION ${VERSION} + CLEAN_DIRECT_OUTPUT 1 + OUTPUT_NAME ${DPL_EFL_LOG_LIBRARY}) + +# Install libraries +INSTALL(TARGETS ${TARGET_DPL_LOG_EFL} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +INSTALL(FILES ${DPL_LOG_HEADERS} + DESTINATION include/dpl-efl/dpl/log) + +# Install pkgconfig script +INSTALL(FILES dpl-log-efl.pc + DESTINATION lib/pkgconfig) diff --git a/build/log/dpl-log-efl.pc b/build/log/dpl-log-efl.pc new file mode 100644 index 0000000..ea4fff9 --- /dev/null +++ b/build/log/dpl-log-efl.pc @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: dpl-log-efl +Description: DPL Log Engine - EFL based +Version: 1.0.0 +Requires: dpl-efl dlog +Libs: -L${libdir} -ldpl-log-efl +Cflags: -I${includedir}/dpl-efl diff --git a/build/popup/CMakeLists.txt b/build/popup/CMakeLists.txt new file mode 100644 index 0000000..d55fa47 --- /dev/null +++ b/build/popup/CMakeLists.txt @@ -0,0 +1,68 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Pawel Sikorski (p.sikorski@samsung.com) +# @version 1.0 +# @brief +# + +# Check required modules +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(SYS_POPUP + elementary + REQUIRED) + +# Add core include directories +INCLUDE_DIRECTORIES( + ${DPL_LOG_INCLUDE_DIR} + ${DPL_CORE_INCLUDE_DIR} + ${DPL_POPUP_INCLUDE_DIR} + ${DPL_EVENT_INCLUDE_DIR} + ${SYS_EFL_INCLUDE_DIRS}) + +LINK_DIRECTORIES( + ${SYS_POPUP_LIBRARY_DIRS} +) + +# Base EFL based DPL library +SET(DPL_POPUP_LIBRARY "${PROJECT_NAME}-popup-efl") + +# Build shared library + +ADD_LIBRARY(${TARGET_DPL_POPUP} SHARED ${DPL_POPUP_SOURCES}) + +TARGET_LINK_LIBRARIES(${TARGET_DPL_POPUP} + ${SYS_POPUP_LIBRARIES} + ${TARGET_DPL_EFL} +) + +# Target library properties +SET_TARGET_PROPERTIES(${TARGET_DPL_POPUP} PROPERTIES + SOVERSION ${VERSION} + CLEAN_DIRECT_OUTPUT 1 + OUTPUT_NAME ${DPL_POPUP_LIBRARY}) + +# Install libraries +INSTALL(TARGETS ${TARGET_DPL_POPUP} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +# Install detail headers +INSTALL(FILES ${DPL_POPUP_HEADERS} + DESTINATION include/dpl-efl/dpl/popup) + +# Install pkgconfig script +INSTALL(FILES dpl-popup-efl.pc + DESTINATION lib/pkgconfig) diff --git a/build/popup/dpl-popup-efl.pc b/build/popup/dpl-popup-efl.pc new file mode 100644 index 0000000..92053b9 --- /dev/null +++ b/build/popup/dpl-popup-efl.pc @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: dpl-popup-efl +Description: DPL Popup functionality - EFL based +Version: 1.0.0 +Requires: dpl-efl +Libs: -L${libdir} -ldpl-popup-efl +Cflags: -I${includedir}/dpl-efl diff --git a/build/rpc/CMakeLists.txt b/build/rpc/CMakeLists.txt new file mode 100644 index 0000000..553c52f --- /dev/null +++ b/build/rpc/CMakeLists.txt @@ -0,0 +1,74 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +# Check required modules +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(SYS_EFL_RPC + ecore + appcore-efl + REQUIRED +) + +# Add core include directories +INCLUDE_DIRECTORIES( + ${DPL_LOG_INCLUDE_DIR} + ${DPL_CORE_INCLUDE_DIR} + ${DPL_SOCKET_INCLUDE_DIR} + ${DPL_EVENT_INCLUDE_DIR} + ${DPL_RPC_INCLUDE_DIR} + ${SYS_EFL_RPC_INCLUDE_DIRS} +) + +LINK_DIRECTORIES( + ${SYS_EFL_RPC_LIBRARY_DIRS} +) + +# Base EFL based DPL library +SET(DPL_EFL_RPC_LIBRARY "${PROJECT_NAME}-rpc-efl") + +# Build shared library + +ADD_LIBRARY(${TARGET_DPL_RPC_EFL} SHARED ${DPL_RPC_SOURCES}) + +TARGET_LINK_LIBRARIES(${TARGET_DPL_RPC_EFL} + ${SYS_EFL_RPC_LIBRARIES} + ${TARGET_DPL_EFL} + ${TARGET_DPL_EVENT_EFL} + ${TARGET_DPL_SOCKET_EFL} +) + +# Target library properties +SET_TARGET_PROPERTIES(${TARGET_DPL_RPC_EFL} PROPERTIES + SOVERSION ${VERSION} + CLEAN_DIRECT_OUTPUT 1 + OUTPUT_NAME ${DPL_EFL_RPC_LIBRARY}) + +# Install libraries +INSTALL(TARGETS ${TARGET_DPL_RPC_EFL} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +# Install detail headers +INSTALL(FILES ${DPL_RPC_HEADERS} + DESTINATION include/dpl-efl/dpl/rpc) + +# Install pkgconfig script +INSTALL(FILES dpl-rpc-efl.pc + DESTINATION lib/pkgconfig) diff --git a/build/rpc/dpl-rpc-efl.pc b/build/rpc/dpl-rpc-efl.pc new file mode 100644 index 0000000..c5e870a --- /dev/null +++ b/build/rpc/dpl-rpc-efl.pc @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: dpl-rpc-efl +Description: DPL RPC - EFL based +Version: 1.0.0 +Requires: dpl-efl dpl-event-efl dpl-socket-efl +Libs: -L${libdir} -ldpl-rpc-efl +Cflags: -I${includedir}/dpl-efl diff --git a/build/socket/CMakeLists.txt b/build/socket/CMakeLists.txt new file mode 100644 index 0000000..ea7df4b --- /dev/null +++ b/build/socket/CMakeLists.txt @@ -0,0 +1,72 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +# Check required modules +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(SYS_EFL_SOCKET + ecore + appcore-efl + REQUIRED +) + +# Add core include directories +INCLUDE_DIRECTORIES( + ${DPL_LOG_INCLUDE_DIR} + ${DPL_CORE_INCLUDE_DIR} + ${DPL_SOCKET_INCLUDE_DIR} + ${DPL_EVENT_INCLUDE_DIR} + ${SYS_EFL_SOCKET_INCLUDE_DIRS} +) + +LINK_DIRECTORIES( + ${SYS_EFL_SOCKET_LIBRARY_DIRS} +) + +# Base EFL based DPL library +SET(DPL_EFL_SOCKET_LIBRARY "${PROJECT_NAME}-socket-efl") + +# Build shared library + +ADD_LIBRARY(${TARGET_DPL_SOCKET_EFL} SHARED ${DPL_SOCKET_SOURCES}) + +TARGET_LINK_LIBRARIES(${TARGET_DPL_SOCKET_EFL} + ${SYS_EFL_SOCKET_LIBRARIES} + ${TARGET_DPL_EFL} + ${TARGET_DPL_EVENT_EFL} +) + +# Target library properties +SET_TARGET_PROPERTIES(${TARGET_DPL_SOCKET_EFL} PROPERTIES + SOVERSION ${VERSION} + CLEAN_DIRECT_OUTPUT 1 + OUTPUT_NAME ${DPL_EFL_SOCKET_LIBRARY}) + +# Install libraries +INSTALL(TARGETS ${TARGET_DPL_SOCKET_EFL} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +# Install detail headers +INSTALL(FILES ${DPL_SOCKET_HEADERS} + DESTINATION include/dpl-efl/dpl/socket) + +# Install pkgconfig script +INSTALL(FILES dpl-socket-efl.pc + DESTINATION lib/pkgconfig) diff --git a/build/socket/dpl-socket-efl.pc b/build/socket/dpl-socket-efl.pc new file mode 100644 index 0000000..6de04b0 --- /dev/null +++ b/build/socket/dpl-socket-efl.pc @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: dpl-socket-efl +Description: DPL Socket - EFL based +Version: 1.0.0 +Requires: dpl-efl dpl-event-efl +Libs: -L${libdir} -ldpl-socket-efl +Cflags: -I${includedir}/dpl-efl diff --git a/build/test/CMakeLists.txt b/build/test/CMakeLists.txt new file mode 100644 index 0000000..22cb230 --- /dev/null +++ b/build/test/CMakeLists.txt @@ -0,0 +1,67 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(SYS_EFL_TEST_ENGINE + appcore-efl + REQUIRED) + + +# Add core include directories +INCLUDE_DIRECTORIES( + ${DPL_LOG_INCLUDE_DIR} + ${DPL_CORE_INCLUDE_DIR} + ${DPL_TEST_ENGINE_INCLUDE_DIR} + ${SYS_EFL_TEST_ENGINE_INCLUDE_DIRS} +) + +LINK_DIRECTORIES( + ${SYS_EFL_TEST_ENGINE_LIBRARY_DIRS} +) + +# Base EFL based DPL library +SET(DPL_EFL_TEST_ENGINE_LIBRARY "${PROJECT_NAME}-test-efl") + +# Build shared library + +ADD_LIBRARY(${TARGET_DPL_TEST_ENGINE_EFL} SHARED ${DPL_TEST_ENGINE_SOURCES}) + +TARGET_LINK_LIBRARIES(${TARGET_DPL_TEST_ENGINE_EFL} + ${TARGET_DPL_EFL} +) + +# Target library properties +SET_TARGET_PROPERTIES(${TARGET_DPL_TEST_ENGINE_EFL} PROPERTIES + SOVERSION ${VERSION} + CLEAN_DIRECT_OUTPUT 1 + OUTPUT_NAME ${DPL_EFL_TEST_ENGINE_LIBRARY}) + +# Install libraries +INSTALL(TARGETS ${TARGET_DPL_TEST_ENGINE_EFL} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +# Install detail headers +INSTALL(FILES ${DPL_TEST_ENGINE_HEADERS} + DESTINATION include/dpl-efl/dpl/test) + +# Install pkgconfig script +INSTALL(FILES dpl-test-efl.pc + DESTINATION lib/pkgconfig) diff --git a/build/test/dpl-test-efl.pc b/build/test/dpl-test-efl.pc new file mode 100644 index 0000000..94e9064 --- /dev/null +++ b/build/test/dpl-test-efl.pc @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: dpl-test-efl +Description: DPL Test Engine - EFL based +Version: 1.0.0 +Requires: dpl-efl +Libs: -L${libdir} -ldpl-test-efl +Cflags: -I${includedir}/dpl-efl diff --git a/build/utils/CMakeLists.txt b/build/utils/CMakeLists.txt new file mode 100644 index 0000000..6a4f875 --- /dev/null +++ b/build/utils/CMakeLists.txt @@ -0,0 +1,85 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Soyoung Kim (sy037.kim@samsung.com) +# @version 1.0 +# @brief +# + +# Check required modules +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(SYS_EFL_UTILS + dlog + libiri + appcore-efl + libidn + REQUIRED +) + +# Add core include directories +INCLUDE_DIRECTORIES( + ${DPL_LOG_INCLUDE_DIR} + ${DPL_CORE_INCLUDE_DIR} + ${DPL_DB_INCLUDE_DIR} + ${SYS_EFL_UTILS_INCLUDE_DIRS} + ${DPL_UTILS_INCLUDE_DIR} + ${DPL_LOCALIZATION_INCLUDE_DIR} +) + +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/modules/widget_dao/include) +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/modules/vcore/src/vcore) + +LINK_DIRECTORIES( + ${SYS_EFL_UTILS_LIBRARY_DIRS} +) + +# Base EFL based DPL library +SET(DPL_EFL_UTILS_LIBRARY "${PROJECT_NAME}-utils-efl") + +# Build shared library + +ADD_LIBRARY(${TARGET_DPL_UTILS_EFL} SHARED + ${DPL_UTILS_SOURCES} + ${DPL_LOCALIZATION_SOURCES} +) + +TARGET_LINK_LIBRARIES(${TARGET_DPL_UTILS_EFL} + ${SYS_EFL_UTILS_LIBRARIES} + ${TARGET_DPL_EFL} + ${TARGET_WRT_DAO_RW_LIB} + ${SYS_EFL_DB_LIBRARIES} +) + +# Target library properties +SET_TARGET_PROPERTIES(${TARGET_DPL_UTILS_EFL} PROPERTIES + SOVERSION ${VERSION} + CLEAN_DIRECT_OUTPUT 1 + OUTPUT_NAME ${DPL_EFL_UTILS_LIBRARY}) + +# Install libraries +INSTALL(TARGETS ${TARGET_DPL_UTILS_EFL} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +# Install detail headers +INSTALL(FILES ${DPL_UTILS_HEADERS} + DESTINATION include/dpl-efl/dpl/utils) + +INSTALL(FILES ${DPL_LOCALIZATION_HEADERS} + DESTINATION include/dpl-efl/dpl/localization) + +# Install pkgconfig script +INSTALL(FILES dpl-utils-efl.pc + DESTINATION lib/pkgconfig) diff --git a/build/utils/dpl-utils-efl.pc b/build/utils/dpl-utils-efl.pc new file mode 100644 index 0000000..ce770c1 --- /dev/null +++ b/build/utils/dpl-utils-efl.pc @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: dpl-utils-efl +Description: DPL UTILS - EFL based +Version: 1.0.0 +Requires: dpl-efl +Libs: -L${libdir} -ldpl-utils-efl +Cflags: -I${includedir}/dpl-efl diff --git a/build/vcore/CMakeLists.txt b/build/vcore/CMakeLists.txt new file mode 100644 index 0000000..99200d2 --- /dev/null +++ b/build/vcore/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) +# @brief +# + +CONFIGURE_FILE(dpl-vcore.pc.in dpl-vcore.pc @ONLY) +INSTALL(FILES ${CMAKE_BINARY_DIR}/build/vcore/dpl-vcore.pc DESTINATION lib/pkgconfig) diff --git a/build/vcore/dpl-vcore.pc.in b/build/vcore/dpl-vcore.pc.in new file mode 100644 index 0000000..73fb442 --- /dev/null +++ b/build/vcore/dpl-vcore.pc.in @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include/dpl-efl/dpl/vcore + +Name: dpl-vcore +Description: dpl-vcore +Version: @VERSION@ +Requires: libxml-2.0 openssl libsoup-2.4 +Libs: -ldpl-vcore -L${libdir} +Cflags: -I${includedir} diff --git a/build/widget_dao/CMakeLists.txt b/build/widget_dao/CMakeLists.txt new file mode 100644 index 0000000..2703ae5 --- /dev/null +++ b/build/widget_dao/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) +# @brief +# + +CONFIGURE_FILE(dpl-wrt-dao-ro.pc.in dpl-wrt-dao-ro.pc @ONLY) +CONFIGURE_FILE(dpl-wrt-dao-rw.pc.in dpl-wrt-dao-rw.pc @ONLY) +INSTALL(FILES + ${CMAKE_BINARY_DIR}/build/widget_dao/dpl-wrt-dao-ro.pc + ${CMAKE_BINARY_DIR}/build/widget_dao/dpl-wrt-dao-rw.pc + DESTINATION + lib/pkgconfig) diff --git a/build/widget_dao/dpl-wrt-dao-ro.pc.in b/build/widget_dao/dpl-wrt-dao-ro.pc.in new file mode 100644 index 0000000..d2d112b --- /dev/null +++ b/build/widget_dao/dpl-wrt-dao-ro.pc.in @@ -0,0 +1,12 @@ +prefix=/usr +exec_prefix=${prefix} + +libdir=${prefix}/lib +includedir=${prefix}/include +Name: dpl-wrt-dao-ro +Description: dpl-wrt-dao-ro + +Version: @VERSION@ +Requires: dpl-efl libxml-2.0 +Libs: -ldpl-wrt-dao-ro -L${libdir} +Cflags: -I${includedir}/dpl-efl diff --git a/build/widget_dao/dpl-wrt-dao-rw.pc.in b/build/widget_dao/dpl-wrt-dao-rw.pc.in new file mode 100644 index 0000000..c71e58d --- /dev/null +++ b/build/widget_dao/dpl-wrt-dao-rw.pc.in @@ -0,0 +1,12 @@ +prefix=/usr +exec_prefix=${prefix} + +libdir=${prefix}/lib +includedir=${prefix}/include +Name: dpl-wrt-dao-rw +Description: dpl-wrt-dao-rw + +Version: @VERSION@ +Requires: dpl-efl dpl-wrt-dao-ro libxml-2.0 +Libs: -ldpl-wrt-dao-rw -ldpl-wrt-dao-ro -L${libdir} +Cflags: -I${includedir}/dpl-efl diff --git a/debian/DESCRIPTION b/debian/DESCRIPTION new file mode 100644 index 0000000..158a449 --- /dev/null +++ b/debian/DESCRIPTION @@ -0,0 +1 @@ +Debian folder (rules, control etc.) diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..0ec9a2d --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,6 @@ +dpl for Debian +-------------- + + + + -- unknown Tue, 23 Mar 2010 11:40:32 +0100 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..6326243 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,26 @@ +wrt-commons (0.2.18) unstable; urgency=low + + * Boilerplate update + + * Git : tizen2/pkgs/w/wrt-commons + * Tag : wrt-commons_0.2.18 + + -- Tae-Jeong Lee Thu, 23 Feb 2012 16:12:49 +0900 + +wrt-commons (0.2.17) unstable; urgency=low + + * debianize + + * Git : tizen2/pkgs/w/wrt-commons + * Tag : wrt-commons_0.2.17 + + -- Yunchan Cho Wed, 22 Feb 2012 16:57:31 +0900 + +wrt-commons (0.2.16) unstable; urgency=low + + * Init changelog + + * Git : tizen2/pkgs/w/wrt-commons + * Tag : wrt-commons_0.2.16 + + -- Pawel Sikorski Mon, 13 Feb 2012 16:04:58 +0100 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..a786fd3 --- /dev/null +++ b/debian/control @@ -0,0 +1,83 @@ +Source: wrt-commons +Section: devel +Priority: extra +Maintainer: Lukasz Wrzosek, Taejeong.lee +Standards-Version: 3.7.2 +Uploaders: Yunchan Cho ,Przemyslaw Dobrowolski , Pawel Sikorski , Zbigniew Kostrzewa +Build-Depends: debhelper (>= 5), libecore-dev, libslp-setting-dev (>=0.2.5-2), libheynoti-dev, libappcore-efl-dev, libssl-dev, libsqlite3-dev, dlog-dev (>= 0.2.14-0), libglib2.0-dev, libslp-db-util-dev (>= 0.1.0-23), zlib1g-dev, libpcre-dev, libicu-dev, libxmlsec1-dev, openssl, libxml2-dev, libsoup2.4-dev, libcert-svc-dev, libiri-dev, libidn11-dev, libslp-tapi-dev +# If you want to build version of gtk, comment out above one live and use the following one line +#Build-Depends: debhelper (>= 5), libecore-dev, libslp-setting-dev (>=0.2.5-2), libheynoti-dev, libappcore-efl-dev, libssl-dev, libsqlite3-dev, dlog-dev (>= 0.2.14-0), libglib2.0-dev, libslp-db-util-dev (>= 0.1.0-23), libgtk2.0-dev, zlib1g-dev, libpcre-dev, libicu-dev + +Package: wrt-commons +Section: libs +Provides: dpl-efl +Conflicts: dpl-efl +Replaces: dpl-efl +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: Design patterns library - EFL based + Design patterns library is a collection of useful C++ utilities + to easily develop window applications. + +# If you want to build version of gtk, comment out the following lines +#Package: dpl-gtk +#Section: libs +#Architecture: any +#Depends: ${shlibs:Depends}, ${misc:Depends} +#Description: Design patterns library - GTK based +# Design patterns library is a collection of useful C++ utilities +# to easily develop window applications. + +Package: wrt-commons-dev +XB-Public-Package: no +Section: devel +Provides: dpl-efl-dev +Conflicts: dpl-efl-dev +Replaces: dpl-efl-dev +Architecture: any +Depends: wrt-commons (= ${Source-Version}), libecore-dev, libslp-setting-dev (>=0.2.5-2), libheynoti-dev, libappcore-efl-dev, libssl-dev, libsqlite3-dev, dlog-dev (>= 0.2.14-0), libslp-db-util-dev (>= 0.1.0-23), zlib1g-dev, libpcre-dev +Description: Design patterns library - EFL based developer files + Design patterns library is a collection of useful C++ utilities + to easily develop window applications. The most important part of + library is full MVC support. It also supports event-based architecture, + adds wrappers for many packages and provides many basic C++ utilities + as RAII objects, singletons, and many other. + +# If you want to build version of gtk, comment out the following lines +#Package: dpl-gtk-dev +#XB-Public-Package: no +#Section: devel +#Architecture: any +#Depends: dpl-gtk (= ${Source-Version}), libglib2.0-dev, libgtk2.0-dev, libssl-dev, libsqlite3-dev, dlog-dev (>= 0.2.14-0), libslp-db-util-dev (>= 0.1.0-23), zlib1g-dev, libpcre-dev +#Description: Design patterns library - GTK based developer files +# Design patterns library is a collection of useful C++ utilities +# to easily develop window applications. The most important part of +# library is full MVC support. It also supports event-based architecture, +# adds wrappers for many packages and provides many basic C++ utilities +# as RAII objects, singletons, and many other. + +#Package: wrt-commons-test +#Section: libs +#Provides: dpl-test +#Conflicts: dpl-test +#Replaces: dpl-test +#Architecture: any +#Depends: wrt-commons (= ${Source-Version}) +#Description: Test programs. + +Package: wrt-commons-dbg +Section: debug +Provides: dpl-dbg +Conflicts: dpl-dbg +Replaces: dpl-dbg +Architecture: any +Depends: wrt-commons (= ${Source-Version}) +# If you want to build version of gtk, comment out above one live and use the following one line +#Depends: wrt-commons (= ${Source-Version}), dpl-gtk (= ${Source-Version}) +Description: Design patterns library - Debug files + Design patterns library is a collection of useful C++ utilities + to easily develop window applications. The most important part of + library is full MVC support. It also supports event-based architecture, + adds wrappers for many packages and provides many basic C++ utilities + as RAII objects, singletons, and many other. + diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..e69de29 diff --git a/debian/dirs b/debian/dirs new file mode 100644 index 0000000..e69de29 diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..3c1584f --- /dev/null +++ b/debian/rules @@ -0,0 +1,84 @@ +#!/usr/bin/make -f + +#export DH_VERBOSE=1 +PACKAGE_VERSION ?= $(shell sed -n "1 p" debian/changelog | sed 's/.*([0-9]*.\([0-9]*\).*).*/\1/') +CFLAGS = -Wall -g +LDFLAGS = -Wl,--as-needed -Wl,--hash-style=both + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif + +ifneq (,$(findstring yes,$(DPL_LOG))) + DPL_LOGS_STATUS = "ON" +else + DPL_LOGS_STATUS = "OFF" +endif + +BUILD_DIR = cmake-build + +configure: configure-stamp +configure-stamp: + dh_testdir + rm -rf $(BUILD_DIR) + mkdir $(BUILD_DIR) + ( cd $(BUILD_DIR); cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DDPL_LOG=$(DPL_LOGS_STATUS) -DVERSION=${PACKAGE_VERSION}; ) + touch configure-stamp + +build: build-stamp +build-stamp: configure-stamp + dh_testdir + ( cd $(BUILD_DIR); $(MAKE) -j 4 ) + touch $@ + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + rm -rf $(BUILD_DIR) + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + ( cd $(BUILD_DIR); $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install ) + cd .. + +binary-indep: build install + +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_installexamples + dh_install --sourcedir=debian/tmp +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_installinit +# dh_installcron +# dh_installinfo + dh_installman + dh_link + dh_strip --dbg-package=wrt-commons-dbg + dh_compress + dh_fixperms +# dh_perl +# dh_python + dh_makeshlibs + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/debian/wrt-commons-dev.dirs b/debian/wrt-commons-dev.dirs new file mode 100644 index 0000000..ca24d3e --- /dev/null +++ b/debian/wrt-commons-dev.dirs @@ -0,0 +1,5 @@ +usr/include +usr/include/dpl-efl +usr/include/dpl-efl/dpl +usr/include/dpl-efl/dpl/3rdparty +usr/include/dpl-efl/dpl/3rdparty/fastdelegate diff --git a/debian/wrt-commons-dev.install b/debian/wrt-commons-dev.install new file mode 100644 index 0000000..8163e23 --- /dev/null +++ b/debian/wrt-commons-dev.install @@ -0,0 +1,2 @@ +usr/include/dpl-efl/* +usr/lib/pkgconfig/*.pc diff --git a/debian/wrt-commons-test.install b/debian/wrt-commons-test.install new file mode 100644 index 0000000..5dec5e8 --- /dev/null +++ b/debian/wrt-commons-test.install @@ -0,0 +1,13 @@ +/usr/bin/dpl-test* +/usr/bin/dpl-dbus-test-service +/usr/etc/ace/policy* +/usr/etc/ace/attr* +/usr/etc/ace/general* +/usr/etc/ace/undefined* +/usr/etc/ace/CMTest/* +/opt/apps/widget/tests/vcore_widget_uncompressed/* +/opt/apps/widget/tests/vcore_keys/* +/opt/apps/widget/tests/vcore_certs/* +/opt/apps/wrt/wrt-commons/tests/* +/usr/share/dbus-1/services/org.tizen.DBusTestService.service +/opt/apps/widget/tests/localization/* diff --git a/debian/wrt-commons.dirs b/debian/wrt-commons.dirs new file mode 100644 index 0000000..f5b3bee --- /dev/null +++ b/debian/wrt-commons.dirs @@ -0,0 +1,2 @@ +usr/lib + diff --git a/debian/wrt-commons.install b/debian/wrt-commons.install new file mode 100644 index 0000000..dce5a2d --- /dev/null +++ b/debian/wrt-commons.install @@ -0,0 +1,10 @@ +usr/lib/*.so* +/usr/share/wrt-engine/* +/usr/bin/wrt_reset_db.sh +/usr/bin/wrt_create_clean_db.sh +/usr/etc/ace/config* +/usr/etc/ace/bondixml* +/usr/etc/ace/demo.* +/usr/etc/ace/WACPolicy.xml +/usr/etc/ace/UnrestrictedPolicy.xml +/opt/share/cert-svc/certs/code-signing/wac/* diff --git a/debian/wrt-commons.postinst b/debian/wrt-commons.postinst new file mode 100755 index 0000000..ed20a1f --- /dev/null +++ b/debian/wrt-commons.postinst @@ -0,0 +1,63 @@ +#!/bin/sh + +if [ -z ${2} ]; then + echo "This is new install of wrt-commons" + echo "Calling /usr/bin/wrt_reset_db.sh" + /usr/bin/wrt_reset_db.sh +else + # Find out old and new version of databases + WRT_OLD_DB_VERSION=`sqlite3 /opt/dbspace/.wrt.db ".tables" | grep "DB_VERSION_"` + WRT_NEW_DB_VERSION=`cat /usr/share/wrt-engine/wrt_db.sql | tr '[:blank:]' '\n' | grep DB_VERSION_` + ACE_OLD_DB_VERSION=`sqlite3 /opt/dbspace/.ace.db ".tables" | grep "DB_VERSION_"` + ACE_NEW_DB_VERSION=`cat /usr/share/wrt-engine/ace_db.sql | tr '[:blank:]' '\n' | grep DB_VERSION_` + VCORE_OLD_DB_VERSION=`sqlite3 /opt/dbspace/.vcore.db ".tables" | grep "DB_VERSION_"` + VCORE_NEW_DB_VERSION=`cat /usr/share/wrt-engine/vcore_db.sql | tr '[:blank:]' '\n' | grep DB_VERSION_` + echo "OLD wrt database version ${WRT_OLD_DB_VERSION}" + echo "NEW wrt database version ${WRT_NEW_DB_VERSION}" + echo "OLD ace database version ${ACE_OLD_DB_VERSION}" + echo "NEW ace database version ${ACE_NEW_DB_VERSION}" + echo "OLD vcore database version ${VCORE_OLD_DB_VERSION}" + echo "NEW vcore database version ${VCORE_NEW_DB_VERSION}" + + + if [ ${WRT_OLD_DB_VERSION} -a ${WRT_NEW_DB_VERSION} -a ${ACE_OLD_DB_VERSION} -a ${ACE_NEW_DB_VERSION} -a ${VCORE_OLD_DB_VERSION} -a ${VCORE_NEW_DB_VERSION} ] + then + if [ ${WRT_NEW_DB_VERSION} = ${WRT_OLD_DB_VERSION} -a ${ACE_NEW_DB_VERSION} = ${ACE_OLD_DB_VERSION} -a ${VCORE_OLD_DB_VERSION} = ${VCORE_NEW_DB_VERSION} ] + then + echo "Equal database detected so db installation ignored" + else + echo "Calling /usr/bin/wrt_reset_db.sh" + /usr/bin/wrt_reset_db.sh + fi + else + echo "Calling /usr/bin/wrt_reset_db.sh" + /usr/bin/wrt_reset_db.sh + fi +fi + +mkdir -p /usr/etc/ace +mkdir -p /usr/apps/org.tizen.policy + +# 3. configurations +chown root:root /usr/etc/ace/config.xml + +# DBUS services fix +# WARNING: THIS IS TEMPORARY SOLUTION, AS THIS SHOULD NOT BE OUR +# RESPONSIBILITY!!! WE HAVE TO CONTACT TO DBUS MAINAINERS + +if [ -f /var/lib/dbus/machine-id ]; then + echo "machine-id exists" +else + if [ -f /usr/var/lib/dbus/machine-id ]; then + echo "machine-id exists" + else + echo "Creating machine-id" + mkdir -p /usr/var/lib/dbus/ + dbus-uuidgen > /usr/var/lib/dbus/machine-id + dbus-uuidgen --ensure=/usr/var/lib/dbus/machine-id + fi + mkdir -p /var/lib/dbus/ + cp /usr/var/lib/dbus/machine-id /var/lib/dbus/ +fi + +echo "[WRT] wrt-commons postinst done ..." diff --git a/dir-struct.py b/dir-struct.py new file mode 100755 index 0000000..a1fc0f0 --- /dev/null +++ b/dir-struct.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python + +import os +import re + + +def countLines(path): + with open(path) as f: + return len(f.readlines()) + +# RETURNS: ( +# short description (string or None) +# long decsription (array of strings or None) +# options: stop +def parseDescr(lines): + if len(lines) == 0: + return (None, None, False) + linesRest = None + if re.match( r"!!!options!!!", lines[0] ): + optStop = True + linesRest = lines[1:] + else: + optStop = False + linesRest = lines + if len(linesRest) == 0: + return(None,None,optStop) + short = linesRest[0].rstrip() + long = [] + for l in linesRest[1:]: + ll = l.rstrip() + if re.search( r"\S", ll ): + long.append( ll ) + if len(long) == 0: + long = None + + return (short, long, optStop) + +# RETURNS a tree with nodes like: ( +# path (string) +# short description (string or None) +# long decsription (array of strings or None) +# LOC (integer) +# list of subdirs (child nodes like this one) +def parseDir(path): + short = None + long = None + optStop = False + try: + with open( path+'/DESCRIPTION' ) as f: + short, long, optStop = parseDescr( f.readlines() ) + except IOError: + pass + dirs = [] + cntLines = 0 + for fname in os.listdir(path): + if fname != '.git' and os.path.isdir(path+'/'+fname): + subdir = parseDir(path+'/'+fname) + if optStop == False: + dirs.append(subdir) + (dummy0, dummy1, dummy2, subLines, dummy4) = subdir + cntLines += subLines + + if os.path.isfile(path+'/'+fname) \ + and not os.path.islink(path+'/'+fname): + cntLines += countLines(path+'/'+fname) + + return path, short, long, cntLines, dirs + + +### ##### PRINT AS TEXT +### +### def printTextSub(path,indent,withLongDesc): +### short, long, dirs, loc = parseDir(path) +### if short == None: +### p = re.sub(r"^\./", '', path) +### print '%s%s -- ' % (indent, p) +### else: +### p = re.sub(r"^\./", '', path) +### print '%s%s -- %s' % (indent, p, short) +### if withLongDesc: +### if long != None: +### print '' +### for line in long: +### print '%s%s' % (indent+' ',line) +### print '' +### for dir in dirs: +### printTextSub(path+'/'+dir, indent+' ', withLongDesc) +### +### def printText(path,withLongDesc): +### printTextSub(path,'',withLongDesc) +### +### def printTextWoMain(path,withLongDesc): +### short, long, dirs, loc = parseDir(path) +### for dir in dirs: +### printTextSub(path+'/'+dir, '', withLongDesc) +### + +##### PRINT AS a sort of CSV delimited by '|' + +# indent is a number (0..) +def printTabSub(tree,indent): + path, short, long, loc, subdirs = tree + p = re.sub(r"^\./", '', path) + m = re.search(r"/([^/]*$)", p) + if m != None: p = m.groups()[0] + if short == None: + print '%s%s|%d|' % (" "*indent, p, loc) + else: + print '%s%s|%d|%s' % (" "*indent, p, loc, short) + for dir in subdirs: + printTabSub(dir, indent+1) + +def printTab(tree): + printTabSub(tree,0) + +def printTabWoMain(tree): + path, short, long, loc, dirs = tree + for dir in dirs: + printTabSub(dir, 0) + + +##### MAIN + +tree = parseDir('.') +printTabWoMain(tree) + diff --git a/doc/DESCRIPTION b/doc/DESCRIPTION new file mode 100644 index 0000000..c3f01bd --- /dev/null +++ b/doc/DESCRIPTION @@ -0,0 +1 @@ +Documentation diff --git a/doc/doxyfile b/doc/doxyfile new file mode 100644 index 0000000..f058b7a --- /dev/null +++ b/doc/doxyfile @@ -0,0 +1,1600 @@ +# Doxyfile 1.6.2 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = DPL + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 1.0 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = . + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = YES + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it parses. +# With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this tag. +# The format is ext=language, where ext is a file extension, and language is one of +# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, +# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. Note that for custom extensions you also need to set +# FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ../core ../detail + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.vhd \ + *.vhdl + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = NO + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = YES + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's +# filter section matches. +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvances is that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = NO + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = gif + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/doc/dpl_programming_guide.docx b/doc/dpl_programming_guide.docx new file mode 100644 index 0000000000000000000000000000000000000000..f8e11e9c2f155f1e126cfb173f46cd468925e409 GIT binary patch literal 250036 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 z

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≤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

fGutGhNXWn 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 z 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

Yx@Tl3P+T`Q5{$o^i3Mx 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 + + AF:90:29:D2:B2:E1:6F:D6:7E:7E:EC:8E:BE:74:FA:4C:00:9C:49:FE + A6:00:BC:53:AC:37:5B:6A:03:C3:7A:8A:E0:1B:87:8B:82:94:9B:C2 + C2:C4:B5:72:9A:CF:D9:72:C5:DE:C1:E1:30:FF:74:7F:7A:AF:27:12 + + + AF:90:29:D2:B2:E1:6F:D6:7E:7E:EC:8E:BE:74:FA:4C:00:9C:49:FE + C2:C4:B5:72:9A:CF:D9:72:C5:DE:C1:E1:30:FF:74:7F:7A:AF:27:12 + A0:59:D3:37:E8:C8:2E:7F:38:84:7D:21:A9:9E:19:A9:8E:EC:EB:E1 + 8D:1F:CB:31:68:11:DA:22:59:26:58:13:6C:C6:72:C9:F0:DE:84:2A + + + 4A:9D:7A:4B:3B:29:D4:69:0A:70:B3:80:EC:A9:44:6B:03:7C:9A:38 + + + + + AD:A1:44:89:6A:35:6D:17:01:E9:6F:46:C6:00:7B:78:BE:2E:D9:4E + + diff --git a/etc/fingerprint_list.xsd b/etc/fingerprint_list.xsd new file mode 100644 index 0000000..b0fab23 --- /dev/null +++ b/etc/fingerprint_list.xsd @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/etc/schema.xsd b/etc/schema.xsd new file mode 100644 index 0000000..8028f3e --- /dev/null +++ b/etc/schema.xsd @@ -0,0 +1,415 @@ + + + + + + ]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/etc/wrt_create_clean_db.sh b/etc/wrt_create_clean_db.sh new file mode 100755 index 0000000..db450a3 --- /dev/null +++ b/etc/wrt_create_clean_db.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +for name in wrt ace vcore +do + rm -f /opt/dbspace/.$name.db + rm -f /opt/dbspace/.$name.db-journal + SQL="PRAGMA journal_mode = PERSIST;" + sqlite3 /opt/dbspace/.$name.db "$SQL" + SQL=".read /usr/share/wrt-engine/"$name"_db.sql" + sqlite3 /opt/dbspace/.$name.db "$SQL" + touch /opt/dbspace/.$name.db-journal + chown root:6026 /opt/dbspace/.$name.db + chown root:6026 /opt/dbspace/.$name.db-journal + chmod 660 /opt/dbspace/.$name.db + chmod 660 /opt/dbspace/.$name.db-journal +done + + diff --git a/etc/wrt_reset_db.sh b/etc/wrt_reset_db.sh new file mode 100755 index 0000000..42afbe3 --- /dev/null +++ b/etc/wrt_reset_db.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +wrt_create_clean_db.sh + +rm -rf /opt/apps/widget/system/* + +# This directories contains test widgets and test keys. It shouldn't be removed. +#rm -rf /opt/apps/widget/user/* +#rm -rf /opt/apps/widget/data/* + +#Removing of widget desktop icons +WIDGET_EXEC_PATH=/opt/apps/widget/exec +WIDGET_DESKTOP_PATH=/opt/share/install-info/application +WIDGET_ICON_PATH=/opt/share/icons/default/small +WIDGET_EXECS="${WIDGET_EXEC_PATH}/*"; + +for file in $WIDGET_EXECS; do + widget_id=${file#${WIDGET_EXEC_PATH}/}; + + widget_desktop_file="${WIDGET_DESKTOP_PATH}/org.tizen.${widget_id}.desktop"; + if [ -f ${widget_desktop_file} ]; then + echo "rm -f $widget_desktop_file"; + rm -f $widget_desktop_file; + fi + + widget_icon_file="${WIDGET_ICON_PATH}/${widget_id}.*" + if [ -f ${widget_icon_file} ]; then + echo "rm -f $widget_icon_file"; + rm -f $widget_icon_file; + fi +done + +rm -rf /opt/apps/widget/exec/* +touch /opt/apps/widget/plugin-installation-required + diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..a5546f4 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# + +# Check minimum CMake version +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +# Project name +PROJECT(dpl-examples) + +# Set common compiler flags +ADD_DEFINITIONS("-Wall -Wextra -ansi -pedantic") + +# Add examples +ADD_SUBDIRECTORY(simple) +ADD_SUBDIRECTORY(rpc) +ADD_SUBDIRECTORY(fake_rpc) +ADD_SUBDIRECTORY(binary_queue) +ADD_SUBDIRECTORY(socket) +ADD_SUBDIRECTORY(tcpsock) +ADD_SUBDIRECTORY(timed_event) +ADD_SUBDIRECTORY(single_instance) +ADD_SUBDIRECTORY(crypto_hash) +ADD_SUBDIRECTORY(event_delivery_test) +ADD_SUBDIRECTORY(metronome) +ADD_SUBDIRECTORY(copy) diff --git a/examples/DESCRIPTION b/examples/DESCRIPTION new file mode 100644 index 0000000..db99a6d --- /dev/null +++ b/examples/DESCRIPTION @@ -0,0 +1,2 @@ +!!!options!!! stop +Example code diff --git a/examples/binary_queue/CMakeLists.txt b/examples/binary_queue/CMakeLists.txt new file mode 100644 index 0000000..5fb9fee --- /dev/null +++ b/examples/binary_queue/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(BINARY_QUEUE_SYS dpl-gtk REQUIRED) + +SET(BINARY_QUEUE_SOURCES + binary_queue.cpp) + +ADD_DEFINITIONS("-D_DEBUG") + +INCLUDE_DIRECTORIES(${BINARY_QUEUE_SYS_INCLUDE_DIRS}) +LINK_DIRECTORIES(${BINARY_QUEUE_SYS_LIBRARY_DIRS}) + +ADD_EXECUTABLE(binary_queue ${BINARY_QUEUE_SOURCES}) +TARGET_LINK_LIBRARIES(binary_queue ${BINARY_QUEUE_SYS_LIBRARIES}) diff --git a/examples/binary_queue/binary_queue.cpp b/examples/binary_queue/binary_queue.cpp new file mode 100644 index 0000000..a978a6d --- /dev/null +++ b/examples/binary_queue/binary_queue.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file binary_queue.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of binary queue example + */ +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + + DPL::BinaryQueue queue; + Assert(queue.Size() == 0); + + for (int i=0; i<10; ++i) + { + int value = 12345; + queue.AppendCopy(&value, sizeof(value)); + queue.AppendUnmanaged(malloc(100), 100); + } + + Assert(queue.Size() == 10 * sizeof(int) + 10 * 100); + + for (size_t i = 0; i < 10 * sizeof(int) + 10 * 100; ++i) + { + char buffer[1]; + queue.Flatten(buffer, 1); + + queue.FlattenConsume(NULL, 0); + queue.Flatten(NULL, 0); + queue.FlattenConsume(buffer, 1); + } + +// UNHANDLED_EXCEPTION_HANDLER_BEGIN +// { +// char a; +// queue.FlattenConsume(&a, sizeof(a)); +// } +// UNHANDLED_EXCEPTION_HANDLER_END + + return 0; +} diff --git a/examples/copy/CMakeLists.txt b/examples/copy/CMakeLists.txt new file mode 100644 index 0000000..ace88df --- /dev/null +++ b/examples/copy/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(COPY_SYS dpl-gtk REQUIRED) + +SET(COPY_SOURCES + copy.cpp) + +ADD_DEFINITIONS("-D_DEBUG") + +INCLUDE_DIRECTORIES(${COPY_SYS_INCLUDE_DIRS}) +LINK_DIRECTORIES(${COPY_SYS_LIBRARY_DIRS}) + +ADD_EXECUTABLE(copy ${COPY_SOURCES}) +TARGET_LINK_LIBRARIES(copy ${COPY_SYS_LIBRARIES}) diff --git a/examples/copy/copy.cpp b/examples/copy/copy.cpp new file mode 100644 index 0000000..1ab7607 --- /dev/null +++ b/examples/copy/copy.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file copy.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of copy example + */ +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc != 3 && argc != 4) + { + std::cout << "Invalid parameters: copy [input_file] [output_file] [OPTIONAL: number_of_bytes]" << std::endl; + return -1; + } + UNHANDLED_EXCEPTION_HANDLER_BEGIN + { + DPL::FileInput input(argv[1]); + DPL::FileOutput output(argv[2]); + + if (argc == 3) + DPL::Copy(&input, &output); + else + DPL::Copy(&input, &output, static_cast(atoi(argv[3]))); + } + UNHANDLED_EXCEPTION_HANDLER_END + + return 0; +} diff --git a/examples/crypto_hash/CMakeLists.txt b/examples/crypto_hash/CMakeLists.txt new file mode 100644 index 0000000..e0b0598 --- /dev/null +++ b/examples/crypto_hash/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(CRYPTO_HASH_SYS dpl-efl REQUIRED) + +SET(CRYPTO_HASH_SOURCES + crypto_hash.cpp) + +ADD_DEFINITIONS("-D_DEBUG") + +INCLUDE_DIRECTORIES(${CRYPTO_HASH_SYS_INCLUDE_DIRS}) +LINK_DIRECTORIES(${CRYPTO_HASH_SYS_LIBRARY_DIRS}) + +ADD_EXECUTABLE(crypto_hash ${CRYPTO_HASH_SOURCES}) +TARGET_LINK_LIBRARIES(crypto_hash ${CRYPTO_HASH_SYS_LIBRARIES}) diff --git a/examples/crypto_hash/crypto_hash.cpp b/examples/crypto_hash/crypto_hash.cpp new file mode 100644 index 0000000..0fffefa --- /dev/null +++ b/examples/crypto_hash/crypto_hash.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file crypto_hash.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of crypto hash example + */ +#include +#include +#include + +void help() +{ + std::cout << "Invalid parameters: crypto_hash [hash_function] [message]" << std::endl; + std::cout << "hash_function is one of following: MD2, MD4, MD5, SHA, SHA1, DSS, DSS1, ECDSA, SHA224, SHA256, SHA384, SHA512" << std::endl; +} + +int main(int argc, char *argv[]) +{ + if (argc != 3) + { + help(); + return 0; + } + + DPL::Crypto::Hash::Base *crypto; + std::string algorithm = argv[1]; + + if (algorithm == "MD2") + crypto = new DPL::Crypto::Hash::MD2(); + else if (algorithm == "MD4") + crypto = new DPL::Crypto::Hash::MD4(); + else if (algorithm == "MD5") + crypto = new DPL::Crypto::Hash::MD5(); + else if (algorithm == "SHA") + crypto = new DPL::Crypto::Hash::SHA(); + else if (algorithm == "SHA1") + crypto = new DPL::Crypto::Hash::SHA1(); + else if (algorithm == "DSS") + crypto = new DPL::Crypto::Hash::DSS(); + else if (algorithm == "DSS1") + crypto = new DPL::Crypto::Hash::DSS1(); + else if (algorithm == "ECDSA") + crypto = new DPL::Crypto::Hash::ECDSA(); + else if (algorithm == "SHA224") + crypto = new DPL::Crypto::Hash::SHA224(); + else if (algorithm == "SHA256") + crypto = new DPL::Crypto::Hash::SHA256(); + else if (algorithm == "SHA384") + crypto = new DPL::Crypto::Hash::SHA384(); + else if (algorithm == "SHA512") + crypto = new DPL::Crypto::Hash::SHA512(); + else + { + help(); + return 0; + } + + crypto->Append(argv[2]); + crypto->Finish(); + + std::cout << crypto->ToString() << std::endl; + + delete crypto; + + return 0; +} diff --git a/examples/dbus/client-example/CMakeLists.txt b/examples/dbus/client-example/CMakeLists.txt new file mode 100644 index 0000000..1f9241f --- /dev/null +++ b/examples/dbus/client-example/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 2.6) +project(client-example) + +include(FindPkgConfig) + +pkg_check_modules(DEPS + dbus-1 + dpl-efl + dpl-dbus-efl + REQUIRED) + +set(TARGET_NAME "client-example") + +set(SRCS + client-example.cpp) + +include_directories(${DEPS_INCLUDE_DIRS}) + +add_definitions("-std=c++0x") +add_definitions("-DDPL_LOGS_ENABLED") + +add_executable(${TARGET_NAME} ${SRCS}) +target_link_libraries(${TARGET_NAME} ${DEPS_LIBRARIES}) + diff --git a/examples/dbus/client-example/client-example.cpp b/examples/dbus/client-example/client-example.cpp new file mode 100644 index 0000000..964333e --- /dev/null +++ b/examples/dbus/client-example/client-example.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file client-example.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 1.0 + * @brief Implementation for simple echo service DBus client. + */ + +#include +#include + +#include + +int main(int argc, char **argv) +{ + DPL::DBus::Client client("/path/to/object", + "org.tizen.EchoService", + "org.tizen.EchoInterface"); + std::string outstr; + client.Call("echo", "Samsung Rocks! Hello World Test!", &outstr); + std::cout << "Returned: " << outstr << std::endl; + exit(0); +} + diff --git a/examples/dbus/server-example/CMakeLists.txt b/examples/dbus/server-example/CMakeLists.txt new file mode 100644 index 0000000..34266e3 --- /dev/null +++ b/examples/dbus/server-example/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 2.6) +project(server-example) + +include(FindPkgConfig) + +pkg_check_modules(DEPS + glib-2.0 + gio-2.0 + dpl-efl + dpl-dbus-efl + REQUIRED) + +set(TARGET_NAME "server-example") + +set(SRCS + server-example.cpp) + +include_directories(${DEPS_INCLUDE_DIRS}) + +add_definitions("-std=c++0x") +add_definitions("-pedantic") +add_definitions("-Wall") +add_definitions("-DDPL_LOGS_ENABLED") + +add_executable(${TARGET_NAME} ${SRCS}) +target_link_libraries(${TARGET_NAME} ${DEPS_LIBRARIES}) \ No newline at end of file diff --git a/examples/dbus/server-example/server-example.cpp b/examples/dbus/server-example/server-example.cpp new file mode 100644 index 0000000..3a54d2a --- /dev/null +++ b/examples/dbus/server-example/server-example.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +std::string xml = +"" +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +""; + +GMainLoop* g_loop = NULL; + +auto g_interfaces = DPL::DBus::Interface::fromXMLString(xml); + +class DBusDispatcher : public DPL::DBus::Dispatcher +{ +public: + void onMethodCall(GDBusConnection *connection, + const gchar *sender, + const gchar *objectPath, + const gchar *interfaceName, + const gchar *methodName, + GVariant *parameters, + GDBusMethodInvocation *invocation) + { + LogInfo("On method call: " << methodName); + + if (g_strcmp0(methodName, "echo") == 0) + { + const gchar* arg = NULL; + + g_variant_get(parameters, "(&s)", &arg); + LogInfo("Client said: " << arg); + + gchar* response = g_strdup_printf(arg); + g_dbus_method_invocation_return_value(invocation, + g_variant_new("(s)", + response)); + g_free (response); + sleep(5); + } + else if (g_strcmp0(methodName, "quit") == 0) + { + g_main_loop_quit(g_loop); + } + } +}; + +class NewConnectionListener : + public DPL::EventListener +{ +protected: + void OnEventReceived(const DPL::DBus::ServerEvents::NewConnectionEvent& event) + { + m_connection = event.GetArg0(); + + auto quitInterface = g_interfaces.at(1); + quitInterface->setDispatcher(std::make_shared()); + m_quitObject = DBus::Object::create("/object/quit", quitInterface); + + m_connection->registerObject(m_quitObject); + } + +private: + DPL::DBus::ConnectionPtr m_connection; + DPL::DBus::ObjectPtr m_quitObject; +}; + +int main() +{ + g_type_init(); + + // --------------- echo + auto echoConnection = DPL::DBus::Connection::sessionBus(); + + auto echoInterface = g_interfaces.at(0); + echoInterface->setDispatcher(std::make_shared()); + + auto echoObject = DPL::DBus::Object::create("/object/echo", echoInterface); + + echoConnection->registerObject(echoObject); + + echoConnection->registerService("org.tizen.EchoService"); + + // --------------- quit + std::unique_ptr listener(new NewConnectionListener); + auto server = DPL::DBus::Server::create("unix:abstract=someaddr"); + server->AddListener(listener.get()); + server->start(); + + g_loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(g_loop); + g_main_loop_unref(g_loop); + + return 0; +} diff --git a/examples/event_delivery_test/CMakeLists.txt b/examples/event_delivery_test/CMakeLists.txt new file mode 100644 index 0000000..1734a80 --- /dev/null +++ b/examples/event_delivery_test/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Jaroslaw Osmanski j.osmanski@samsung.com +# @version 1.0 +# @brief +# +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(DPL_GTK "dpl-gtk" "gthread-2.0" REQUIRED) + +SET(EVENT_DELIVERY_SOURCES + event_delivery_test.cpp) + +ADD_DEFINITIONS("-D_DEBUG -g") + +INCLUDE_DIRECTORIES(${DPL_GTK_INCLUDE_DIRS}) +LINK_DIRECTORIES(${DPL_GTK_LIBRARY_DIRS}) + +ADD_EXECUTABLE(event_delivery_test ${EVENT_DELIVERY_SOURCES}) +TARGET_LINK_LIBRARIES(event_delivery_test ${DPL_GTK_LIBRARIES}) + + diff --git a/examples/event_delivery_test/event_delivery_test.cpp b/examples/event_delivery_test/event_delivery_test.cpp new file mode 100644 index 0000000..5f0f104 --- /dev/null +++ b/examples/event_delivery_test/event_delivery_test.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file event_delivery_server.h + * @author Jaroslaw Osmanski j.osmanski@samsung.com + * @version 1.0 + * @brief This file is the implementation file of simple event delivery test + */ +#include +#include +#include +#include +#include +#include +#include + +namespace TestEvents +{ +EVENT_DELIVERY_DECLARE_EVENT_MESSAGE_0(EmptyMessage) +EVENT_DELIVERY_DECLARE_EVENT_MESSAGE_2(HelloWorldMessage, std::string, std::string) +EVENT_DELIVERY_DECLARE_EVENT_MESSAGE_2(NumberMessage, int, float) +EVENT_DELIVERY_DECLARE_EVENT_MESSAGE_4(TestMessage, int, int, int, int) + +EVENT_DELIVERY_IMPLEMENT_EVENT_MESSAGE(EmptyMessage) +EVENT_DELIVERY_IMPLEMENT_EVENT_MESSAGE(HelloWorldMessage) +EVENT_DELIVERY_IMPLEMENT_EVENT_MESSAGE(NumberMessage) +EVENT_DELIVERY_IMPLEMENT_EVENT_MESSAGE(TestMessage) +} // namespace TestEvents + +DECLARE_GENERIC_EVENT_0(StartEvent) +DECLARE_GENERIC_EVENT_0(CloseEvent) +DECLARE_GENERIC_EVENT_0(DeleteEvent) + +class MyListener : + public DPL::Application, + private DPL::Controller::Type>, + public DPL::EventListener, + public DPL::EventListener +{ +private: + class OneTimeListener : + public DPL::EventListener + { + public: + OneTimeListener() + { + DPL::EventDeliverySystem::AddListener (this); + } + + ~OneTimeListener() + { + LogError("Deleting OneTimeListener"); + DPL::EventDeliverySystem::RemoveListener (this); + } + + private: + void OnEventReceived(const TestEvents::EmptyMessage &message) + { + (void) message; + std::cout << "OneTimeListener empty message"; + + } + }; + + OneTimeListener * oneTimeListener; + + virtual void OnEventReceived(const CloseEvent &event) + { + (void) event; + Quit(); + } + + virtual void OnEventReceived(const DeleteEvent &event) + { + (void) event; + delete oneTimeListener; + oneTimeListener = NULL; + } + +protected: + + void OnEventReceived(const TestEvents::HelloWorldMessage &message) + { + std::cout << "Got HelloWorldMessage: " << message.GetArg0() << " : " << message.GetArg1() << std::endl; + + if (oneTimeListener != NULL) + { + DPL::ControllerEventHandler::PostTimedEvent(DeleteEvent(), 1); + } + } + + void OnEventReceived(const TestEvents::NumberMessage &message) + { + std::cout << "Got NumberMessage: " << message.GetArg0() << ", " << message.GetArg1() << std::endl; + } + +public: + + MyListener(int argc, char **argv) + : Application(argc, argv, "Listener") + { + Touch(); + + DPL::EventDeliverySystem::AddListener(this); + DPL::EventDeliverySystem::AddListener(this); + + DPL::ControllerEventHandler::PostTimedEvent(CloseEvent(), 8); + oneTimeListener = new OneTimeListener(); + } + + virtual ~MyListener() + { + DPL::EventDeliverySystem::RemoveListener (this); + DPL::EventDeliverySystem::RemoveListener (this); + delete oneTimeListener; + } +}; + +class MyPusher : + public DPL::Application, + private DPL::Controller::Type> +{ +private: + virtual void OnEventReceived(const CloseEvent &event) + { + (void) event; + Quit(); + } + + virtual void OnEventReceived(const StartEvent &event) + { + (void) event; + std::cout << "Publishing HelloWorldMessage..." << std::endl; + TestEvents::HelloWorldMessage hello("Hello cruel world !", "AAA BBB CCC"); + DPL::EventDeliverySystem::Publish(hello); + std::cout << "HelloWorldMessage published." << std::endl; + + std::cout << "Publishing NumberMessage message..." << std::endl; + TestEvents::NumberMessage number(13, 3.14f); + DPL::EventDeliverySystem::Publish(number); + std::cout << "NumberMessage published." << std::endl; + + std::cout << "Publishing EmptyMessage..." << std::endl; + TestEvents::EmptyMessage empty; + DPL::EventDeliverySystem::Publish(empty); + std::cout << "EmptyMessage published." << std::endl; + + } +public: + MyPusher(int argc, char **argv) + : Application(argc, argv, "Pusher") + { + Touch(); + + DPL::ControllerEventHandler::PostTimedEvent(StartEvent(), 2); + DPL::ControllerEventHandler::PostTimedEvent(StartEvent(), 6); + DPL::ControllerEventHandler::PostTimedEvent(CloseEvent(), 8); + } + + virtual ~MyPusher() + { + } +}; + +int main(int argc, char* argv[]) +{ + switch (fork()) + { + case 0: + { + MyPusher myPusher(argc, argv); + myPusher.Exec(); + } + break; + + case -1: + printf("fork() failed!"); + break; + + default: + { + MyListener myListener(argc, argv); + myListener.Exec(); + } + break; + } + + return 0; +} diff --git a/examples/fake_rpc/CMakeLists.txt b/examples/fake_rpc/CMakeLists.txt new file mode 100644 index 0000000..0778da7 --- /dev/null +++ b/examples/fake_rpc/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Pawel Sikorski (p.sikorski@samsung.com) +# @version 1.0 +# @brief +# +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(FAKE_RPC_SYS dpl-efl REQUIRED) + +SET(FAKE_RPC_SOURCES + fake_rpc.cpp) + +ADD_DEFINITIONS("-D_DEBUG") +ADD_DEFINITIONS("-DDPL_LOGS_ENABLED") + +INCLUDE_DIRECTORIES(${FAKE_RPC_SYS_INCLUDE_DIRS}) +LINK_DIRECTORIES(${FAKE_RPC_SYS_LIBRARY_DIRS}) + +ADD_EXECUTABLE(fake_rpc ${FAKE_RPC_SOURCES}) +TARGET_LINK_LIBRARIES(fake_rpc ${FAKE_RPC_SYS_LIBRARIES}) diff --git a/examples/fake_rpc/fake_rpc.cpp b/examples/fake_rpc/fake_rpc.cpp new file mode 100644 index 0000000..9750c62 --- /dev/null +++ b/examples/fake_rpc/fake_rpc.cpp @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file rpc.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of RPC example + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// FLOW: +// 1st connection - UnixSockets +// 2nd connection - Fake +// client sends data via fake IPC +// server sends back this data via unix IPC. +// client compare sent and received data + +static const char *UNIX_RPC_NAME = "/tmp/unix_socket_rpc"; +static const char *FAKE_RPC_NAME = "/tmp/fake_rpc"; + +typedef DPL::AbstractRPCConnectionEvents::AsyncCallEvent AsyncCallEvent; +typedef DPL::AbstractRPCConnectionEvents::ConnectionClosedEvent + ConnectionClosedEvent; +typedef DPL::AbstractRPCConnectionEvents::ConnectionBrokenEvent + ConnectionBrokenEvent; +typedef DPL::AbstractRPCConnectorEvents::ConnectionEstablishedEvent + ConnectionEstablishedEvent; + +class MyThread + : public DPL::Thread, + private DPL::EventListener, + private DPL::EventListener +{ +private: + DPL::UnixSocketRPCClient m_rpcUnixClient; + DPL::FakeRpcClient m_rpcFakeClient; + int m_connections; + int m_sentData; + int m_receivedData; + + DPL::ScopedPtr m_rpcUnixConnection; + DPL::ScopedPtr m_rpcFakeConnection; + + virtual void OnEventReceived(const AsyncCallEvent &event) + { + LogInfo("CLIENT: AsyncCallEvent received"); + + event.GetArg0().ConsumeArg(m_receivedData); + LogInfo("CLIENT: Result from server: " << m_receivedData); + + if (m_receivedData != m_sentData) + LogError("Wrong data Received!"); + } + + virtual void OnEventReceived(const ConnectionEstablishedEvent &event) + { + if (dynamic_cast(event.GetArg1())){ + ++m_connections; + LogInfo("CLIENT: Acquiring new fake connection"); + m_rpcFakeConnection.Reset(event.GetArg1()); + //this is not used on this side +// m_rpcFakeConnection->DPL::EventSupport::AddListener(this); + } + else{ + ++m_connections; + LogInfo("CLIENT: Acquiring new unix connection"); + m_rpcUnixConnection.Reset(event.GetArg1()); + m_rpcUnixConnection->DPL::EventSupport::AddListener(this); + } + if(m_connections == 2){ + m_sentData = 1213; + // Emit RPC function call + DPL::RPCFunction proc; + proc.AppendArg(m_sentData); + LogInfo("CLIENT: Calling RPC function"); + m_rpcFakeConnection->AsyncCall(proc); + } + } + +public: + virtual ~MyThread() + { + // Always quit thread + Quit(); + } + + virtual int ThreadEntry() + { + m_connections = 0; + // Attach RPC listeners + LogInfo("CLIENT: Attaching connection established event"); + m_rpcUnixClient.DPL::EventSupport::AddListener(this); + m_rpcFakeClient.DPL::EventSupport::AddListener(this); + + // Open connection to server + LogInfo("CLIENT: Opening connection to RPC"); + m_rpcUnixClient.Open(UNIX_RPC_NAME); + m_rpcFakeClient.Open(FAKE_RPC_NAME); + + // Start message loop + LogInfo("CLIENT: Starting thread event loop"); + int ret = Exec(); + + if (m_rpcUnixConnection.Get()){ + LogInfo("CLIENT: Removing Unix connection"); + m_rpcUnixConnection->DPL::EventSupport::RemoveListener(this); + m_rpcUnixConnection.Reset(); + } + + LogInfo("CLIENT: Closing"); + + if (m_rpcFakeConnection.Get()){ + LogInfo("CLIENT: Removing Fake connection"); + //this is not used on this side +// m_rpcFakeConnection->DPL::EventSupport::RemoveListener(this); + m_rpcFakeConnection.Reset(); + } + + // Detach RPC client listener + LogInfo("CLIENT: Detaching connection established event"); + m_rpcUnixClient.DPL::EventSupport::RemoveListener(this); + m_rpcFakeClient.DPL::EventSupport::RemoveListener(this); + + // Close RPC + LogInfo("CLIENT: Closing RPC client"); + m_rpcUnixClient.CloseAll(); + m_rpcFakeClient.Close();//not needed + + // Done + return ret; + } +}; + +DECLARE_GENERIC_EVENT_0(QuitEvent) +DECLARE_GENERIC_EVENT_0(CloseThreadEvent) + +class MyApplication + : public DPL::Application, + private DPL::Controller::Type>, + private DPL::EventListener, + private DPL::EventListener +{ +private: + DPL::UnixSocketRPCServer m_rpcUnixServer; + DPL::FakeRpcServer m_rpcFakeServer; + + DPL::ScopedPtr m_rpcUnixConnection; + DPL::ScopedPtr m_rpcFakeConnection; + + MyThread m_thread; + + // Quit application event occurred + virtual void OnEventReceived(const QuitEvent &/*event*/){ + // Close RPC now + LogInfo("SERVER: Closing RPC connection..."); + + // Detach RPC connection listeners + if (m_rpcUnixConnection.Get()) { + //this is not used on this side +// m_rpcUnixConnection->DPL::EventSupport::RemoveListener(this); + m_rpcUnixConnection.Reset(); + } + + if (m_rpcFakeConnection.Get()) { + m_rpcFakeConnection->DPL::EventSupport::RemoveListener(this); + m_rpcFakeConnection.Reset(); + } + + LogInfo("SERVER: Closing Server"); + m_rpcUnixServer.CloseAll(); + m_rpcFakeServer.CloseAll();//not needed + m_rpcUnixServer.DPL::EventSupport::RemoveListener(this); + m_rpcFakeServer.DPL::EventSupport::RemoveListener(this); + + LogInfo("SERVER: Server closed"); + + Quit(); + } + + virtual void OnEventReceived(const CloseThreadEvent &/*event*/){ + m_thread.Quit(); + } + + virtual void OnEventReceived(const AsyncCallEvent &event) + { + LogInfo("SERVER: AsyncCallEvent received"); + + int value; + event.GetArg0().ConsumeArg(value); + LogInfo("SERVER: Result from client: " << value); + + // send back data to client (via fake) + // Emit RPC function call + DPL::RPCFunction proc; + proc.AppendArg(value); + LogInfo("SERVER: Calling RPC function"); + m_rpcUnixConnection->AsyncCall(proc); + } + + virtual void OnEventReceived(const ConnectionEstablishedEvent &event) + { + // Save connection pointer + if (dynamic_cast(event.GetArg1())){ + LogInfo("SERVER: Acquiring Fake RPC connection"); + m_rpcFakeConnection.Reset(event.GetArg1()); + m_rpcFakeConnection->DPL::EventSupport::AddListener(this); + } + else{ + LogInfo("SERVER: Acquiring Unix RPC connection"); + m_rpcUnixConnection.Reset(event.GetArg1()); + //this is not used on this side +// m_rpcUnixConnection->DPL::EventSupport::AddListener(this); + } + } + +public: + MyApplication(int argc, char **argv) + : Application(argc, argv, "rpc") + { + // Attach RPC server listeners + LogInfo("SERVER: Attaching connection established event"); + m_rpcUnixServer.DPL::EventSupport::AddListener(this); + m_rpcFakeServer.DPL::EventSupport::AddListener(this); + + // Self touch + LogInfo("SERVER: Touching controller"); + Touch(); + + // Open RPC server + LogInfo("SERVER: Opening server RPC"); + m_rpcUnixServer.Open(UNIX_RPC_NAME); + m_rpcFakeServer.Open(FAKE_RPC_NAME); + + // Run RPC client in thread + LogInfo("SERVER: Starting RPC client thread"); + m_thread.Run(); + + // Quit application automatically in few seconds + LogInfo("SERVER: Sending control timed events"); + DPL::ControllerEventHandler::PostTimedEvent(CloseThreadEvent(), 2); + DPL::ControllerEventHandler::PostTimedEvent(QuitEvent(), 3); + } + + virtual ~MyApplication() + { + // Quit thread + LogInfo("SERVER: Quitting thread"); + } +}; + +int main(int argc, char *argv[]) +{ + LogInfo("Starting"); + MyApplication app(argc, argv); + return app.Exec(); +} diff --git a/examples/metronome/CMakeLists.txt b/examples/metronome/CMakeLists.txt new file mode 100644 index 0000000..b5d1017 --- /dev/null +++ b/examples/metronome/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(METRONOME_SYS dpl-efl REQUIRED) + +SET(METRONOME_CLIENT_SOURCES + metronome_client.cpp) + +SET(METRONOME_SERVER_SOURCES + metronome_server.cpp) + +ADD_DEFINITIONS("-D_DEBUG") + +INCLUDE_DIRECTORIES(${METRONOME_SYS_INCLUDE_DIRS}) +LINK_DIRECTORIES(${METRONOME_SYS_LIBRARY_DIRS}) + +ADD_EXECUTABLE(metronome_client ${METRONOME_CLIENT_SOURCES}) +TARGET_LINK_LIBRARIES(metronome_client ${METRONOME_SYS_LIBRARIES}) + +ADD_EXECUTABLE(metronome_server ${METRONOME_SERVER_SOURCES}) +TARGET_LINK_LIBRARIES(metronome_server ${METRONOME_SYS_LIBRARIES}) diff --git a/examples/metronome/metronome_client.cpp b/examples/metronome/metronome_client.cpp new file mode 100644 index 0000000..2411d09 --- /dev/null +++ b/examples/metronome/metronome_client.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file metronome_client.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of metronome client example + */ +#include +#include +#include +#include + +class MetronomeClientApplication + : public DPL::Application, + private DPL::EventListener, + private DPL::EventListener, + private DPL::EventListener, + private DPL::EventListener +{ +private: + DPL::TcpSocketRPCClient m_rpcClient; + DPL::ScopedPtr m_rpcConnection; + + virtual void OnEventReceived(const DPL::AbstractRPCConnectionEvents::AsyncCallEvent &event) + { + (void)event; + + // Heart beat + LogInfo("* Got metronome signal *"); + } + + virtual void OnEventReceived(const DPL::AbstractRPCConnectionEvents::ConnectionClosedEvent &event) + { + (void)event; + + LogInfo("Connection closed"); + + // Must quit + Quit(); + } + + virtual void OnEventReceived(const DPL::AbstractRPCConnectionEvents::ConnectionBrokenEvent &event) + { + (void)event; + + LogInfo("Connection broken"); + + // Must quit + Quit(); + } + + virtual void OnEventReceived(const DPL::AbstractRPCConnectorEvents::ConnectionEstablishedEvent &event) + { + // Save connection pointer + LogInfo("Connected to metronome server"); + m_rpcConnection.Reset(event.GetArg1()); + + // Attach event listeners + m_rpcConnection->DPL::EventSupport::AddListener(this); + m_rpcConnection->DPL::EventSupport::AddListener(this); + m_rpcConnection->DPL::EventSupport::AddListener(this); + } + +public: + MetronomeClientApplication(int argc, char **argv) + : Application(argc, argv, "rpc") + { + // Attach RPC server listeners + m_rpcClient.DPL::EventSupport::AddListener(this); + + // Open RPC server + m_rpcClient.Open("127.0.0.1", 12345); + + // Started + LogInfo("Metronome client started"); + } + + virtual ~MetronomeClientApplication() + { + // Delete all RPC connections + if (m_rpcConnection.Get()) + { + m_rpcConnection->DPL::EventSupport::RemoveListener(this); + m_rpcConnection->DPL::EventSupport::RemoveListener(this); + m_rpcConnection->DPL::EventSupport::RemoveListener(this); + m_rpcConnection.Reset(); + } + + // Close RPC server + m_rpcClient.CloseAll(); + + // Detach RPC server listener + m_rpcClient.DPL::EventSupport::RemoveListener(this); + } +}; + +int main(int argc, char *argv[]) +{ + return MetronomeClientApplication(argc, argv).Exec(); +} diff --git a/examples/metronome/metronome_server.cpp b/examples/metronome/metronome_server.cpp new file mode 100644 index 0000000..7efa458 --- /dev/null +++ b/examples/metronome/metronome_server.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file metronome_server.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of metronome server example + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Metronome signal event +DECLARE_GENERIC_EVENT_0(SignalEvent) +DECLARE_GENERIC_EVENT_1(DeleteConnectionEvent, DPL::AbstractRPCConnection *) + +// Heart beat interval +const double HEART_BEAT_INTERVAL = 1.0; // seconds + +class MetronomeServerApplication + : public DPL::Application, + private DPL::Controller::Type>, + private DPL::EventListener, + private DPL::EventListener, + private DPL::EventListener +{ +private: + DPL::TcpSocketRPCServer m_rpcServer; + + typedef std::list ConnectionList; + ConnectionList m_connections; + + // Matronome signal received + virtual void OnEventReceived(const SignalEvent &event) + { + (void)event; + + // Signal all conection about heart beat + DPL::RPCFunction proc; + proc.AppendArg((int)0); + + for (ConnectionList::iterator it = m_connections.begin(); it != m_connections.end(); ++it) + (*it)->AsyncCall(proc); + + // Continue to emot heart beats + DPL::ControllerEventHandler::PostTimedEvent(SignalEvent(), HEART_BEAT_INTERVAL); + } + + virtual void OnEventReceived(const DeleteConnectionEvent &event) + { + delete event.GetArg0(); + } + + void RemoveConnection(DPL::AbstractRPCConnection *connection) + { + // Find connection + ConnectionList::iterator it = std::find(m_connections.begin(), m_connections.end(), connection); + Assert(it != m_connections.end()); + + // Erase connection + m_connections.erase(it); + + // Detach RPC connection listeners + connection->DPL::EventSupport::RemoveListener(this); + connection->DPL::EventSupport::RemoveListener(this); + + // Delete connection + DPL::ControllerEventHandler::PostEvent(DeleteConnectionEvent(connection)); + } + + void AddConnection(DPL::AbstractRPCConnection *connection) + { + // Add connection + m_connections.push_back(connection); + + // Attach event listeners + connection->DPL::EventSupport::AddListener(this); + connection->DPL::EventSupport::AddListener(this); + } + + virtual void OnEventReceived(const DPL::AbstractRPCConnectionEvents::ConnectionClosedEvent &event) + { + (void)event; + + LogInfo("Connection closed"); + + // Remove connection from list + RemoveConnection(static_cast(event.GetSender())); + } + + virtual void OnEventReceived(const DPL::AbstractRPCConnectionEvents::ConnectionBrokenEvent &event) + { + (void)event; + + LogInfo("Connection broken"); + + // Remove connection from list + RemoveConnection(static_cast(event.GetSender())); + } + + virtual void OnEventReceived(const DPL::AbstractRPCConnectorEvents::ConnectionEstablishedEvent &event) + { + // Save connection pointer + LogInfo("New connection"); + + // Add nre connection to list + AddConnection(event.GetArg1()); + } + +public: + MetronomeServerApplication(int argc, char **argv) + : Application(argc, argv, "rpc") + { + // Attach RPC server listeners + m_rpcServer.DPL::EventSupport::AddListener(this); + + // Inherit calling context + Touch(); + + // Open RPC server + m_rpcServer.Open(12345); + + // Start heart beat + DPL::ControllerEventHandler::PostTimedEvent(SignalEvent(), HEART_BEAT_INTERVAL); + + // Started + LogInfo("Metronome server started"); + } + + virtual ~MetronomeServerApplication() + { + // Delete all RPC connections + while (!m_connections.empty()) + RemoveConnection(m_connections.front()); + + // Close RPC server + m_rpcServer.CloseAll(); + + // Detach RPC server listener + m_rpcServer.DPL::EventSupport::RemoveListener(this); + } +}; + +int main(int argc, char *argv[]) +{ + return MetronomeServerApplication(argc, argv).Exec(); +} diff --git a/examples/rpc/CMakeLists.txt b/examples/rpc/CMakeLists.txt new file mode 100644 index 0000000..d9c7d57 --- /dev/null +++ b/examples/rpc/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(RPC_SYS dpl-efl REQUIRED) + +SET(RPC_SOURCES + rpc.cpp) + +ADD_DEFINITIONS("-D_DEBUG") + +INCLUDE_DIRECTORIES(${RPC_SYS_INCLUDE_DIRS}) +LINK_DIRECTORIES(${RPC_SYS_LIBRARY_DIRS}) + +ADD_EXECUTABLE(rpc ${RPC_SOURCES}) +TARGET_LINK_LIBRARIES(rpc ${RPC_SYS_LIBRARIES}) diff --git a/examples/rpc/rpc.cpp b/examples/rpc/rpc.cpp new file mode 100644 index 0000000..833a20f --- /dev/null +++ b/examples/rpc/rpc.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file rpc.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of RPC example + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *RPC_NAME = "/tmp/unix_socket_rpc"; + +class MyThread + : public DPL::Thread, + private DPL::EventListener, + private DPL::EventListener, + private DPL::EventListener, + private DPL::EventListener +{ +private: + DPL::UnixSocketRPCClient m_rpcClient; + DPL::ScopedPtr m_rpcConnection; + + virtual void OnEventReceived(const DPL::AbstractRPCConnectionEvents::AsyncCallEvent &event) + { + (void)event; + + LogInfo("CLIENT: AsyncCallEvent received"); + + int value; + event.GetArg0().ConsumeArg(value); + LogInfo("CLIENT: Result from server: " << value); + } + + virtual void OnEventReceived(const DPL::AbstractRPCConnectionEvents::ConnectionClosedEvent &event) + { + (void)event; + LogInfo("CLIENT: ConnectionClosedEvent received"); + } + + virtual void OnEventReceived(const DPL::AbstractRPCConnectionEvents::ConnectionBrokenEvent &event) + { + (void)event; + LogInfo("CLIENT: ConnectionBrokenEvent received"); + } + + virtual void OnEventReceived(const DPL::AbstractRPCConnectorEvents::ConnectionEstablishedEvent &event) + { + // Save connection pointer + LogInfo("CLIENT: Acquiring new connection"); + m_rpcConnection.Reset(event.GetArg1()); + + // Attach listener to new connection + LogInfo("CLIENT: Attaching connection event listeners"); + m_rpcConnection->DPL::EventSupport::AddListener(this); + m_rpcConnection->DPL::EventSupport::AddListener(this); + m_rpcConnection->DPL::EventSupport::AddListener(this); + + LogInfo("CLIENT: Connection established"); + + // Emit RPC function call + DPL::RPCFunction proc; + proc.AppendArg((int)1111); + LogInfo("CLIENT: Calling RPC function"); + m_rpcConnection->AsyncCall(proc); + } + +public: + virtual ~MyThread() + { + // Always quit thread + Quit(); + } + + virtual int ThreadEntry() + { + // Attach RPC listeners + LogInfo("CLIENT: Attaching connection established event"); + m_rpcClient.DPL::EventSupport::AddListener(this); + + // Open connection to server + LogInfo("CLIENT: Opening connection to RPC"); + m_rpcClient.Open(RPC_NAME); + + // Start message loop + LogInfo("CLIENT: Starting thread event loop"); + int ret = Exec(); + + // Detach RPC listeners + if (m_rpcConnection.Get()) + { + LogInfo("CLIENT: Detaching RPC connection events"); + m_rpcConnection->DPL::EventSupport::RemoveListener(this); + m_rpcConnection->DPL::EventSupport::RemoveListener(this); + m_rpcConnection->DPL::EventSupport::RemoveListener(this); + + LogInfo("CLIENT: Resetting connection"); + m_rpcConnection.Reset(); + } + + // Detach RPC client listener + LogInfo("CLIENT: Detaching connection established event"); + m_rpcClient.DPL::EventSupport::RemoveListener(this); + + // Close RPC + LogInfo("CLIENT: Closing RPC client"); + m_rpcClient.CloseAll(); + + // Done + return ret; + } +}; + +DECLARE_GENERIC_EVENT_0(QuitEvent) +DECLARE_GENERIC_EVENT_0(CloseThreadEvent) + +class MyApplication + : public DPL::Application, + private DPL::Controller::Type>, + private DPL::EventListener, + private DPL::EventListener, + private DPL::EventListener, + private DPL::EventListener +{ +private: + DPL::UnixSocketRPCServer m_rpcServer; + DPL::ScopedPtr m_rpcConnection; + + MyThread m_thread; + + // Quit application event occurred + virtual void OnEventReceived(const QuitEvent &event) + { + (void)event; + Quit(); + } + + virtual void OnEventReceived(const CloseThreadEvent &event) + { + (void)event; + m_thread.Quit(); + } + + virtual void OnEventReceived(const DPL::AbstractRPCConnectionEvents::AsyncCallEvent &event) + { + (void)event; + + LogInfo("SERVER: AsyncCallEvent received"); + + int value; + event.GetArg0().ConsumeArg(value); + LogInfo("SERVER: Result from client: " << value); + } + + virtual void OnEventReceived(const DPL::AbstractRPCConnectionEvents::ConnectionClosedEvent &event) + { + (void)event; + + LogInfo("SERVER: ConnectionClosedEvent received"); + + // Close RPC now + LogInfo("SERVER: Closing RPC connection on event..."); + + // Detach RPC connection listeners + if (m_rpcConnection.Get()) + { + LogInfo("SERVER: Detaching connection events"); + m_rpcConnection->DPL::EventSupport::RemoveListener(this); + m_rpcConnection->DPL::EventSupport::RemoveListener(this); + m_rpcConnection->DPL::EventSupport::RemoveListener(this); + } + LogInfo("SERVER: RPC connection closed"); + + LogInfo("SERVER: Closing RPC on event..."); + m_rpcServer.CloseAll(); + LogInfo("SERVER: RPC closed"); + } + + virtual void OnEventReceived(const DPL::AbstractRPCConnectionEvents::ConnectionBrokenEvent &event) + { + (void)event; + LogInfo("SERVER: ConnectionBrokenEvent received"); + } + + virtual void OnEventReceived(const DPL::AbstractRPCConnectorEvents::ConnectionEstablishedEvent &event) + { + // Save connection pointer + LogInfo("SERVER: Acquiring RPC connection"); + m_rpcConnection.Reset(event.GetArg1()); + + // Attach event listeners + LogInfo("SERVER: Attaching connection listeners"); + m_rpcConnection->DPL::EventSupport::AddListener(this); + m_rpcConnection->DPL::EventSupport::AddListener(this); + m_rpcConnection->DPL::EventSupport::AddListener(this); + + LogInfo("SERVER: Connection established"); + + // Emit RPC function call + DPL::RPCFunction proc; + proc.AppendArg((int)2222); + LogInfo("SERVER: Calling RPC function"); + m_rpcConnection->AsyncCall(proc); + } + +public: + MyApplication(int argc, char **argv) + : Application(argc, argv, "rpc") + { + // Attach RPC server listeners + LogInfo("SERVER: Attaching connection established event"); + m_rpcServer.DPL::EventSupport::AddListener(this); + + // Self touch + LogInfo("SERVER: Touching controller"); + Touch(); + + // Open RPC server + LogInfo("SERVER: Opening server RPC"); + m_rpcServer.Open(RPC_NAME); + + // Run RPC client in thread + LogInfo("SERVER: Starting RPC client thread"); + m_thread.Run(); + + // Quit application automatically in few seconds + LogInfo("SERVER: Sending control timed events"); + DPL::ControllerEventHandler::PostTimedEvent(CloseThreadEvent(), 2); + DPL::ControllerEventHandler::PostTimedEvent(QuitEvent(), 3); + } + + virtual ~MyApplication() + { + // Quit thread + LogInfo("SERVER: Quitting thread"); + m_thread.Quit(); + + // Close RPC server + LogInfo("SERVER: Closing RPC server"); + m_rpcServer.CloseAll(); + + // Detach RPC server listener + m_rpcServer.DPL::EventSupport::RemoveListener(this); + } +}; + +int main(int argc, char *argv[]) +{ + LogInfo("Starting"); + MyApplication app(argc, argv); + return app.Exec(); +} diff --git a/examples/simple/CMakeLists.txt b/examples/simple/CMakeLists.txt new file mode 100644 index 0000000..42020ec --- /dev/null +++ b/examples/simple/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(SIMPLE_SYS dpl-gtk REQUIRED) + +SET(SIMPLE_SOURCES + simple.cpp) + +ADD_DEFINITIONS("-D_DEBUG") + +INCLUDE_DIRECTORIES(${SIMPLE_SYS_INCLUDE_DIRS}) +LINK_DIRECTORIES(${SIMPLE_SYS_LIBRARY_DIRS}) + +ADD_EXECUTABLE(simple ${SIMPLE_SOURCES}) +TARGET_LINK_LIBRARIES(simple ${SIMPLE_SYS_LIBRARIES}) diff --git a/examples/simple/simple.cpp b/examples/simple/simple.cpp new file mode 100644 index 0000000..e09af81 --- /dev/null +++ b/examples/simple/simple.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file simple.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of simple example + */ +#include + +int main(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + LogInfo("Hello world!"); + return 0; +} + diff --git a/examples/single_instance/CMakeLists.txt b/examples/single_instance/CMakeLists.txt new file mode 100644 index 0000000..3c29cc6 --- /dev/null +++ b/examples/single_instance/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(RPC_SYS dpl-efl REQUIRED) + +SET(RPC_SOURCES + single_instance.cpp) + +ADD_DEFINITIONS("-D_DEBUG") + +INCLUDE_DIRECTORIES(${RPC_SYS_INCLUDE_DIRS}) +LINK_DIRECTORIES(${RPC_SYS_LIBRARY_DIRS}) + +ADD_EXECUTABLE(single_instance ${RPC_SOURCES}) +TARGET_LINK_LIBRARIES(single_instance ${RPC_SYS_LIBRARIES}) diff --git a/examples/single_instance/single_instance.cpp b/examples/single_instance/single_instance.cpp new file mode 100644 index 0000000..cadb4f4 --- /dev/null +++ b/examples/single_instance/single_instance.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file single_instance.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of single instance example + */ +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc != 2) + { + std::cout << "Specify instance name!" << std::endl; + return 0; + } + + DPL::SingleInstance singleInstance; + + if (singleInstance.TryLock(argv[1])) + { + std::cout << "Succedded to lock single instance." << std::endl << "Press ENTER to release lock..." << std::endl; + + // Wait for any key + getchar(); + + // Release gathered lock + singleInstance.Release(); + + // Done + return 0; + } + + std::cout << "Cannot retrieve single instance lock." << std::endl + << "Another application has locked single instance." << std::endl + << "Will now exit." << std::endl; + + // Done + return 0; +} diff --git a/examples/socket/CMakeLists.txt b/examples/socket/CMakeLists.txt new file mode 100644 index 0000000..9fe5009 --- /dev/null +++ b/examples/socket/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(SOCKET_SYS dpl-efl REQUIRED) + +SET(SOCKET_SOURCES + socket.cpp) + +ADD_DEFINITIONS("-D_DEBUG") + +INCLUDE_DIRECTORIES(${SOCKET_SYS_INCLUDE_DIRS}) +LINK_DIRECTORIES(${SOCKET_SYS_LIBRARY_DIRS}) + +ADD_EXECUTABLE(socket ${SOCKET_SOURCES}) +TARGET_LINK_LIBRARIES(socket ${SOCKET_SYS_LIBRARIES}) diff --git a/examples/socket/socket.cpp b/examples/socket/socket.cpp new file mode 100644 index 0000000..c8a2894 --- /dev/null +++ b/examples/socket/socket.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file socket.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of socket example + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace // anonymous +{ +static const char *SOCKET_NAME = "/tmp/unix_sock"; +} // namespace anonymous + +class MyThread + : public DPL::Thread, + private DPL::EventListener +{ +private: + DPL::UnixSocket m_socket; + + // Socket accept event + virtual void OnEventReceived(const DPL::AbstractSocketEvents::AcceptEvent &event) + { + (void)event; + LogInfo("Accept event occurred"); + + DPL::UnixSocket *client = static_cast(m_socket.Accept()); + + LogInfo("Accepted client remote address: " << client->GetRemoteAddress().ToString()); + LogInfo("Accepted client local address: " << client->GetLocalAddress().ToString()); + + delete client; + } + +public: + virtual ~MyThread() + { + // Quit thread + Quit(); + } + + virtual int ThreadEntry() + { + // Add listeners + m_socket.DPL::EventSupport::AddListener(this); + + // Create server + LogInfo("Starting server..."); + + m_socket.Bind(DPL::Address(SOCKET_NAME)); + m_socket.Listen(5); + + LogInfo("Server started"); + + LogInfo("Server local address: " << m_socket.GetLocalAddress().ToString()); + + int result = Exec(); + + // Remove listeners + m_socket.DPL::EventSupport::RemoveListener(this); + + // Must close socket in same context + m_socket.Close(); + + return result; + } +}; + +DECLARE_GENERIC_EVENT_0(QuitEvent) + +class MyApplication + : public DPL::Application, + public DPL::Controller::Type>, + private DPL::EventListener +{ +private: + MyThread thread; + DPL::UnixSocket sock; + + // Quit application event occurred + virtual void OnEventReceived(const QuitEvent &event) + { + (void)event; + Quit(); + } + + // Socket connected event + virtual void OnEventReceived(const DPL::AbstractSocketEvents::ConnectedEvent &event) + { + (void)event; + LogInfo("Connected event occurred"); + } + +public: + MyApplication(int argc, char **argv) + : Application(argc, argv, "example_socket_application") + { + // Add listeners + sock.DPL::EventSupport::AddListener(this); + + // Touch self controller + Touch(); + + // Start threaded server + LogInfo("Running threaded server"); + + // Run server in thread + thread.Run(); + + LogInfo("Waiting for server to start..."); + sleep(1); + + // Connect to server + sock.Connect(DPL::Address(SOCKET_NAME)); + + // Quit application automatically in few seconds + DPL::ControllerEventHandler::PostTimedEvent(QuitEvent(), 2); + } + + virtual ~MyApplication() + { + // Remove listeners + sock.DPL::EventSupport::RemoveListener(this); + } +}; + +int main(int argc, char *argv[]) +{ + MyApplication app(argc, argv); + return app.Exec(); +} diff --git a/examples/tcpsock/CMakeLists.txt b/examples/tcpsock/CMakeLists.txt new file mode 100644 index 0000000..a6550fc --- /dev/null +++ b/examples/tcpsock/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(TCPSOCK_SYS dpl-efl REQUIRED) + +SET(TCPSOCK_SOURCES + tcpsock.cpp) + +ADD_DEFINITIONS("-D_DEBUG") + +INCLUDE_DIRECTORIES(${TCPSOCK_SYS_INCLUDE_DIRS}) +LINK_DIRECTORIES(${TCPSOCK_SYS_LIBRARY_DIRS}) + +ADD_EXECUTABLE(tcpsock ${TCPSOCK_SOURCES}) +TARGET_LINK_LIBRARIES(tcpsock ${TCPSOCK_SYS_LIBRARIES}) diff --git a/examples/tcpsock/tcpsock.cpp b/examples/tcpsock/tcpsock.cpp new file mode 100644 index 0000000..c2697cd --- /dev/null +++ b/examples/tcpsock/tcpsock.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file tcpsock.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of tcpsock example + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class MyApplication + : public DPL::Application, + private DPL::EventListener, + private DPL::EventListener +{ +private: + DPL::TcpSocket m_socket; + + virtual void OnEventReceived(const DPL::AbstractSocketEvents::ConnectedEvent &event) + { + (void)event; + LogInfo("Connected!"); + + // Send request + DPL::BinaryQueue data; + const char *query = "GET /wiki/Main_Page HTTP/1.1\nHost: en.wikipedia.org\n\n"; + data.AppendCopy(query, strlen(query) + 1); + m_socket.Write(data, data.Size()); + } + + virtual void OnEventReceived(const DPL::AbstractSocketEvents::ReadEvent &event) + { + (void)event; + LogInfo("Read!"); + + DPL::BinaryQueueAutoPtr data = m_socket.Read(100); // Bad: DLOG cannot log more than about 450 bytes... + + Assert(data.get() != NULL); + + if (data->Empty()) + { + LogInfo("Connection closed!"); + m_socket.Close(); + + // Done + Quit(); + return; + } + + // Show data + DPL::ScopedArray text(new char[data->Size()]); + data->Flatten(text.Get(), data->Size()); + + LogPedantic("READ: \n--------------------------------------------------------\n" + << std::string(text.Get(), text.Get() + data->Size()) << + "\n--------------------------------------------------------"); + } + +public: + MyApplication(int argc, char **argv) + : Application(argc, argv, "tcpsock") + { + LogInfo("CTOR!"); + + // Add listeners + m_socket.DPL::EventSupport::AddListener(this); + m_socket.DPL::EventSupport::AddListener(this); + + // Connect + m_socket.Open(); + LogInfo("Connecting..."); + m_socket.Connect(DPL::Address("en.wikipedia.org", 80)); + } + + virtual ~MyApplication() + { + LogInfo("DTOR!"); + + // Remove listeners + m_socket.DPL::EventSupport::RemoveListener(this); + m_socket.DPL::EventSupport::RemoveListener(this); + } +}; + +int main(int argc, char *argv[]) +{ + MyApplication app(argc, argv); + return app.Exec(); +} diff --git a/examples/timed_event/CMakeLists.txt b/examples/timed_event/CMakeLists.txt new file mode 100644 index 0000000..cb60fb0 --- /dev/null +++ b/examples/timed_event/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(RPC_SYS dpl-efl REQUIRED) + +SET(RPC_SOURCES + timed_event.cpp) + +ADD_DEFINITIONS("-D_DEBUG") + +INCLUDE_DIRECTORIES(${RPC_SYS_INCLUDE_DIRS}) +LINK_DIRECTORIES(${RPC_SYS_LIBRARY_DIRS}) + +ADD_EXECUTABLE(timed_event ${RPC_SOURCES}) +TARGET_LINK_LIBRARIES(timed_event ${RPC_SYS_LIBRARIES}) diff --git a/examples/timed_event/timed_event.cpp b/examples/timed_event/timed_event.cpp new file mode 100644 index 0000000..28c74a4 --- /dev/null +++ b/examples/timed_event/timed_event.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file timed_event.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of timed event example + */ +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GENERIC_EVENT_0(FirstEvent) +DECLARE_GENERIC_EVENT_0(SecondEvent) + +class ControllerInThread + : public DPL::Controller::Type> +{ +protected: + virtual void OnEventReceived(const FirstEvent &event) + { + (void)event; + LogInfo("First event occurred"); + } + + virtual void OnEventReceived(const SecondEvent &event) + { + (void)event; + LogInfo("Second event occurred"); + } +}; + +DECLARE_GENERIC_EVENT_0(QuitEvent) + +class MyApplication + : public DPL::Application, + private DPL::Controller::Type> +{ +private: + DPL::Thread m_thread; + ControllerInThread m_controllerInThread; + + // Quit application event occurred + virtual void OnEventReceived(const QuitEvent &event) + { + (void)event; + Quit(); + } + +public: + MyApplication(int argc, char **argv) + : Application(argc, argv, "timed_event", false) + { + // Touch + Touch(); + m_controllerInThread.Touch(); + + // Run thread + m_thread.Run(); + m_controllerInThread.SwitchToThread(&m_thread); + + // Emit thread timed events + m_controllerInThread.DPL::ControllerEventHandler::PostTimedEvent(SecondEvent(), 3); + m_controllerInThread.DPL::ControllerEventHandler::PostTimedEvent(FirstEvent(), 2); + + // Emit framework timed quit event + DPL::ControllerEventHandler::PostTimedEvent(QuitEvent(), 5); + } + + virtual ~MyApplication() + { + m_controllerInThread.SwitchToThread(NULL); + + // Quit thread + m_thread.Quit(); + } +}; + +int main(int argc, char *argv[]) +{ + MyApplication app(argc, argv); + return app.Exec(); +} diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt new file mode 100644 index 0000000..7d34aaf --- /dev/null +++ b/modules/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file CMakeLists.txt +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +include(core/config.cmake) +include(dbus/config.cmake) +include(db/config.cmake) +include(event/config.cmake) +include(socket/config.cmake) +include(rpc/config.cmake) +include(test/config.cmake) +include(log/config.cmake) +ADD_SUBDIRECTORY(vcore) +ADD_SUBDIRECTORY(ace) +ADD_SUBDIRECTORY(widget_dao) +include(popup/config.cmake) +include(utils/config.cmake) +include(localization/config.cmake) diff --git a/modules/ace/CMakeLists.txt b/modules/ace/CMakeLists.txt new file mode 100644 index 0000000..358ba76 --- /dev/null +++ b/modules/ace/CMakeLists.txt @@ -0,0 +1,169 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Path for all tests binary files + +###################################################################### +#DB ace +ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_BINARY_DIR}/modules/ace/database_checksum_ace.h + COMMAND ${CMAKE_SOURCE_DIR}/modules/ace/orm/gen_db_md5.sh + ARGS ${CMAKE_BINARY_DIR}/modules/ace/database_checksum_ace.h + ${CMAKE_SOURCE_DIR}/modules/ace/orm/ace_db + DEPENDS ${CMAKE_SOURCE_DIR}/modules/ace/orm/ace_db + ${CMAKE_SOURCE_DIR}/modules/ace/orm/gen_db_md5.sh + COMMENT "Generating ACE database checksum" + ) +ADD_CUSTOM_COMMAND( OUTPUT .ace.db + COMMAND rm -f ${CMAKE_CURRENT_BINARY_DIR}/.ace.db + COMMAND gcc -Wall -include ${CMAKE_BINARY_DIR}/modules/ace/database_checksum_ace.h -I${PROJECT_SOURCE_DIR}/modules/db/include -I${PROJECT_SOURCE_DIR}/modules/ace/orm -E ${PROJECT_SOURCE_DIR}/modules/ace/orm/ace_db_sql_generator.h | grep --invert-match "^#" > ${CMAKE_CURRENT_BINARY_DIR}/ace_db.sql + COMMAND sqlite3 ${CMAKE_CURRENT_BINARY_DIR}/.ace.db ".read ${CMAKE_CURRENT_BINARY_DIR}/ace_db.sql" || rm -f ${CMAKE_CURRENT_BINARY_DIR}/.ace.db + DEPENDS ${CMAKE_BINARY_DIR}/modules/ace/database_checksum_ace.h ${PROJECT_SOURCE_DIR}/modules/ace/orm/ace_db_sql_generator.h ${PROJECT_SOURCE_DIR}/modules/ace/orm/ace_db + ) + +ADD_CUSTOM_COMMAND( OUTPUT .ace.db-journal + COMMAND touch + ARGS ${CMAKE_CURRENT_BINARY_DIR}/.ace.db-journal + ) + +ADD_CUSTOM_TARGET(Sqlite3DbACE ALL DEPENDS .ace.db .ace.db-journal) + +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/ace_db.sql + DESTINATION share/wrt-engine/ + ) + +########################################################### + +INCLUDE(FindPkgConfig) + +SET(ACE_TEST_PATH "/usr/apps/org.tizen.policy") + +INSTALL(FILES + ${CMAKE_CURRENT_SOURCE_DIR}/configuration/config.xml + ${CMAKE_CURRENT_SOURCE_DIR}/configuration/config.dtd + ${CMAKE_CURRENT_SOURCE_DIR}/configuration/bondixml.xsd + ${CMAKE_CURRENT_SOURCE_DIR}/configuration/demo.xml + ${CMAKE_CURRENT_SOURCE_DIR}/configuration/WACPolicy.xml + ${CMAKE_CURRENT_SOURCE_DIR}/configuration/UnrestrictedPolicy.xml + DESTINATION /usr/etc/ace + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ GROUP_WRITE) + +add_subdirectory(dao) + +SET(ACE_LIB_DEPS_BASIC + ecore + appcore-efl + openssl + sqlite3 + heynoti + dlog + vconf + db-util + libpcrecpp + icu-uc + libxml-2.0 + ) + +IF(SMACK_ENABLED) + LIST(APPEND ACE_LIB_DEPS_BASIC libprivilege-control) +ENDIF(SMACK_ENABLED) + +PKG_CHECK_MODULES(ACE_LIB_DEPS ${ACE_LIB_DEPS_BASIC} REQUIRED) + +SET(WRT_ACE_DIR ${PROJECT_SOURCE_DIR}/modules/ace) + +SET(ACE_SOURCES + ${WRT_ACE_DIR}/engine/PolicyEvaluator.cpp + ${WRT_ACE_DIR}/engine/PolicyInformationPoint.cpp + ${WRT_ACE_DIR}/engine/CombinerImpl.cpp + ${WRT_ACE_DIR}/engine/parser.cpp + ${WRT_ACE_DIR}/engine/PolicyEnforcementPoint.cpp + ${WRT_ACE_DIR}/engine/SettingsLogic.cpp + ${WRT_ACE_DIR}/engine/Attribute.cpp + ${WRT_ACE_DIR}/engine/Condition.cpp + ${WRT_ACE_DIR}/engine/NodeFactory.cpp + ${WRT_ACE_DIR}/engine/Policy.cpp + ${WRT_ACE_DIR}/engine/Rule.cpp + ${WRT_ACE_DIR}/engine/Serializer.cpp + ${WRT_ACE_DIR}/engine/Subject.cpp + ${WRT_ACE_DIR}/engine/TreeNode.cpp + ${WRT_ACE_DIR}/engine/ConfigurationManager.cpp +) + +INCLUDE_DIRECTORIES(${ACE_LIB_DEPS_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(${WRT_ACE_DIR}/include) +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/modules/core/include) +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/modules/db/include) +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/modules/log/include) +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/modules/event/include) + +SET(WITH_ACE_SETTINGS_SERVER_SOURCES + ${WITH_ACE_SETTINGS_SERVER_NONE_SOURCES} + ) + +ADD_LIBRARY(${TARGET_ACE_LIB} SHARED + ${ACE_SOURCES} + ${WITH_ACE_SETTINGS_SERVER_SOURCES} +) + +SET_TARGET_PROPERTIES(${TARGET_ACE_LIB} PROPERTIES + SOVERSION ${VERSION}) + +SET_TARGET_PROPERTIES(${TARGET_ACE_LIB} PROPERTIES + COMPILE_FLAGS -fPIC) + +TARGET_LINK_LIBRARIES(${TARGET_ACE_LIB} +# ${TARGET_WRT_DAO_RW_LIB} #For now WRT has to be RW - Global dao write is needed + ${TARGET_ACE_DAO_RW_LIB} + ${ACE_LIB_DEPS_LIBRARIES} + ${DPL_EFL_LIBRARY} +) + +INSTALL(TARGETS ${TARGET_ACE_LIB} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE +) + +INSTALL(FILES + include/dpl/ace/AbstractPolicyEnforcementPoint.h + include/dpl/ace/AbstractTreeElement.h + include/dpl/ace/Attribute.h + include/dpl/ace/AsyncVerdictResultListener.h + include/dpl/ace/Combiner.h + include/dpl/ace/CombinerImpl.h + include/dpl/ace/ConfigurationManager.h + include/dpl/ace/Constants.h + include/dpl/ace/Effect.h + include/dpl/ace/PermissionTriple.h + include/dpl/ace/Policy.h + include/dpl/ace/PolicyEffect.h + include/dpl/ace/PolicyEnforcementPoint.h + include/dpl/ace/PolicyEvaluator.h + include/dpl/ace/PolicyEvaluatorFactory.h + include/dpl/ace/PolicyInformationPoint.h + include/dpl/ace/PolicyResult.h + include/dpl/ace/Preference.h + include/dpl/ace/PromptDecision.h + include/dpl/ace/Request.h + include/dpl/ace/SettingsLogic.h + include/dpl/ace/Subject.h + include/dpl/ace/TreeNode.h + include/dpl/ace/UserDecision.h + include/dpl/ace/WRT_INTERFACE.h + include/dpl/ace/Verdict.h + DESTINATION + include/dpl-efl/dpl/ace + ) + diff --git a/modules/ace/DESCRIPTION b/modules/ace/DESCRIPTION new file mode 100644 index 0000000..aac5ef6 --- /dev/null +++ b/modules/ace/DESCRIPTION @@ -0,0 +1,2 @@ +!!!options!!! stop +ACE - Access Control Engine - security module for Device APIs diff --git a/modules/ace/configuration/UnrestrictedPolicy.xml b/modules/ace/configuration/UnrestrictedPolicy.xml new file mode 100644 index 0000000..558f2dc --- /dev/null +++ b/modules/ace/configuration/UnrestrictedPolicy.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/modules/ace/configuration/WACPolicy.xml b/modules/ace/configuration/WACPolicy.xml new file mode 100644 index 0000000..f2c9236 --- /dev/null +++ b/modules/ace/configuration/WACPolicy.xml @@ -0,0 +1,520 @@ + + + + + + + + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + asdfkjlhxvczxnbcvnjahfjhsdfklahfdas + + + +

PValue

QValue Gvalue laj? + + + + modulus + + + exponent + + + + + Subject name + SKI + + + + + + + + + + + + Identified + + + + + + + + http://jil.org/jil/api/1.1/devicestateinfo.DeviceStateInfo.requestPositionInfo + + + + + + + http://jil.org/jil/api/1.1/widget.Widget.openURL + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.update + + + http://jil.org/jil/api/1.1/device.Device.launchApplication + + + http://jil.org/jil/api/1.1/multimedia.Camera.captureImage + + + http://jil.org/jil/api/1.1/messaging.Messaging.sendMessage + + + http://jil.org/jil/api/1.1.1/pim.PIM.findAddressBookItems + + + http://jil.org/jil/api/1.1.1/pim.PIM.getAddressBookItem + + + http://jil.org/jil/api/1.1.1/pim.PIM.getAddressBookItemsCount + + + + + + + + http://jil.org/jil/api/1.1/device.Device.getAvailableApplications + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.getAttributeValue + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.getAvailableAttributes + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.setAttributeValue + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.open + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.play + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.pause + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.resume + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.stop + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.onStateChange + + + http://jil.org/jil/api/1.1/multimedia.Camera.onCameraCaptured + + + http://jil.org/jil/api/1.1/multimedia.Camera.setWindow + + + http://jil.org/jil/api/1.1/device.Device.PositionInfo + + + http://jil.org/jil/api/1.1/device.Device.DeviceStateInfo + + + http://jil.org/jil/api/1.1/devicestateinfo.DeviceStateInfo.onPositionRetrieved + + + http://jil.org/jil/api/1.1/messaging.Messaging.createMessage + + + http://jil.org/jil/api/1.1/messaging.Messaging.onMessageSendingFailure + + + http://jil.org/jil/api/1.1/multimedia.Multimedia.getVolume + + + http://jil.org/jil/api/1.1/multimedia.Multimedia.stopAll + + + http://jil.org/jil/api/1.1/multimedia.Multimedia.isAudioPlaying + + + http://jil.org/jil/api/1.1.1/pim.PIM.createAddressBookItem + + + http://jil.org/jil/api/1.1.1/pim.PIM.onAddressBookItemFound + + + http://jil.org/jil/api/1.1/accelerometerinfo + + + http://jil.org/jil/api/1.1/addressbookitem + + + http://jil.org/jil/api/1.1.5/applicationtypes + + + http://jil.org/jil/api/1.1.2/camera + + + http://jil.org/jil/api/1.1/device + + + http://jil.org/jil/api/1.1/devicestateinfo + + + http://jil.org/jil/api/1.1.5/exception + + + http://jil.org/jil/api/1.1.5/exceptiontypes + + + http://jil.org/jil/api/1.1/message + + + http://jil.org/jil/api/1.1/messagetypes + + + http://jil.org/jil/api/1.1/messaging + + + http://jil.org/jil/api/1.1/multimedia + + + http://jil.org/jil/api/1.1.1/pim + + + http://jil.org/jil/api/1.1/positioninfo + + + http://jil.org/jil/api/1.1/widget + + + + + + + + + + + + Operator + + + + + + + + + + diff --git a/modules/ace/configuration/bondixml.xsd b/modules/ace/configuration/bondixml.xsd new file mode 100644 index 0000000..d16a14d --- /dev/null +++ b/modules/ace/configuration/bondixml.xsd @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ace/configuration/config.dtd b/modules/ace/configuration/config.dtd new file mode 100644 index 0000000..f483631 --- /dev/null +++ b/modules/ace/configuration/config.dtd @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/modules/ace/configuration/config.xml b/modules/ace/configuration/config.xml new file mode 100644 index 0000000..278081a --- /dev/null +++ b/modules/ace/configuration/config.xml @@ -0,0 +1,10 @@ + + + + /usr/etc/ace/ + + demo.xml + + + + diff --git a/modules/ace/configuration/demo.xml b/modules/ace/configuration/demo.xml new file mode 100644 index 0000000..4c4f423 --- /dev/null +++ b/modules/ace/configuration/demo.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/modules/ace/dao/AceDAO.cpp b/modules/ace/dao/AceDAO.cpp new file mode 100644 index 0000000..155c979 --- /dev/null +++ b/modules/ace/dao/AceDAO.cpp @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file AceDAO.cpp + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 0.1 + * @brief + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace DPL::DB::ORM; +using namespace DPL::DB::ORM::ace; +using namespace AceDB::AceDaoUtilities; +using namespace AceDB::AceDaoConversions; + +namespace { +char const * const EMPTY_SESSION = ""; +} // namespace + +namespace AceDB{ + +void AceDAO::setPromptDecision( + const DPL::String &hash, + const DPL::String &userParam, + const DPL::OptionalString &session, + PromptDecision decision) +{ + Try { + ScopedTransaction transaction(&AceDaoUtilities::m_databaseInterface); + + ACE_DB_DELETE(del, AcePromptDecision, &AceDaoUtilities::m_databaseInterface); + del->Where( + And( + Equals(userParam), + Equals(hash))); + del->Execute(); + + AcePromptDecision::Row row; + row.Set_hash(hash); + row.Set_decision(promptDecisionToInt(decision)); + row.Set_user_param(userParam); + row.Set_session(session); + ACE_DB_INSERT(insert, AcePromptDecision, &AceDaoUtilities::m_databaseInterface); + insert->Values(row); + insert->Execute(); + + transaction.Commit(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to setUserSetting"); + } +} + +void AceDAO::setPromptDecision( + const BaseAttributeSet &attributes, + const DPL::String &userParam, + const DPL::OptionalString &session, + PromptDecision decision) +{ + DPL::String DPLHash = convertToHash(attributes); + + setPromptDecision( + DPLHash, + userParam, + session, + decision); +} + +void AceDAO::removePolicyResult( + const BaseAttributeSet &attributes) +{ + Try { + ScopedTransaction transaction(&AceDaoUtilities::m_databaseInterface); + + auto attrHash = convertToHash(attributes); + + ACE_DB_DELETE(del, + AcePolicyResult, + &AceDaoUtilities::m_databaseInterface); + del->Where(Equals(attrHash)); + del->Execute(); + transaction.Commit(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to removeVerdict"); + } +} + +void AceDAO::clearAllSettings(void) +{ + clearWidgetDevCapSettings(); + clearDevCapSettings(); +} + +void AceDAO::setDevCapSetting(const std::string &resource, + PreferenceTypes preference) +{ + Try { + ACE_DB_UPDATE(update, AceDevCap, &AceDaoUtilities::m_databaseInterface); + AceDevCap::Row row; + row.Set_general_setting(preferenceToInt(preference)); + update->Values(row); + update->Where( + Equals(DPL::FromUTF8String(resource))); + update->Execute(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to SetResourceSetting"); + } +} + +void AceDAO::removeDevCapSetting(const std::string &resource) +{ + Try { + ACE_DB_UPDATE(update, AceDevCap, &AceDaoUtilities::m_databaseInterface); + AceDevCap::Row row; + row.Set_general_setting(preferenceToInt(PreferenceTypes::PREFERENCE_DEFAULT)); + update->Values(row); + update->Where( + Equals(DPL::FromUTF8String(resource))); + update->Execute(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to removeResourceSetting"); + } +} + + +void AceDAO::setWidgetDevCapSetting(const std::string &resource, + WidgetHandle handler, + PreferenceTypes preference) +{ + Try { + ScopedTransaction transaction(&AceDaoUtilities::m_databaseInterface); + // TODO JOIN + AceDevCap::Row rrow; + if (!getResourceByUri(resource, rrow)) { + ThrowMsg(Exception::DatabaseError, "Resource not found"); + } + + ACE_DB_INSERT(insert, + AceWidgetDevCapSetting, + &AceDaoUtilities::m_databaseInterface); + + AceWidgetDevCapSetting::Row row; + row.Set_app_id(handler); + int rid = rrow.Get_resource_id(); + row.Set_resource_id(rid); + row.Set_access_value(preferenceToInt(preference)); + insert->Values(row); + insert->Execute(); + + transaction.Commit(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to setUserSetting"); + } +} + +void AceDAO::removeWidgetDevCapSetting(const std::string &resource, + WidgetHandle handler) +{ + Try { + ScopedTransaction transaction(&AceDaoUtilities::m_databaseInterface); + AceDevCap::Row rrow; + if (!getResourceByUri(resource, rrow)) { + ThrowMsg(Exception::DatabaseError, "resource not found"); + } + + ACE_DB_DELETE(del, + AceWidgetDevCapSetting, + &AceDaoUtilities::m_databaseInterface); + + Equals e1(handler); + Equals e2(rrow.Get_resource_id()); + del->Where(And(e1, e2)); + del->Execute(); + transaction.Commit(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to clearUserSettings"); + } +} + + +void AceDAO::setPolicyResult(const BaseAttributeSet &attributes, + const PolicyResult &policyResult) +{ + Try { + ScopedTransaction transaction(&AceDaoUtilities::m_databaseInterface); + + // TODO: this call is connected with logic. + // It should be moved to PolicyEvaluator + addAttributes(attributes); + + auto attrHash = convertToHash(attributes); + + ACE_DB_DELETE(del, AcePolicyResult, &AceDaoUtilities::m_databaseInterface) + del->Where(Equals(attrHash)); + del->Execute(); + + ACE_DB_INSERT(insert, AcePolicyResult, &AceDaoUtilities::m_databaseInterface); + AcePolicyResult::Row row; + row.Set_decision(PolicyResult::serialize(policyResult)); + row.Set_hash(attrHash); + insert->Values(row); + insert->Execute(); + + transaction.Commit(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to addVerdict"); + } +} + +void AceDAO::resetDatabase(void) +{ + Try { + ScopedTransaction transaction(&AceDaoUtilities::m_databaseInterface); + ACE_DB_DELETE(del1, AcePolicyResult, &AceDaoUtilities::m_databaseInterface); + del1->Execute(); + ACE_DB_DELETE(del2, AceWidgetDevCapSetting, &AceDaoUtilities::m_databaseInterface); + del2->Execute(); + ACE_DB_DELETE(del3, AceDevCap, &AceDaoUtilities::m_databaseInterface); + del3->Execute(); + ACE_DB_DELETE(del4, AceSubject, &AceDaoUtilities::m_databaseInterface); + del4->Execute(); + ACE_DB_DELETE(del5, AceAttribute, &AceDaoUtilities::m_databaseInterface); + del5->Execute(); + ACE_DB_DELETE(del6, AcePromptDecision, &AceDaoUtilities::m_databaseInterface); + del6->Execute(); + + transaction.Commit(); + + // TODO there is no such query yet in ORM. + // GlobalConnection::DataCommandAutoPtr command = + // GlobalConnectionSingleton::Instance().PrepareDataCommand( + // "VACUUM"); + // command->Step(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to resetDatabase"); + } +} + +void AceDAO::clearDevCapSettings() +{ + Try { + ACE_DB_UPDATE(update, AceDevCap, &AceDaoUtilities::m_databaseInterface); + AceDevCap::Row row; + row.Set_general_setting(-1); + update->Values(row); + update->Execute(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to clearResourceSettings"); + } +} + +void AceDAO::clearWidgetDevCapSettings() +{ + Try { + ACE_DB_DELETE(del, AceWidgetDevCapSetting, &AceDaoUtilities::m_databaseInterface); + del->Execute(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to clearUserSettings"); + } +} + +int AceDAO::addResource(const std::string &request) +{ + LogDebug("addResource: " << request); + Try { + ScopedTransaction transaction(&AceDaoUtilities::m_databaseInterface); + AceDevCap::Row rrow; + if (getResourceByUri(request, rrow)) { + transaction.Commit(); + return rrow.Get_resource_id(); + } + + ACE_DB_INSERT(insert, AceDevCap, &AceDaoUtilities::m_databaseInterface); + AceDevCap::Row row; + row.Set_id_uri(DPL::FromUTF8String(request)); + row.Set_general_setting(-1); + insert->Values(row); + int id = insert->Execute(); + transaction.Commit(); + return id; + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed in addResource"); + } +} + +void AceDAO::addAttributes(const BaseAttributeSet &attributes) +{ + Try { + BaseAttributeSet::const_iterator iter; + + for (iter = attributes.begin(); iter != attributes.end(); ++iter) { + ACE_DB_SELECT(select, AceAttribute, &AceDaoUtilities::m_databaseInterface); + select->Where(Equals(DPL::FromUTF8String( + *(*iter)->getName()))); + std::list rows = select->GetRowList(); + if (!rows.empty()) { + continue; + } + + ACE_DB_INSERT(insert, AceAttribute, &AceDaoUtilities::m_databaseInterface); + AceAttribute::Row row; + row.Set_name(DPL::FromUTF8String(*(*iter)->getName())); + row.Set_type(attributeTypeToInt((*iter)->getType())); + insert->Values(row); + insert->Execute(); + } + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed in addAttributes"); + } +} + +void AceDAO::setStaticDevCapPermissions( + int widgetHandle, + const std::set &permissions) +{ + Try { + FOREACH(it, permissions) { + ACE_DB_INSERT(insert, AceStaticDevCapPermission, + &AceDaoUtilities::m_databaseInterface); + AceStaticDevCapPermission::Row row; + row.Set_app_id(widgetHandle); + row.Set_dev_cap(*it); + insert->Values(row); + insert->Execute(); + } + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed in setStaticDevCapPermissions"); + } +} + + +} diff --git a/modules/ace/dao/AceDAOConversions.cpp b/modules/ace/dao/AceDAOConversions.cpp new file mode 100644 index 0000000..5254112 --- /dev/null +++ b/modules/ace/dao/AceDAOConversions.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file AceDaoConversions.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 0.1 + * @brief + */ + +#include +#include + +#include + +namespace AceDB { + +DPL::String AceDaoConversions::convertToHash(const BaseAttributeSet &attributes) +{ + unsigned char attrHash[MD5_DIGEST_LENGTH]; + std::string attrString; + FOREACH(it, attributes) { + // [CR] implementation of it->toString() is not secure, 24.03.2010 + attrString.append((*it)->toString()); + } + + MD5((unsigned char *) attrString.c_str(), attrString.length(), attrHash); + + char attrHashCoded[MD5_DIGEST_LENGTH*2 + 1]; + for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) { + sprintf(&attrHashCoded[i << 1], + "%02X", + static_cast(attrHash[i])); + } + return DPL::FromASCIIString(attrHashCoded); +} + + +} diff --git a/modules/ace/dao/AceDAOReadOnly.cpp b/modules/ace/dao/AceDAOReadOnly.cpp new file mode 100644 index 0000000..1f1b617 --- /dev/null +++ b/modules/ace/dao/AceDAOReadOnly.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file AceDAOReadOnlyReadOnly.cpp + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 0.1 + * @brief + */ + +#include +#include +#include +#include + +#include + +using namespace DPL::DB::ORM; +using namespace DPL::DB::ORM::ace; +using namespace AceDB::AceDaoUtilities; +using namespace AceDB::AceDaoConversions; + +namespace AceDB { + +static const int DB_ALLOW_ALWAYS = 0; +static const int DB_ALLOW_FOR_SESSION = 1; +static const int DB_ALLOW_THIS_TIME = 2; +static const int DB_DENY_ALWAYS = 3; +static const int DB_DENY_FOR_SESSION = 4; +static const int DB_DENY_THIS_TIME = 5; + +int AceDAOReadOnly::promptDecisionToInt(PromptDecision decision) +{ + if (PromptDecision::ALLOW_ALWAYS == decision) { + return DB_ALLOW_ALWAYS; + } else if (PromptDecision::DENY_ALWAYS == decision) { + return DB_DENY_ALWAYS; + } else if (PromptDecision::ALLOW_THIS_TIME == decision) { + return DB_ALLOW_THIS_TIME; + } else if (PromptDecision::DENY_THIS_TIME == decision) { + return DB_DENY_THIS_TIME; + } else if (PromptDecision::ALLOW_FOR_SESSION == decision) { + return DB_ALLOW_FOR_SESSION; + } + // DENY_FOR_SESSION + return DB_DENY_FOR_SESSION; +} + +PromptDecision AceDAOReadOnly::intToPromptDecision(int dec) { + if (dec == DB_ALLOW_ALWAYS) { + return PromptDecision::ALLOW_ALWAYS; + } else if (dec == DB_DENY_ALWAYS) { + return PromptDecision::DENY_ALWAYS; + } else if (dec == DB_ALLOW_THIS_TIME) { + return PromptDecision::ALLOW_THIS_TIME; + } else if (dec == DB_DENY_THIS_TIME) { + return PromptDecision::DENY_THIS_TIME; + } else if (dec == DB_ALLOW_FOR_SESSION) { + return PromptDecision::ALLOW_FOR_SESSION; + } + // DB_DENY_FOR_SESSION + return PromptDecision::DENY_FOR_SESSION; +} + +void AceDAOReadOnly::attachToThread() +{ + AceDaoUtilities::m_databaseInterface.AttachToThread(); +} + +void AceDAOReadOnly::detachFromThread() +{ + AceDaoUtilities::m_databaseInterface.DetachFromThread(); +} + +OptionalCachedPromptDecision AceDAOReadOnly::getPromptDecision( + const DPL::String &hash, + const std::string &userParam) +{ + Try { + ScopedTransaction transaction(&AceDaoUtilities::m_databaseInterface); + + // get matching subject verdict + ACE_DB_SELECT(select, AcePromptDecision, &AceDaoUtilities::m_databaseInterface); + DPL::String DPLParam = DPL::FromUTF8String(userParam); + + select->Where( + And( + Equals(hash), + Equals(DPLParam))); + + std::list rows = select->GetRowList(); + if (rows.empty()) { + transaction.Commit(); + return OptionalCachedPromptDecision(); + } + + AcePromptDecision::Row row = rows.front(); + CachedPromptDecision decision; + decision.decision = intToPromptDecision(row.Get_decision()); + decision.session = row.Get_session(); + + return OptionalCachedPromptDecision(decision); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to getPromptDecision"); + } +} + +OptionalCachedPromptDecision AceDAOReadOnly::getPromptDecision( + const BaseAttributeSet &attributes, + const std::string &userParam) +{ + return getPromptDecision(convertToHash(attributes), userParam); +} + +void AceDAOReadOnly::getAttributes(BaseAttributeSet *attributes) +{ + if (NULL == attributes) { + LogError("NULL pointer"); + return; + } + attributes->clear(); + std::string aname; + int type; + Try { + ScopedTransaction transaction(&AceDaoUtilities::m_databaseInterface); + + ACE_DB_SELECT(select, AceAttribute, &AceDaoUtilities::m_databaseInterface); + typedef std::list RowList; + RowList list = select->GetRowList(); + + FOREACH(i, list) { + BaseAttributePtr attribute(new BaseAttribute()); + DPL::String name = i->Get_name(); + aname = DPL::ToUTF8String(name); + type = i->Get_type(); + + attribute->setName(&aname); + attribute->setType(intToAttributeType(type)); + attributes->insert(attribute); + } + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to getAttributes"); + } +} + +OptionalPolicyResult AceDAOReadOnly::getPolicyResult( + const BaseAttributeSet &attributes) +{ + + auto attrHash = convertToHash(attributes); + return getPolicyResult(attrHash); +} + +OptionalPolicyResult AceDAOReadOnly::getPolicyResult( + const DPL::String &attrHash) +{ + Try { + ScopedTransaction transaction(&AceDaoUtilities::m_databaseInterface); + + // get matching subject verdict + ACE_DB_SELECT(select, AcePolicyResult, &AceDaoUtilities::m_databaseInterface); + Equals e1(attrHash); + select->Where(e1); + + std::list rows = select->GetRowList(); + if (rows.empty()) { + transaction.Commit(); + return OptionalPolicyResult(); + } + + AcePolicyResult::Row row = rows.front(); + int decision = row.Get_decision(); + PolicyResult res = PolicyResult::deserialize(decision); + transaction.Commit(); + return OptionalPolicyResult(res); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to getVerdict"); + } +} + +PreferenceTypes AceDAOReadOnly::getDevCapSetting(const std::string &resource) +{ + Try { + AceDevCap::Row row; + if (!getResourceByUri(resource, row)) { + return PreferenceTypes::PREFERENCE_DEFAULT; + } + return intToPreference(row.Get_general_setting()); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to getResourceSetting"); + } +} + +void AceDAOReadOnly::getDevCapSettings(PreferenceTypesMap *globalSettingsMap) +{ + if (NULL == globalSettingsMap) { + LogError("Null pointer"); + return; + } + globalSettingsMap->clear(); + Try { + ACE_DB_SELECT(select, AceDevCap, &AceDaoUtilities::m_databaseInterface); + typedef std::list RowList; + RowList list = select->GetRowList(); + + FOREACH(i, list) { + PreferenceTypes p = intToPreference(i->Get_general_setting()); + globalSettingsMap->insert(make_pair(DPL::ToUTF8String( + i->Get_id_uri()), p)); + } + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to getResourceSettings"); + } +} + + + +void AceDAOReadOnly::getWidgetDevCapSettings(BasePermissionList *outputList) +{ + if (NULL == outputList) { + LogError("NULL pointer"); + return; + } + outputList->clear(); + Try { + ScopedTransaction transaction(&AceDaoUtilities::m_databaseInterface); + + std::string resourceName; + PreferenceTypes allowAccess; + + ACE_DB_SELECT(select, + AceWidgetDevCapSetting, + &AceDaoUtilities::m_databaseInterface); + + typedef std::list RowList; + RowList list = select->GetRowList(); + + // TODO JOIN + FOREACH(i, list) { + int app_id = i->Get_app_id(); + int res_id = i->Get_resource_id(); + + ACE_DB_SELECT(resourceSelect, AceDevCap, &AceDaoUtilities::m_databaseInterface); + resourceSelect->Where(Equals(res_id)); + AceDevCap::Row rrow = resourceSelect->GetSingleRow(); + + resourceName = DPL::ToUTF8String(rrow.Get_id_uri()); + + if (!resourceName.empty()) { + allowAccess = intToPreference(i->Get_access_value()); + outputList->push_back( + BasePermission(app_id, + resourceName, + allowAccess)); + } + } + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to findUserSettings"); + } +} + +PreferenceTypes AceDAOReadOnly::getWidgetDevCapSetting( + const std::string &resource, + WidgetHandle handler) +{ + Try { + ScopedTransaction transaction(&AceDaoUtilities::m_databaseInterface); + + AceDevCap::Row rrow; + if (!getResourceByUri(resource, rrow)) { + return PreferenceTypes::PREFERENCE_DEFAULT; + } + int resourceId = rrow.Get_resource_id(); + + // get matching user setting + ACE_DB_SELECT(select, AceWidgetDevCapSetting, &AceDaoUtilities::m_databaseInterface); + + select->Where(And(Equals(resourceId), + Equals(handler))); + + std::list values = + select->GetValueList(); + if (values.empty()) { + return PreferenceTypes::PREFERENCE_DEFAULT; + } + return intToPreference(values.front()); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed in getUserSetting"); + } +} + +void AceDAOReadOnly::getStaticDevCapPermissions( + int widgetHandle, + std::set *permissions) +{ + if (NULL == permissions) { + LogError("NULL pointer"); + return; + } + permissions->clear(); + Try { + ScopedTransaction transaction(&AceDaoUtilities::m_databaseInterface); + + ACE_DB_SELECT(select, AceStaticDevCapPermission, + &AceDaoUtilities::m_databaseInterface); + select->Where( + Equals(widgetHandle)); + std::list devCapNames = + select->GetValueList(); + permissions->insert(devCapNames.begin(), devCapNames.end()); + transaction.Commit(); + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to getStaticDevCapPermissions"); + } +} +} diff --git a/modules/ace/dao/AceDAOUtilities.cpp b/modules/ace/dao/AceDAOUtilities.cpp new file mode 100644 index 0000000..1f8c95e --- /dev/null +++ b/modules/ace/dao/AceDAOUtilities.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file AceDaoReadOnly.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 0.1 + * @brief + */ + +#include +#include +#include + +#include +#include +#include + +namespace AceDB { + +namespace { +const char* ACE_DB_DATABASE = "/opt/dbspace/.ace.db"; +DPL::DB::SqlConnection::Flag::Type ACE_DB_FLAGS = + DPL::DB::SqlConnection::Flag::UseLucene; +} + +DPL::DB::ThreadDatabaseSupport AceDaoUtilities::m_databaseInterface( + ACE_DB_DATABASE, ACE_DB_FLAGS); + +BaseAttribute::Type AceDaoUtilities::intToAttributeType(int val) +{ + switch (val) { + case 0: + return BaseAttribute::Type::Subject; + case 1: + return BaseAttribute::Type::Environment; + case 2: + return BaseAttribute::Type::Resource; + case 3: + return BaseAttribute::Type::FunctionParam; + + default: + Assert(0 && "Unknown Attribute type value"); + return BaseAttribute::Type::Subject; //remove compilation warrning + } +} + +int AceDaoUtilities::attributeTypeToInt(BaseAttribute::Type type) +{ + // we cannot cast enum -> int because this cast will be removed from next c++ standard + switch (type) { + case BaseAttribute::Type::Subject: + return 0; + case BaseAttribute::Type::Environment: + return 1; + case BaseAttribute::Type::Resource: + return 2; + case BaseAttribute::Type::FunctionParam: + return 3; + + default: + Assert(0 && "Unknown Attribute type!"); + return 0; //remove compilation warrning + } +} + +int AceDaoUtilities::preferenceToInt(PreferenceTypes p) +{ + switch (p) { + case PreferenceTypes::PREFERENCE_PERMIT: + return 1; + case PreferenceTypes::PREFERENCE_DENY: + return 0; + case PreferenceTypes::PREFERENCE_BLANKET_PROMPT: + return 2; + case PreferenceTypes::PREFERENCE_SESSION_PROMPT: + return 3; + case PreferenceTypes::PREFERENCE_ONE_SHOT_PROMPT: + return 4; + + default: + return -1; + } +} + +PreferenceTypes AceDaoUtilities::intToPreference(int p) +{ + switch (p) { + case 1: + return PreferenceTypes::PREFERENCE_PERMIT; + case 0: + return PreferenceTypes::PREFERENCE_DENY; + case 2: + return PreferenceTypes::PREFERENCE_BLANKET_PROMPT; + case 3: + return PreferenceTypes::PREFERENCE_SESSION_PROMPT; + case 4: + return PreferenceTypes::PREFERENCE_ONE_SHOT_PROMPT; + + default: + return PreferenceTypes::PREFERENCE_DEFAULT; + } +} + +VerdictTypes AceDaoUtilities::intToVerdict(int v) +{ + switch (v) { + case -1: + return VerdictTypes::VERDICT_UNKNOWN; + case 0: + return VerdictTypes::VERDICT_DENY; + case 1: + return VerdictTypes::VERDICT_PERMIT; + case 2: + return VerdictTypes::VERDICT_INAPPLICABLE; + + default: + Assert(0 && "Cannot convert int to verdict"); + return VerdictTypes::VERDICT_UNKNOWN; // remove compile warrning + } +} + +int AceDaoUtilities::verdictToInt(VerdictTypes v) +{ + switch (v) { + case VerdictTypes::VERDICT_UNKNOWN: + return -1; + case VerdictTypes::VERDICT_DENY: + return 0; + case VerdictTypes::VERDICT_PERMIT: + return 1; + case VerdictTypes::VERDICT_INAPPLICABLE: + return 2; + + default: + Assert(0 && "Unknown Verdict value"); + return -1; // remove compile warrning + } +} + +bool AceDaoUtilities::getSubjectByUri(const std::string &uri, + DPL::DB::ORM::ace::AceSubject::Row &row) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::ace; + ACE_DB_SELECT(select, AceSubject, &m_databaseInterface); + select->Where(Equals(DPL::FromUTF8String(uri))); + std::list rows = select->GetRowList(); + if (rows.empty()) { + return false; + } + + row = rows.front(); + return true; +} + +bool AceDaoUtilities::getResourceByUri(const std::string &uri, + DPL::DB::ORM::ace::AceDevCap::Row &row) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::ace; + ACE_DB_SELECT(select, AceDevCap, &m_databaseInterface); + select->Where(Equals(DPL::FromUTF8String(uri))); + std::list rows = select->GetRowList(); + if (rows.empty()) { + return false; + } + + row = rows.front(); + return true; +} + + +} diff --git a/modules/ace/dao/AceDatabase.cpp b/modules/ace/dao/AceDatabase.cpp new file mode 100644 index 0000000..110f791 --- /dev/null +++ b/modules/ace/dao/AceDatabase.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file AceDatabase.cpp + * @author Lukasz Marek (l.marek@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of ace database + */ + +#include + +DPL::Mutex g_aceDbQueriesMutex; diff --git a/modules/ace/dao/BaseAttribute.cpp b/modules/ace/dao/BaseAttribute.cpp new file mode 100644 index 0000000..095e2f5 --- /dev/null +++ b/modules/ace/dao/BaseAttribute.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file BaseAttribute.cpp + * @author Lukasz Marek (l.marek@samsung.com) + * @version 0.1 + * @brief + */ + +#include +#include + +#include + +namespace AceDB { + +const char* BaseAttribute::typeToString(Type type) +{ + const char * ret = NULL; + switch (type) { + case Type::Resource: + ret = "resource"; + break; + case Type::Subject: + ret = "subject"; + break; + case Type::Environment: + ret = "environment"; + break; + default: + ret = "unknown type"; + break; + } + + return ret; +} + +std::string BaseAttribute::toString() const +{ + std::string ret; + const char * SEPARATOR = ";"; + + ret.append(m_name); + ret.append(SEPARATOR); + ret.append(typeToString(m_typeId)); + ret.append(SEPARATOR); + if (m_undetermindState) { + ret.append("true"); + } else { + ret.append("false"); + } + ret.append(SEPARATOR); + for (std::list::const_iterator it = value.begin(); + it != value.end(); + ++it) { + std::stringstream num; + num << it->size(); + ret.append(num.str()); + ret.append(SEPARATOR); + ret.append(*it); + ret.append(SEPARATOR); + } + + return ret; +} + +} diff --git a/modules/ace/dao/CMakeLists.txt b/modules/ace/dao/CMakeLists.txt new file mode 100644 index 0000000..a19dcae --- /dev/null +++ b/modules/ace/dao/CMakeLists.txt @@ -0,0 +1,110 @@ + +SET(ACE_DAO_DEPS_LIST + ecore + appcore-efl + openssl + vconf + db-util + libpcrecpp + icu-uc + libxml-2.0 + ) + +PKG_CHECK_MODULES(ACE_DAO_DEPS ${ACE_DAO_DEPS_LIST} REQUIRED) + +set(ACE_SRC_DIR ${PROJECT_SOURCE_DIR}/modules/ace/dao) + +set(ACE_DAO_RO_SOURCES + ${ACE_SRC_DIR}/AceDAOReadOnly.cpp + ${ACE_SRC_DIR}/AceDAOUtilities.cpp + ${ACE_SRC_DIR}/AceDAOConversions.cpp + ${ACE_SRC_DIR}/BaseAttribute.cpp + ${ACE_SRC_DIR}/AceDatabase.cpp + ${ACE_SRC_DIR}/PromptModel.cpp +) + +set(ACE_DAO_RW_SOURCES + ${ACE_SRC_DIR}/AceDAO.cpp +) + +INCLUDE_DIRECTORIES(${ACE_SRC_DIR}) +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/modules/log/include) +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/modules/db/include) +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/modules/core/include) +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/modules/ace/include) +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/modules/ace/orm) +INCLUDE_DIRECTORIES(${ACE_DAO_DEPS_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/modules/ace/orm) + +ADD_LIBRARY(${TARGET_ACE_DAO_RO_LIB} SHARED + ${ACE_DAO_RO_SOURCES} +) + +SET_TARGET_PROPERTIES(${TARGET_ACE_DAO_RO_LIB} PROPERTIES + SOVERSION ${VERSION}) + +SET_TARGET_PROPERTIES(${TARGET_ACE_DAO_RO_LIB} PROPERTIES + COMPILE_FLAGS -fPIC) + +SET_TARGET_PROPERTIES(${TARGET_ACE_DAO_RO_LIB} PROPERTIES + COMPILE_FLAGS "-include ${CMAKE_BINARY_DIR}/modules/ace/database_checksum_ace.h") + +target_link_libraries(${TARGET_ACE_DAO_RO_LIB} + ${TARGET_DPL_EFL} + ${TARGET_DPL_DB_EFL} + ${ACE_DAO_DEPS_LIBRARY} + ${ACE_DAO_DEPS_LDFLAGS} +) + +ADD_LIBRARY(${TARGET_ACE_DAO_RW_LIB} SHARED + ${ACE_DAO_RW_SOURCES} +) + +SET_TARGET_PROPERTIES(${TARGET_ACE_DAO_RW_LIB} PROPERTIES + SOVERSION ${VERSION}) + +SET_TARGET_PROPERTIES(${TARGET_ACE_DAO_RW_LIB} PROPERTIES + COMPILE_FLAGS -fPIC) + +SET_TARGET_PROPERTIES(${TARGET_ACE_DAO_RW_LIB} PROPERTIES + COMPILE_FLAGS "-include ${CMAKE_BINARY_DIR}/modules/ace/database_checksum_ace.h") + +target_link_libraries(${TARGET_ACE_DAO_RW_LIB} + ${TARGET_DPL_EFL} + ${TARGET_DPL_DB_EFL} + ${TARGET_ACE_DAO_RO_LIB} +) + +INSTALL(TARGETS ${TARGET_ACE_DAO_RO_LIB} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE +) + +INSTALL(TARGETS ${TARGET_ACE_DAO_RW_LIB} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE +) + +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/modules/ace/include/dpl/ace-dao-ro/PromptModel.h + ${PROJECT_SOURCE_DIR}/modules/ace/include/dpl/ace-dao-ro/PreferenceTypes.h + ${PROJECT_SOURCE_DIR}/modules/ace/include/dpl/ace-dao-ro/BaseAttribute.h + ${PROJECT_SOURCE_DIR}/modules/ace/include/dpl/ace-dao-ro/BasePermission.h + ${PROJECT_SOURCE_DIR}/modules/ace/include/dpl/ace-dao-ro/VerdictTypes.h + ${PROJECT_SOURCE_DIR}/modules/ace/include/dpl/ace-dao-ro/TimedVerdict.h + ${PROJECT_SOURCE_DIR}/modules/ace/include/dpl/ace-dao-ro/IRequest.h + ${PROJECT_SOURCE_DIR}/modules/ace/include/dpl/ace-dao-ro/ValidityTypes.h + ${PROJECT_SOURCE_DIR}/modules/ace/include/dpl/ace-dao-ro/AceDAOReadOnly.h + ${PROJECT_SOURCE_DIR}/modules/ace/include/dpl/ace-dao-ro/AceDatabase.h + ${PROJECT_SOURCE_DIR}/modules/ace/include/dpl/ace-dao-ro/wrt_db_types.h + ${PROJECT_SOURCE_DIR}/modules/ace/include/dpl/ace-dao-ro/common_dao_types.h + ${PROJECT_SOURCE_DIR}/modules/ace/include/dpl/ace-dao-ro/AceDAOConversions.h + DESTINATION include/dpl-efl/dpl/ace-dao-ro +) + +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/modules/ace/include/dpl/ace-dao-rw/AceDAO.h + DESTINATION include/dpl-efl/dpl/ace-dao-rw +) diff --git a/modules/ace/dao/PromptModel.cpp b/modules/ace/dao/PromptModel.cpp new file mode 100644 index 0000000..d3c82a0 --- /dev/null +++ b/modules/ace/dao/PromptModel.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* @file PromptModel.cpp + * @author Justyna Mejzner (j.kwiatkowsk@samsung.com) + * @author Jaroslaw Osmanski (j.osmanski@samsung.com) + * @version 1.0 + * + */ + +#include + +#include +#include +#include + +namespace { + +const char INFO[] = "Widget requires access to:"; +const char DENY[] = "Deny"; +const char ALLOW[] = "Permit"; + +const char BLANKET_CHECKBOX_LABEL[] = "Keep setting as permanent"; +const char SESSION_CHECKBOX_LABEL[] = "Remember for one run"; + +Prompt::ButtonLabels aceQuestionLabel = {DENY, ALLOW}; + +static Prompt::PromptLabels* getModel( + Prompt::PromptModel::PromptType promptType, + const std::string& resourceId) +{ + std::string strLabel; + strLabel = INFO; + strLabel += "
"; + strLabel += resourceId; + + return new Prompt::PromptLabels(promptType, aceQuestionLabel, strLabel); +} + +Prompt::Validity fromPromptTypeToValidity(int aPromptType, bool checkClicked) +{ + using namespace Prompt; + PromptModel::PromptType promptTypeEnum = + static_cast(aPromptType); + switch (promptTypeEnum) { + case PromptModel::PROMPT_ONESHOT: + return Validity::ONCE; + case PromptModel::PROMPT_SESSION: + if (checkClicked) + { + return Validity::SESSION; + } + else + { + return Validity::ONCE; + } + case PromptModel::PROMPT_BLANKET: + if (checkClicked) + { + return Validity::ALWAYS; + } + else + { + return Validity::ONCE; + } + default: + Assert(0); + return Validity::ONCE; + } +} +} // namespace anonymous + +namespace Prompt { + + +PromptLabels::PromptLabels(int promptType, + const Prompt::ButtonLabels& questionLabel, + const std::string& mainLabel) : + m_promptType(promptType), + m_buttonLabels(questionLabel), + m_mainLabel(mainLabel) +{ + +} + +int PromptLabels::getPromptType() const +{ + return m_promptType; +} +const ButtonLabels& PromptLabels::getButtonLabels() const +{ + return m_buttonLabels; +} +const std::string& PromptLabels::getMainLabel() const +{ + return m_mainLabel; +} + +DPL::OptionalString PromptLabels::getCheckLabel() const +{ + if (PromptModel::PROMPT_BLANKET == m_promptType) + { + return DPL::OptionalString( + DPL::FromUTF8String(BLANKET_CHECKBOX_LABEL)); + } + else if (PromptModel::PROMPT_SESSION == m_promptType) + { + return DPL::OptionalString( + DPL::FromUTF8String(SESSION_CHECKBOX_LABEL)); + } + + return DPL::OptionalString::Null; +} + +bool PromptLabels::isAllowed(const size_t buttonClicked) const +{ + Assert(buttonClicked < aceQuestionLabel.size() && + "Button Clicked number is not in range of questionLabel"); + + return aceQuestionLabel[buttonClicked] == ALLOW; +} + +PromptAnswer::PromptAnswer(bool isAccessAllowed, Validity validity) : + m_isAccessAllowed(isAccessAllowed), + m_validity(validity) +{ + +} + +PromptAnswer::PromptAnswer( + int aPromptType, unsigned int buttonAns, bool checkAns) +{ + Assert(buttonAns < aceQuestionLabel.size() && + "Button Clicked number is not in range of questionLabel"); + + m_isAccessAllowed = aceQuestionLabel[buttonAns] == ALLOW; + m_validity = fromPromptTypeToValidity(aPromptType, checkAns); +} + +bool PromptAnswer::isAccessAllowed() const +{ + return m_isAccessAllowed; +} + +Validity PromptAnswer::getValidity() const +{ + return m_validity; +} + +PromptLabels* PromptModel::getOneShotModel(const std::string& resourceId) +{ + return getModel(PROMPT_ONESHOT, resourceId); +} + +PromptLabels* PromptModel::getSessionModel(const std::string& resourceId) +{ + return getModel(PROMPT_SESSION, resourceId); +} + +PromptLabels* PromptModel::getBlanketModel(const std::string& resourceId) +{ + return getModel(PROMPT_BLANKET, resourceId); +} + + +} // Prompt diff --git a/modules/ace/dao/common_dao_types.cpp b/modules/ace/dao/common_dao_types.cpp new file mode 100644 index 0000000..94e1222 --- /dev/null +++ b/modules/ace/dao/common_dao_types.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * @file common_dao_types.h + * @author Michal Ciepielski (m.ciepielski@samsung.com) + * @version 1.0 + * @brief This file contains the implementation of common data types for wrtdb + */ + +#include + +#include + +namespace WrtDB { +namespace Powder { + +Description::LevelEntry::LevelEntry(LevelEnum level) : + level(level) +{ +} + +bool Description::LevelEntry::isContextValid(LevelEnum level, + const DPL::OptionalString& aContext) const +{ + if (!aContext) { + return level < level; + } else { + Context::const_iterator iter = context.find(*aContext); + if (iter != context.end()) { + return level < level; + } else { + return true; + } + } +} + +bool Description::CategoryEntry::isCategoryValid(LevelEntry& aReason, + LevelEnum aLevel, + const DPL::OptionalString& aContext) const +{ + for (LevelsContainer::const_iterator iter = levels.begin(); + iter != levels.end(); ++iter) { + if (!iter->isContextValid(aLevel, aContext)) { + aReason = *iter; + return false; + } + } + return true; +} +} + +namespace ChildProtection +{ + +PowderRules::CategoryRule::CategoryRule(const DPL::String& aCategory, + Powder::Description::LevelEnum aLevel, + const DPL::OptionalString& aContext) : + category(aCategory), + level(aLevel), + context(aContext) +{ +} + +PowderRules::PowderResult::PowderResult(InvalidReason aReason, + const Powder::Description::LevelEntry& anInvalidDescription, + const CategoryRule& anInvalidRule) : + invalidDescription(anInvalidDescription), + invalidRule(anInvalidRule), + reason(aReason) +{ +} + +//! Function checks if rule is fulfilled by description +//! \param[in] rule checked rule +//! \param[in] description +//! \retval true rule is valid +//! \retval false rule is invalid +PowderRules::ResultPair PowderRules::isRuleValidForDescription( + const CategoryRule& aRule, + const Powder::Description& aDescription) const +{ + Powder::Description::CategoryEntries::const_iterator + iter = aDescription.categories.find(aRule.category); + if (iter != aDescription.categories.end()) { + Powder::Description::LevelEntry invalidDescription; + if (!iter->second.isCategoryValid(invalidDescription, aRule.level, + aRule.context)) { + LogWarning("Widget forbidden for children detected"); + return std::make_pair(false, + PowderResult(PowderResult::InvalidRule, + invalidDescription, + aRule)); + } else { + return std::make_pair(true, PowderResult()); + } + } else { + return std::make_pair(true, PowderResult()); + } +} + +//! Function checks if age limit is fulfilled by description +//! \param[in] description +//! \retval true age is valid +//! \retval false age is invalid +PowderRules::ResultPair PowderRules::isAgeValidForDescription( + const Powder::Description& aDescription) const +{ + if (!ageLimit) { + return std::make_pair(true, PowderResult()); + } else { + if (!!aDescription.ageRating) { + if (*aDescription.ageRating <= *ageLimit) { + return std::make_pair(true, PowderResult()); + } else { + return std::make_pair(false, + PowderResult(PowderResult::InvalidAge)); + } + } else { + if (!isAgeRatingRequired) { + return std::make_pair(true, PowderResult()); + } else { + return std::make_pair( + false, + PowderResult(PowderResult::AgeRatingNotSet)); + } + } + } +} + +//! Function check if Widget description is valid for ChildProtection +//! configuration +//! \param description widget description +//! \retval true widget is valid +//! \retval false widget is invalid +PowderRules::ResultPair PowderRules::isDescriptionValid( + const Powder::Description& aDescription) const +{ + ResultPair powderResult; + for (RulesContainer::const_iterator iter = rules.begin(); + iter != rules.end(); ++iter) { + powderResult = isRuleValidForDescription(*iter, aDescription); + if (!powderResult.first) { + return powderResult; + } + } + return isAgeValidForDescription(aDescription); +} + +} +} // namespace WrtDB diff --git a/modules/ace/engine/Attribute.cpp b/modules/ace/engine/Attribute.cpp new file mode 100644 index 0000000..45a8221 --- /dev/null +++ b/modules/ace/engine/Attribute.cpp @@ -0,0 +1,913 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +const bool Attribute::alpha[256] = { + 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, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 +}; +const bool Attribute::digit[256] = { + 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, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 +}; + +const bool Attribute::mark[256] = { + 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, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 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, 1, + 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, 1, 0 +}; + +bool Attribute::searchAndCut(const char *str) +{ + //TODO + size_t pos = m_name.rfind(str); + if (pos == std::string::npos) { + return false; + } + if ((strlen(str) + pos) == m_name.size()) { + m_name.erase(pos, std::string::npos); + return true; + } + return false; +} + +Attribute::Attribute(const std::string *name, + const Match matchFunc, + const Type type_) : + matchFunction(matchFunc) +{ + m_name = *name; + m_typeId = type_; + m_undetermindState = false; + if (matchFunction != Match::Equal + && matchFunction != Match::Glob + && matchFunction != Match::Regexp) + { + //LogDebug("MID: " << matchFunction); + Assert(0 && "Match function problem"); + } + + if (searchAndCut(".scheme")) { + modifierFunction = Modifier::Scheme; + } else if (searchAndCut(".authority")) { + modifierFunction = Modifier::Authority; + } else if (searchAndCut(".scheme-authority")) { + modifierFunction = Modifier::SchemeAuthority; + } else if (searchAndCut(".host")) { + modifierFunction = Modifier::Host; + } else if (searchAndCut(".path")) { + modifierFunction = Modifier::Path; + } else { + modifierFunction = Modifier::Non; + } +} + +static Attribute::MatchResult equal_comparator(const std::string *first, + const std::string *second) +{ + if((*first) == (*second)) { + return Attribute::MatchResult::MRTrue; + } + return Attribute::MatchResult::MRFalse; +} + +static Attribute::MatchResult glob_comparator(const std::string *first, + const std::string *second) +{ + // order is important + if (!fnmatch(first->c_str(), second->c_str(), 0)) { + return Attribute::MatchResult::MRTrue; + } + return Attribute::MatchResult::MRFalse; +} + +static Attribute::MatchResult regexp_comparator(const std::string *first, + const std::string *second) +{ + // order is important + pcrecpp::RE re(first->c_str()); + if (re.FullMatch(second->c_str())) { + return Attribute::MatchResult::MRTrue; + } + return Attribute::MatchResult::MRFalse; +} + +Attribute::MatchResult Attribute::lists_comparator( + const std::list *first, + const std::list *second, + Attribute::MatchResult (*comparator)(const std::string *, + const std::string *)) const +{ + //NOTE: BONDI defines all availabe matching function as: if some string from first input bag + //matches some input string from second input bag, so it's required to find only one matching string + MatchResult result = MatchResult::MRFalse; + + for (std::list::const_iterator second_iter = second->begin(); + (second_iter != second->end()) && (result != MatchResult::MRTrue); + ++second_iter) + { + std::string *modified_value = applyModifierFunction(&(*second_iter)); + //Value was not an URI, it will be removed from the string bag (ignored) + if (modified_value == NULL) { + continue; + } + + for (std::list::const_iterator first_iter = first->begin(); + first_iter != first->end(); + ++first_iter) { + //Compare attributes + if ((*comparator)(&(*first_iter), modified_value) == MatchResult::MRTrue) { + result = MatchResult::MRTrue; + break; //Only one match is enough + } + } + if (modified_value) { + delete modified_value; + modified_value = NULL; + } + } + + if (result == MatchResult::MRTrue) { + LogDebug("Returning TRUE"); + } else if (result == MatchResult::MRFalse) { + LogDebug("Returning FALSE"); + } else if (result == MatchResult::MRUndetermined) { + LogDebug("Returning UNDETERMINED"); + } + return result; +} + +std::string * Attribute::applyModifierFunction(const std::string * val) const +{ + std::string * result = NULL; + switch (modifierFunction) { + case Modifier::Scheme: + result = uriScheme(val); + break; + case Modifier::Authority: + result = uriAuthority(val); + break; + case Modifier::SchemeAuthority: + result = uriSchemeAuthority(val); + break; + case Modifier::Host: + result = uriHost(val); + break; + case Modifier::Path: + result = uriPath(val); + break; + default: + result = new std::string(*val); + } + + return result; +} + +/** + * this - attribute obtained from xmlPolicy tree + * attribute - attribute obtained from PIP + */ +Attribute::MatchResult Attribute::matchAttributes( + const BaseAttribute *attribute) const +{ + std::string tempNam = *(attribute->getName()); + std::string tempVal; + std::string myVal; + + if (!(attribute->getValue()->empty())) { + tempVal = attribute->getValue()->front(); + } + + if (!(this->value.empty())) { + myVal = this->value.front(); + } + + LogDebug("Comparing attribute: " << this->m_name << "(" << + myVal << ") with: " << tempNam << + "(" << tempVal << ")"); + + Assert( + (this->m_name == *(attribute->getName())) && + "Two completely different attributes are being compared!"); + Assert( + (this->m_typeId == attribute->getType()) && + "Two completely different attributes are being compared!"); + + if (attribute->isUndetermind()) { + LogDebug("Attribute match undetermined"); + return MatchResult::MRUndetermined; + } + + //Regardles the algorithm used, if we have empty + //bag the result is always false + if (this->isValueEmpty() || attribute->isValueEmpty()) { + if (this->isValueEmpty()) { + LogDebug("empty bag in condition comparing"); + } + if (attribute->isValueEmpty()) { + LogDebug("empty bag in attribute comparing"); + } + return MatchResult::MRFalse; + } + + if (this->matchFunction == Match::Equal) { + return lists_comparator(&(this->value), + attribute->getValue(), + equal_comparator); + } else if (this->matchFunction == Match::Glob) { + return lists_comparator(&(this->value), + attribute->getValue(), + glob_comparator); + } else if (this->matchFunction == Match::Regexp) { + return lists_comparator(&(this->value), + attribute->getValue(), + regexp_comparator); + } //[CR] Change to Assert + Assert(false && " ** Critical :: no match function selected!"); + return MatchResult::MRFalse; // to remove compilator warning +} + +void Attribute::addValue(const std::string *val) +{ + this->getValue()->push_back(*val); +} + +std::ostream & operator<<(std::ostream & out, + const Attribute & attr) +{ + out << "attr: m_name: " << *(attr.getName()) + << " type: " << Attribute::typeToString(attr.getType()) + << " value: "; + if (attr.m_undetermindState) { + out << "Undetermined"; + } else if (attr.getValue()->empty()) { + out << "Empty string bag"; + } else { + FOREACH (it, *attr.getValue()) { + out << *it; + } + } + return out; +} + +bool +Attribute::parse(const std::string *input, + std::string *val) const +{ + static const char *pattern = + "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"; + pcrecpp::RE re(pattern); + re.FullMatch(input->c_str(), &val[0], &val[1], + &val[2], &val[3], &val[4], + &val[5], &val[6], &val[7], &val[8]); + +#ifdef ALL_LOGS + for (int i = 0; i < 9; i++) { + LogDebug("val " << i << " :" << val[i]); + } +#endif + + if (find_error(val)) { + LogDebug("Input is not an URI " << *input); + for (int i = 0; i < 9; ++i) { + val[i].clear(); + } + return false; + } + + return true; +} + +Attribute::Attribute(std::istream& is) +{ + Serializer* serializer = Serializer::getInstance(); + + m_name = serializer->deserializeString(is); + m_typeId = serializer->deserializeType(is); + + setValue(serializer->deserializeListStrings(is)); + + matchFunction = serializer->deserializeMatch(is); + modifierFunction = serializer->deserializeModifier(is); +} + +Attribute::~Attribute() +{ +} + +bool Attribute::serialize(std::ostream& os) const +{ + Serializer* serializer = Serializer::getInstance(); + + serializer->serializeString(os, m_name); + serializer->serializeType(os, m_typeId); + serializer->serializeListStrings(os, value); + + serializer->serializeMatch(os, matchFunction); + serializer->serializeModifier(os, modifierFunction); + return 0; +} + +std::string * Attribute::uriScheme(const std::string *input) const +{ + std::string part[9]; + if (!parse(input, part)) { + return NULL; + } + return new string(part[1]); +} + +std::string * +Attribute::uriAuthority(const std::string *input) const +{ + std::string part[9]; + if (!parse(input, part)) { + return NULL; + } + return new string(part[3]); +} + +std::string * +Attribute::uriSchemeAuthority(const std::string *input) const +{ + std::string part[9]; + if (!parse(input, part)) { + return NULL; + } + + if (part[0].size() == 0 || part[2].size() == 0) { + return new std::string(); + } + return new string(part[0] + part[2]); +} + +std::string * +Attribute::uriHost(const std::string *input) const +{ + std::string part[9]; + if (!parse(input, part)) { + return NULL; + } + return getHost(&(part[3])); +} + +std::string * +Attribute::uriPath(const std::string *input) const +{ + //TODO right now uriPath leaves leading '/' in uri, this slash is removed from the string + //it's not clear if leading '/' is a part of path component or only the separator + std::string part[9]; + if (!parse(input, part)) { + return NULL; + } + + std::string * temp = NULL; + + if (part[4].at(0) == '/') { + temp = new string(part[4].substr(1, part[4].length() - 1)); + } else { + temp = new string(part[4]); + } + + return temp; +} + +bool Attribute::find_error(const std::string *tab) const +{ + //We are checking tab[1] which contains scheme without ':' at the end + if (!checkScheme(&(tab[1]))) { + LogDebug("Check scheme failed, URI is invalid"); + return true; //error found + } + if (!checkAuthority(&(tab[3]))) { + LogDebug("Check authority failed, URI is invalid"); + return true; //error found + } + + if (!checkPath(&(tab[4]))) { + LogDebug("Check path failed, URI is invalid"); + return true; //error found + } + + return false; +} + +bool Attribute::checkScheme(const std::string *part) const +{ + Assert(part != NULL && "Checking NULLable string. This should never happen"); + + bool result = true; + + //TODO change part->at to data=part->c_str() + //TODO can scheme be empty? In absolute URI no, in relative URI yes + if (part->empty()) { + //Empty string is a correct schema + result = true; + } else if (alpha[(int) (part->at(0))] == 0) { + result = false; // First scheme character must be alpha + } else { + // rest must be alpha or digit or '+' or '-' or '.' + for (unsigned int i = 1; i < part->size(); ++i) { + int c = static_cast(part->at(i)); + if (!isSchemeAllowedCharacter(c)) { + result = false; + break; + } + } + } + return result; +} + +bool Attribute::checkAuthority(const std::string *part) const +{ + Assert(part != NULL && "Checking NULLable string. This should never happen"); + + //Server is a subset of reg_m_names so here we only check if authority matches reg_m_name + //Additional check if authority is a valid 'server' component is done in getHost + if (part->empty()) { + return true; //empty authority is valid uri + } + bool result = true; + + const char * data = part->c_str(); + for (size_t i = 0; i < part->length(); ++i) { + int c = (int) data[i]; + if (isUnreserved(c)) { + continue; + } + if (c == '$') { + continue; + } + if (c == ',') { + continue; + } + if (c == ';') { + continue; + } + if (c == ':') { + continue; + } + if (c == '@') { + continue; + } + if (c == '&') { + continue; + } + if (c == '=') { + continue; + } + if (c == '+') { + continue; + } + if (c == '%') { + if (isEscaped(data + i)) { + i += 2; //rewind the two escaped characters + continue; + } + } + result = false; + break; + } + + return result; +} + +std::string * Attribute::getHost(const std::string *part) const +{ + if (part->empty()) { + return new std::string(""); + } + + //Check userinfo + size_t userInfoPos = part->find("@"); + if (userInfoPos != std::string::npos) { + std::string data = part->substr(0, userInfoPos); + if (!isUserInfoAllowedString(&data)) { + return new string(""); //the authority is not composed of 'server' part + } + } + + std::string host; + //If we use host modifier then authority is composed of 'server' part so + //the port must contain only digits + size_t portPos = part->find(":"); + if (portPos != std::string::npos) { + for (unsigned int i = portPos + 1; i < part->size(); ++i) { + if (!digit[(int) part->at(i)]) { + return new string(""); //the authority is not composed of 'server' part + } + } + host = part->substr(userInfoPos + 1, portPos - (userInfoPos + 1)); + } else { + host = part->substr(userInfoPos + 1, part->length() - (userInfoPos + 1)); + } + + if (!isHostAllowedString(&host)) { + //Even if the string is not allowed for host this can still be a valid uri + return new string(""); + } + + return new std::string(host); +} + +bool Attribute::checkPath(const std::string *part) const +{ + bool result = true; + + const char * data = part->c_str(); + + for (unsigned int i = 0; i < part->size(); ++i) { + int c = data[i]; + if (c == '/') { + //If we found slash then the next character must be a part of segment + //It cannot be '/' so we have to check it immediately + i++; + c = data[i]; + if (!isSegmentAllowedCharacter(c)) { + result = false; + break; + } + } else if (c == ';') { + //Start param part of segment + i++; //Param can be empty so we don't have to check what's right after semicolon + continue; + } else if (c == '%') { + //We have to handle escaped characters differently than other segment allowed characters + //because we need an array + if (isEscaped(data + i)) { + i += 2; + } else { + result = false; + break; + } + } else { + if (!isSegmentAllowedCharacter(c)) { + result = false; + break; + } + } + } + + return result; +} + +bool Attribute::isSchemeAllowedCharacter(int c) const +{ + bool result = false; + if (isAlphanum(c)) { + result = true; + } else if (c == '+') { + result = true; + } else if (c == '-') { + result = true; + } else if (c == '.') { + result = true; + } + + return result; +} + +bool Attribute::isSegmentAllowedCharacter(int c) const +{ + bool result = true; + + // LogDebug("Checking is segment allowed for char "<<(char)c); + + if (isUnreserved(c)) { //do nothing, result = true + } else if (c == ':') { //do nothing, result = true + } else if (c == '@') { //do nothing, result = true + } else if (c == '&') { //do nothing, result = true + } else if (c == '=') { //do nothing, result = true + } else if (c == '+') { //do nothing, result = true + } else if (c == '$') { //do nothing, result = true + } else if (c == ',') { //do nothing, result = true + } else { + result = false; + } + + return result; +} + +bool Attribute::isUserInfoAllowedString(const std::string * str) const +{ + bool result = false; + + const char * data = str->c_str(); + + for (unsigned int i = 0; i < str->length(); ++i) { + int c = data[i]; + if (isUnreserved(c)) { + result = true; + } else if (c == '%') { + //isEsacped method checks if we don't cross array bounds, so we can + //safely give data[i] here + result = isEscaped((data + i)); + if (result == false) { + break; + } + i += 2; //rewind the next two characters sEsacped method checks if we don't cross array bounds, so we can safely rewind + } else if (c == ',') { + result = true; + } else if (c == '$') { + result = true; + } else if (c == '+') { + result = true; + } else if (c == '=') { + result = true; + } else if (c == '&') { + result = true; + } else if (c == '@') { + result = true; + } else if (c == ':') { + result = true; + } + } + return result; +} + +bool Attribute::isUnreserved(int c) const +{ + return isAlphanum(c) || mark[c]; +} + +bool Attribute::isAlphanum(int c) const +{ + return alpha[c] || digit[c]; +} + +bool Attribute::isHex(int c) const +{ + bool result = false; + + if (digit[c]) { + result = true; + } else if (c == 'A') { + result = true; + } else if (c == 'B') { + result = true; + } else if (c == 'C') { + result = true; + } else if (c == 'D') { + result = true; + } else if (c == 'E') { + result = true; + } else if (c == 'F') { + result = true; + } else if (c == 'a') { + result = true; + } else if (c == 'b') { + result = true; + } else if (c == 'c') { + result = true; + } else if (c == 'd') { + result = true; + } else if (c == 'e') { + result = true; + } else if (c == 'f') { + result = true; + } + + return result; +} + +bool Attribute::isEscaped(const char esc[3]) const +{ + if (esc == NULL) { + return false; + } + + if ((esc[0] == 0) || (esc[1] == 0) || (esc[2] == 0)) { + //We get an array that seems to be out of bounds. + //To be on the safe side return here + LogDebug("HEX NULLS"); + return false; + } + + if (esc[0] != '%') { + LogDebug( + "Error: first character of escaped value must be a precent but is " + << + esc[0]); + return false; + } + +#ifdef ALL_LOGS + for (int i = 0; i < 3; i++) { + LogDebug("HEX " << esc[i]); + } +#endif + return isHex((int) esc[1]) && isHex((int) esc[2]); +} + +bool Attribute::isHostAllowedString(const std::string * str) const +{ + bool result = true; + + if (digit[(int) str->at(0)]) { + //IPv4 address + result = isIPv4AllowedString(str); + } else { + //Hostname + result = isHostNameAllowedString(str); + } + + return result; +} + +bool Attribute::isIPv4AllowedString(const std::string * str) const +{ + LogDebug("Is hostIPv4 allowed String for " << *str); + + const char * data = str->c_str(); + bool result = true; + int digitCounter = 0; + int dotCounter = 0; + + for (unsigned int i = 0; i < str->length(); ++i) { + if (data[i] == '.') { + dotCounter++; + digitCounter = 0; + } else if (digit[(int) data[i]]) { + digitCounter++; + if ((digitCounter > 3) || !digitCounter) { + result = false; + break; + } + } else { + result = false; + break; + } + } + if (dotCounter != 3) { + result = false; + } + return result; +} + +bool Attribute::isHostNameAllowedString(const std::string * str) const +{ + LogDebug("Is hostname allowed String for " << *str); + + int lastPosition = 0; //the position of last dot + 1 + const char * data = str->c_str(); + bool finalDot = false; + size_t end = str->length(); + bool result = false; + + for (size_t i = 0; i < end; ++i) { + if (data[i] == '.') { + if (i == str->length() - 1) { //ending dot + //There can be a leading '.' int the hostm_name + finalDot = true; + break; + } else { + //we found domain label + if (!isDomainLabelAllowedString(data + lastPosition, i - + lastPosition)) { + result = false; + goto end; + } + lastPosition = i + 1; //Set position to position of last dot + 1 + } + } + } + + if (finalDot) { + //we have to rewind one position to check the rightmost string + //but only in case we find final dot + end--; + } + //Compare only the rightmost string aaa.bbbb.rightmostString. + result = isTopLabelAllowedString(data + lastPosition, end - lastPosition); + +end: + + if (result) { + LogInfo("Hostname is allowed"); + } else { + LogInfo("Hostname is NOT allowed"); + } + + return result; +} + +bool Attribute::isDomainLabelAllowedString(const char * data, + int length) const +{ + LogDebug( + "Is domain allowed String for " << data << " taking first " << + length << + " chars"); + + if (!isAlphanum((int) data[0]) || !isAlphanum((int) data[length - 1])) { + return false; + } + + for (int i = 0; i < length; i++) { + if ((!isAlphanum(data[i])) && !(data[i] == '-')) { + return false; + } + } + return true; +} + +bool Attribute::isTopLabelAllowedString(const char * data, + int length) const +{ + if ((!alpha[(int) data[0]]) || (!isAlphanum((int) data[length - 1]))) { + return false; + } + + for (int i = 1; i < length - 1; i++) { + if ((!isAlphanum(data[i])) && !(data[i] == '-')) { + return false; + } + } + return true; +} + +void printAttributes(const AttributeSet& attrs) +{ + if (attrs.empty()) { + LogWarning("Empty attribute set"); + } else { + LogDebug("PRINT ATTRIBUTES:"); + for (AttributeSet::const_iterator it = attrs.begin(); + it != attrs.end(); + ++it) + { + LogDebug("name: " << *(*it)->getName()); + } + } +} + +void printAttributes(const std::list & attrs) +{ + if (attrs.empty()) { + LogWarning("Empty attribute set"); + } else { + LogDebug("PRINT ATTRIBUTES:"); + for (std::list::const_iterator it = attrs.begin(); + it != attrs.end(); + ++it + ) { + LogDebug(*it); + } + } +} + +//KW const char * matchResultToString(Attribute::MatchResult result){ +//KW +//KW const char * ret = NULL; +//KW +//KW switch(result){ +//KW +//KW case Attribute::MRTrue: +//KW ret = "true"; +//KW break; +//KW case Attribute::MRFalse: +//KW ret = "false"; +//KW break; +//KW case Attribute::MRUndetermined: +//KW ret = "undetermined"; +//KW break; +//KW default: +//KW ret = "Wrong match result"; +//KW } +//KW +//KW return ret; +//KW +//KW } diff --git a/modules/ace/engine/CombinerImpl.cpp b/modules/ace/engine/CombinerImpl.cpp new file mode 100644 index 0000000..ccc3c9b --- /dev/null +++ b/modules/ace/engine/CombinerImpl.cpp @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : CombinerImpl.cpp +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#include +#include + +#include +#include +#include + +const int CombinerImpl::DenyInt = 0, //mb jk changed the numbers !!!!!!!!!!!!!!!!! +CombinerImpl::UndeterminedInt = 1, //this one is added +CombinerImpl::PromptOneShotInt = 2, +CombinerImpl::PromptSessionInt = 3, +CombinerImpl::PromptBlanketInt = 4, +CombinerImpl::PermitInt = 5, +CombinerImpl::InapplicableInt = 6, +CombinerImpl::NotMatchingTargetInt = 7, +CombinerImpl::ErrorInt = 8; + +std::list * CombinerImpl::convertEffectsToInts( + const std::list * effects) +{ + std::list * intList = new std::list(); + for ( + std::list::const_iterator it = effects->begin(); + it != effects->end(); + ++it + ) { + switch (*it) { + case Deny: + intList->push_back(CombinerImpl::DenyInt); + break; + case Undetermined: //jk mb added + intList->push_back(CombinerImpl::UndeterminedInt); + break; + case Permit: + intList->push_back(CombinerImpl::PermitInt); + break; + case PromptBlanket: + intList->push_back(CombinerImpl::PromptBlanketInt); + break; + case PromptOneShot: + intList->push_back(CombinerImpl::PromptOneShotInt); + break; + case PromptSession: + intList->push_back(CombinerImpl::PromptSessionInt); + break; + case Inapplicable: + intList->push_back(CombinerImpl::InapplicableInt); + break; + case NotMatchingTarget: + intList->push_back(CombinerImpl::NotMatchingTargetInt); + break; + case Error: + intList->push_back(CombinerImpl::ErrorInt); + break; + default: + Assert(false && "Wrong effect"); + } + } + return intList; +} + +Effect CombinerImpl::convertIntToEffect(int intEffect) +{ + switch (intEffect) { + case DenyInt: + return Deny; + + case UndeterminedInt: //jk mb added + return Undetermined; + + case PermitInt: + return Permit; + + case PromptBlanketInt: + return PromptBlanket; + + case PromptOneShotInt: + return PromptOneShot; + + case PromptSessionInt: + return PromptSession; + + case InapplicableInt: + return Inapplicable; + + case NotMatchingTargetInt: + return NotMatchingTarget; + + case ErrorInt: + return Error; + + default: + Assert(false && "Wrong integer in int to Effect conversion"); + return Error; + } +} + +Effect CombinerImpl::denyOverrides(const std::list & effects) +{ + if (isError(effects)) { + return Error; + } + + std::list * intList = convertEffectsToInts(&effects); + + int result = InapplicableInt; //Deny has value 0, Undetermined value 1, Inapplicable value 6 + + std::list::const_iterator it = intList->begin(); + + while (it != intList->end()) { + int effect = *it; + //Use most restrictive policy always + result = effect < result ? effect : result; + ++it; + } + delete intList; + return convertIntToEffect(result); +} + +Effect CombinerImpl::permitOverrides(const std::list & effects) //mb jk modified +{ if (isError(effects)) { + return Error; + } + + int result = DenyInt; //Deny has value 0, Inapplicable value 5 + std::list * intList = convertEffectsToInts(&effects); + std::list::const_iterator it = intList->begin(); + + //Flag used to indicate that any of Deny,prompt-*,permit options appear + //Consequently if flag is true then result should be return, otherwise inapplicable should be returned + bool flag = false; + bool flagUndetermined = false; + + while (it != intList->end()) { + int effect = *it; + + if (effect == PermitInt) { + result = effect; + delete intList; + return convertIntToEffect(result); + } // no need for further check if "permit" found + if (effect == UndeterminedInt) { flagUndetermined = true; } //check for undetermined + + //Set the flag and the result even if effect is equal to result + //It is done to mark if any "Deny" effect occured + if (effect >= result && effect != InapplicableInt && effect != + UndeterminedInt) { //check for other results + result = effect; + flag = true; + } + + ++it; + } + delete intList; + + if (flagUndetermined) { + return Undetermined; + } + + if (!flag) { + return Inapplicable; + } + return convertIntToEffect(result); +} + +Effect CombinerImpl::firstApplicable(const std::list & effects) //jk mb modified to comply with BONDI 1.0 ("undetermined" handling) +{ std::list::const_iterator it = effects.begin(); + + if (isError(effects)) { + return Error; + } + + for (; it != effects.end(); ++it) { + if (*it != InapplicableInt) { + return *it; + } + } + return Inapplicable; +} + +Effect CombinerImpl::firstMatchingTarget(const std::list & effects) +{ + if (isError(effects)) { + return Error; + } + // effect list constains result of policies which target has been matched. + // + // If target does not match policy result is NotMatchingTarget + // NotMatchingTarget values are not stored on the effects list + // (you can check it in combinePolicies function). + // + // So we are intrested in first value on the list. + return effects.empty() ? Inapplicable : effects.front(); +} + +bool CombinerImpl::isError(const std::list & effects) +{ + FOREACH(it, effects) + { + if (Error == *it) { + return true; + } + } + return false; +} + +Effect CombinerImpl::combineRules(const TreeNode * policy) +{ + const Policy * policyObj = dynamic_cast(policy->getElement()); + if (!policyObj) { + LogError("dynamic_cast failed. PolicyObj is null."); + return Error; + } + + Policy::CombineAlgorithm algorithm = policyObj->getCombineAlgorithm(); + + Assert( + algorithm != Policy::FirstTargetMatching && + "Policy cannot have algorithm first target matching"); + + bool isUndetermined = false; + + if (!checkIfTargetMatches(policyObj->getSubjects(), isUndetermined)) { + if (isUndetermined) { + //TODO Target is undetermined what should we do now ?? + //Right now simply return NotMatchingTarget + } + //Target doesn't match + return NotMatchingTarget; + } + //Get all rules + const ChildrenSet & children = policy->getChildrenSet(); + ChildrenConstIterator it = children.begin(); + std::list effects; + + while (it != children.end()) { + const Rule * rule = dynamic_cast((*it)->getElement()); + + if (!rule) { + LogError("Error in dynamic_cast. rule is null"); + return Error; + } + + Effect effect = rule->evaluateRule(this->getAttributeSet()); + effects.push_back(effect); + if (algorithm == Policy::FirstApplicable && effect != Inapplicable) { + //For first applicable algorithm we may stop after evaluating first policy + //which has effect other than inapplicable + break; + } + ++it; + } //end policy children iteration + + //Use combining algorithm + Effect ef = combine(policyObj->getCombineAlgorithm(), effects); + return ef; +} + +//WARNING this method makes an assumption that Policy target is a policy child +Effect CombinerImpl::combinePolicies(const TreeNode * policy) +{ + const Policy * policySet = dynamic_cast(policy->getElement()); + + if (!policySet) { + LogError("dynamic_cast failed. Policy set is null."); + return Error; + } + + bool isUndetermined = false; + Policy::CombineAlgorithm algorithm = policySet->getCombineAlgorithm(); + + if (!checkIfTargetMatches(policySet->getSubjects(), isUndetermined)) { + /* I can't explain this... + if (isUndetermined) { + if (algorithm == Policy::FirstTargetMatching) { + return Undetermined; + } + } + */ + //Target doesn't match + return NotMatchingTarget; + } + + const ChildrenSet & children = policy->getChildrenSet(); + + ChildrenConstIterator it = children.begin(); + + std::list effects; + + while (it != children.end()) { + Effect effect; + + if ((*it)->getTypeID() == TreeNode::PolicySet) { + effect = combinePolicies(*it); + if (effect != NotMatchingTarget) { + effects.push_back(effect); + } + } else if ((*it)->getTypeID() == TreeNode::Policy) { + effect = combineRules(*it); + if (effect != NotMatchingTarget) { + effects.push_back(effect); + } + } else { + // [CR] fix it + LogError("effect value is not initialized!"); + return Error; + } + + if (algorithm == Policy::FirstTargetMatching && effect != + NotMatchingTarget) { + //In First matching target algorithm we may return when first result is found + break; + } + ++it; + } + + //Use combining algorithm + Effect ef = combine(policySet->getCombineAlgorithm(), effects); + return ef; +} + +Effect CombinerImpl::combine(Policy::CombineAlgorithm algorithm, + std::list & effects) +{ + LogDebug("Effects to be combined with algorithm: " << toString(algorithm)); + showEffectList(effects); + + switch (algorithm) { + case Policy::DenyOverride: + return denyOverrides(effects); + break; + case Policy::PermitOverride: + return permitOverrides(effects); + break; + case Policy::FirstApplicable: + return firstApplicable(effects); + break; + case Policy::FirstTargetMatching: + return firstMatchingTarget(effects); + break; + default: + Assert(false && "Wrong combining algorithm used"); + return Error; + } +} + +/** + * + * @param attrSet set of Subject attributes in policy that identifies target + * @return true if target is determined and matches, false and isUndertmined is set to true if the target is undetermined + * false and isUndetermined set to false if target is determined but doesn't match + */ +bool CombinerImpl::checkIfTargetMatches( + const std::list * subjectsList, + bool &isUndetermined) +{ + if (subjectsList->empty()) { + return true; + } + + std::list::const_iterator it = subjectsList->begin(); + bool match = false; + //According to BONDI 1.0 at least one target must match + while (it != subjectsList->end()) { + match = (*it)->matchSubject(this->getAttributeSet(), isUndetermined); + if (match) { //at least one match + break; + } + ++it; + } + + #ifdef _DEBUG + if (match == Attribute::MRTrue) { + LogDebug("Target matches "); + } else if (match == Attribute::MRUndetermined) { + LogDebug("Target match undetermined "); + } else { + LogDebug("Target doesn't match"); + } + #endif + return match; +} + +const char * toString(Effect effect) +{ + const char * temp = ""; + + switch (effect) { + case Deny: + temp = "Deny"; + break; + case Undetermined: + temp = "Undetermined"; + break; + case PromptOneShot: + temp = "PromptOneShot"; + break; + case PromptSession: + temp = "PromptSession"; + break; + case PromptBlanket: + temp = "PromptBlanket"; + break; + case Permit: + temp = "Permit"; + break; + case Inapplicable: + temp = "Inapplicable"; + break; + case NotMatchingTarget: + temp = "NotMatchingTarget"; + break; + case Error: + temp = "Error"; + break; + default: + Assert(false && "Wrong effect"); + } + return temp; +} diff --git a/modules/ace/engine/Condition.cpp b/modules/ace/engine/Condition.cpp new file mode 100644 index 0000000..739e58a --- /dev/null +++ b/modules/ace/engine/Condition.cpp @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// File: Condition.cpp +// Author: notroot +// +// Created on June 3, 2009, 9:00 AM +// + +#include +#include +#include +#include +#include + +/** + * Check if attribute in condition matches the values obtained from PIP + * attrSet - attributes from PIP + */ + +Attribute::MatchResult Condition::evaluateCondition( + const AttributeSet * attrSet) const +{ + //Condition may include either matches of attributes or other conditions + //in this method all attributes are matched at first and if possible the + //condition is evaluated. If evaluation is not possible based solely on + //attributes then we start recursion into child conditions. + + Attribute::MatchResult match; + bool undeterminedMatchFound = false; + bool isFinalMatch = false; + + LogDebug("Attributes to be matched"); + printAttributes(*attrSet); + LogDebug("Condition attributes values"); + printAttributes(attributes); + + if (this->isEmpty()) { + LogDebug("Condition is empty, returning true"); + //Condition is empty, it means it evaluates to TRUE + return Attribute::MatchResult::MRTrue; + } + + match = evaluateAttributes(attrSet, isFinalMatch, undeterminedMatchFound); + if (isFinalMatch) { + LogDebug("Evaluate attributes returning verdict" ) ; //<< match); + return match; + } + + match = evaluateChildConditions(attrSet, + isFinalMatch, + undeterminedMatchFound); + if (isFinalMatch) { + LogDebug("Evaluate child conditions returning verdict" ); // << match); + return match; + } + + if (undeterminedMatchFound) { + //If any child condition/attribute-match was undetermined and + //so far we couldn't make a decision then we must return undetermined + LogDebug("Evaluate condition returning MRUndetermined"); + return Attribute::MatchResult::MRUndetermined; + } + + if (this->isAndCondition()) { + match = Attribute::MatchResult::MRTrue; + } else if (this->isOrCondition()) { + match = Attribute::MatchResult::MRFalse; + } else { + Assert(false && "Condition has to be either AND or OR"); + } + return match; +} + +// KW Attribute::MatchResult Condition::performORalgorithm(const std::set* attrSet) const{ +// KW +// KW Attribute::MatchResult match; +// KW bool undeterminedMatchFound = false; +// KW bool isFinalMatch = false; +// KW +// KW LogDebug("Performing OR algorithm"); +// KW +// KW match = evaluateAttributes(attrSet, isFinalMatch, undeterminedMatchFound); +// KW if(isFinalMatch){ +// KW LogDebug("OR algorithm evaluate attributes returning verdict" << match); +// KW return match; +// KW } +// KW +// KW match = evaluateChildConditions(attrSet, isFinalMatch, undeterminedMatchFound); +// KW if(isFinalMatch){ +// KW return match; +// KW } +// KW +// KW if(undeterminedMatchFound){ +// KW //If any child condition/attribute-match was undetermined and +// KW //so far we couldn't make a decision then we must return undetermined +// KW LogDebug("OR algorithm returning MRUndetermined"); +// KW return Attribute::MRUndetermined; +// KW } +// KW +// KW LogDebug("OR algorithm returning MRFalse"); +// KW return Attribute::MRFalse; +// KW } + +// KW Attribute::MatchResult Condition::performANDalgorithm(const std::set* attrSet) const{ +// KW +// KW +// KW Attribute::MatchResult match; +// KW bool undeterminedMatchFound = false; +// KW bool isFinalMatch = false; +// KW +// KW LogDebug("Performing AND algorithm"); +// KW match = evaluateAttributes(attrSet, isFinalMatch, undeterminedMatchFound); +// KW if(isFinalMatch){ +// KW LogDebug("AND algorithm evaluate attributes returning verdict" << match); +// KW return match; +// KW } +// KW match = evaluateChildConditions(attrSet, isFinalMatch, undeterminedMatchFound); +// KW if(isFinalMatch){ +// KW LogDebug("AND algorithm evaluate child returning verdict " << match); +// KW return match; +// KW } +// KW if(undeterminedMatchFound){ +// KW //If any child condition/attribute-match was undetermined and +// KW //so far we couldn't make a decision then we must return undetermined +// KW LogDebug("AND algorithm returning Undetermined"); +// KW return Attribute::MRUndetermined; +// KW } +// KW +// KW LogDebug("AND algorithm returning MRTrue"); +// KW return Attribute::MRTrue; +// KW +// KW } + +Attribute::MatchResult Condition::evaluateAttributes( + const AttributeSet * attrSet, + bool& isFinalMatch, + bool & undeterminedMatchFound) const +{ + Attribute::MatchResult match = Attribute::MatchResult::MRUndetermined; + + std::list::const_iterator condIt = this->attributes.begin(); + while (condIt != this->attributes.end()) { + //Find the value of needed attribute, based on attribute name + AttributeSet::const_iterator attr = + std::find_if(attrSet->begin(), + attrSet->end(), + AceDB::BaseAttribute::UnaryPredicate(&(*condIt))); + if (attr == attrSet->end()) { + LogError("Couldn't find required attribute. This should not happen"); + Assert( + false && + "Couldn't find attribute required in condition. This should not happen" + "This means that some attributes has not been obtained from PIP"); + //Return undetermined here because it seems one of the attributes is unknown/undetermined + isFinalMatch = true; + match = Attribute::MatchResult::MRUndetermined; + break; + } + + match = condIt->matchAttributes(&(*(*attr))); + if ((match == Attribute::MatchResult::MRFalse) && isAndCondition()) { + //FALSE match found in AND condition + isFinalMatch = true; + break; + } else if ((match == Attribute::MatchResult::MRTrue) && isOrCondition()) { + //TRUE match found in OR condition + isFinalMatch = true; + break; + } else if (match == Attribute::MatchResult::MRUndetermined) { + //Just mark that there was undetermined value found + undeterminedMatchFound = true; + } + ++condIt; + } + + return match; +} + +Attribute::MatchResult Condition::evaluateChildConditions( + const AttributeSet * attrSet, + bool& isFinalMatch, + bool & undefinedMatchFound) const +{ + Attribute::MatchResult match = Attribute::MatchResult::MRUndetermined; + + std::list::const_iterator it = conditions.begin(); + while (it != conditions.end()) { + match = it->evaluateCondition(attrSet); + + if ((match == Attribute::MatchResult::MRFalse) && isAndCondition()) { + //FALSE match found in AND condition + LogDebug("Child conditions results MRFalse)"); + isFinalMatch = true; + break; + } else if ((match == Attribute::MatchResult::MRTrue) && isOrCondition()) { + //TRUE match found in OR condition + LogDebug("Child conditions result MRTrue"); + isFinalMatch = true; + break; + } else if (match == Attribute::MatchResult::MRUndetermined) { + undefinedMatchFound = true; + } + ++it; + } + + return match; +} + +void Condition::getAttributes(AttributeSet * attrSet) +{ + //Get attributes from current condition + FOREACH (it, attributes) + { + AceDB::BaseAttributePtr attr(new Attribute(it->getName(), it->getMatchFunction(), it->getType())); + attrSet->insert(attr); + } + //Get attributes from any child conditions + FOREACH (it, conditions) + { + it->getAttributes(attrSet); + } +} + +Condition::Condition(std::istream& is, + Condition* parent) +{ + Serializer* serializer = Serializer::getInstance(); + + //Condition is a object in class + if (parent != NULL) { + this->parent = parent; + } else { + this->parent = NULL; + } + + conditions = serializer->deserializeListConditions(is, this); + combineType = serializer->deserializeCombineType(is); + attributes = serializer->deserializeListAttributes(is); +} + +bool Condition::serialize(std::ostream& os) +{ + Serializer* serializer = Serializer::getInstance(); + + serializer->serializeListConditions(os, conditions); + serializer->serializeCombineType(os, combineType); + serializer->serializeListAttributes(os, attributes); + + return 0; +} + diff --git a/modules/ace/engine/ConfigurationManager.cpp b/modules/ace/engine/ConfigurationManager.cpp new file mode 100644 index 0000000..9cc7cd7 --- /dev/null +++ b/modules/ace/engine/ConfigurationManager.cpp @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace { +const string currentXMLSchema("bondixml.xsd"); +} + +ConfigurationManager * ConfigurationManager::instance = NULL; + +string ConfigurationManager::getCurrentPolicyFile(void) const +{ + return currentPolicyFile; +} + +string ConfigurationManager::getFullPathToCurrentPolicyFile(void) const +{ + if (*(storagePath.rbegin()) == '/') { + return storagePath + currentPolicyFile; + } + return storagePath + "/" + currentPolicyFile; +} + +string ConfigurationManager::getStoragePath(void) const +{ + return storagePath; +} + +string ConfigurationManager::getFullPathToCurrentPolicyXMLSchema() const +{ + if (*(storagePath.rbegin()) == '/') + { + return storagePath + currentXMLSchema; + } + return storagePath + "/" + currentXMLSchema; +} + +int ConfigurationManager::parse(const string &configFileName) +{ + configFile = configFileName; + int error = PARSER_SUCCESS; + policyFiles.clear(); + + reader = xmlNewTextReaderFilename(configFile.c_str()); + + if (reader == NULL) { + LogError("Parser does not exist"); + error = PARSER_ERROR; + goto cleanUp; + } + + if (xmlTextReaderSetParserProp(reader, XML_PARSER_VALIDATE, 1)) { + LogError("Error while setting parser validating."); + error = PARSER_ERROR; + Assert(false && "Cannot make XML parser validating"); + goto cleanUp; + } + + if (xmlTextReaderSetParserProp(reader, XML_PARSER_DEFAULTATTRS, 1)) { + LogError("Error while attribute defaulting."); + error = PARSER_ERROR; + Assert(false && "XML Parser cannot default xml attributes"); + goto cleanUp; + } + + int ret; + while (1 == (ret = xmlTextReaderRead(reader))) { + processNode(); + if (xmlTextReaderIsValid(reader) == 0) { + LogError( + "Parser error while reading file " << configFile << + ". File is not valid."); + error = PARSER_ERROR; + Assert(false && "Configuration Manager error"); + goto cleanUp; + } + } + if (ret != 0) { + LogError("There were some errors while parsing XML file."); + } + +cleanUp: + if (reader != NULL) { + xmlFreeTextReader(reader); + } + return error; +} + +void ConfigurationManager::extractFileAttributes() +{ + xmlChar *active = xmlTextReaderGetAttribute(reader, ATTR_ACTIVE_POLICY); + xmlActive = false; + + if (active && active[0] == 't') { + xmlActive = true; + } + + xmlFree(active); +} + +void ConfigurationManager::startNodeHandler(void) +{ + xmlChar *name = xmlTextReaderName(reader); + switch (*name) { + case 'f': // file + extractFileAttributes(); + break; + default: + break; + } + xmlFree(name); +} + +void ConfigurationManager::endNodeHandler(void) +{ + xmlChar *name = xmlTextReaderName(reader); + switch (*name) { + case 'f': + policyFiles.push_back(currentText); + if (xmlActive) { + currentPolicyFile = currentText; + xmlActive = false; + } + currentText.clear(); + break; + case 's': + storagePath = currentText; + currentText.clear(); + break; + default: + break; + } + xmlFree(name); +} + +void ConfigurationManager::textNodeHandler(void) +{ + xmlChar *text = xmlTextReaderValue(reader); + currentText = reinterpret_cast(text); + xmlFree(text); +} + +void ConfigurationManager::processNode(void) +{ + xmlReaderTypes type = + static_cast(xmlTextReaderNodeType(reader)); + switch (type) { + case XML_READER_TYPE_ELEMENT: startNodeHandler(); + break; + case XML_READER_TYPE_END_ELEMENT: endNodeHandler(); + break; + case XML_READER_TYPE_TEXT: textNodeHandler(); + break; + default: + break; + } +} + +int ConfigurationManager::addPolicyFile(const string & sourcePath) +{ + FILE * sourceFd = NULL, *destFd = NULL; + int flag = CM_OPERATION_SUCCESS; + int sourceLength; + string fileName; + + string newFilePath(getStoragePath()); + newFilePath.append("/"); + + if (sourcePath.empty()) { + LogError("Filename empty"); + flag = CM_GENERAL_ERROR; + goto cleanup; + } + + fileName = extractFilename(sourcePath); + newFilePath.append(fileName); + + LogDebug("Adding new file " << newFilePath); + if (checkIfFileExistst(newFilePath)) { + LogError("Destination file already exists"); + flag = CM_FILE_EXISTS; + goto cleanup; + } + + sourceFd = fopen(sourcePath.c_str(), "r"); + if (sourceFd == NULL) { + LogError("Source file opening failed"); + flag = CM_GENERAL_ERROR; + goto cleanup; + } + + destFd = fopen(newFilePath.c_str(), "w"); + if (destFd == NULL) { + LogError("Destination file creation failed"); + flag = CM_GENERAL_ERROR; + goto cleanup; + } + + if (0 > (sourceLength = getFileSize(sourcePath))) { + LogError("getFileSize error"); + flag = CM_GENERAL_ERROR; + goto cleanup; + } + + if (!copyFile(sourceFd, destFd, sourceLength)) { + //Copying failed, we have to remove corrupted file + flag = CM_GENERAL_ERROR; + if (removePolicyFile(newFilePath) != CM_OPERATION_SUCCESS) { + flag = CM_REMOVE_ERROR; + } + goto cleanup; + } + policyFiles.push_back(fileName); + + if (saveConfig() != CM_OPERATION_SUCCESS) { + LogError("Error while saving policy file"); + //TODO HOW TO ROLLBACK save config? + } + +cleanup: + + if (sourceFd) { + fclose(sourceFd); + } + if (destFd) { + fclose(destFd); + } + + return flag; +} + +int ConfigurationManager::removePolicyFile(const string& file) +{ + LogDebug("Trying to remove policy " << file); + int errorFlag = CM_OPERATION_SUCCESS; + string fileName = extractFilename(file); + string filePath(CONFIGURATION_MGR_TEST_POLICY_STORAGE); + + if (fileName == currentPolicyFile) { + errorFlag = CM_REMOVE_CURRENT; + LogError("Cannot remove current policy"); + goto end; + } + + filePath.append("/").append(fileName); + + if (remove(filePath.c_str()) != 0) { + if (checkIfFileExistst(filePath)) { + errorFlag = CM_REMOVE_ERROR; + LogError("Cannot delete file" << filePath); + goto end; + } + LogError("Cannot delete file" << filePath << " the file doesn't exist"); + errorFlag = CM_REMOVE_NOT_EXISTING; + } else { + errorFlag = CM_OPERATION_SUCCESS; + } + //If remove was successful or unsuccessful but file doesn't exists then it + //should be removed from the list of available policies + policyFiles.remove(fileName); + +end: + return errorFlag; +} + +string ConfigurationManager::extractFilename(const string & sourcePath) const +{ + //begining of filename without path + size_t filenamePos = sourcePath.rfind('/'); + + string tmp; + if (filenamePos == string::npos) { + tmp = sourcePath; + } else { + tmp = sourcePath.substr(filenamePos + 1); + } + LogDebug("Extracted filename " << tmp); + return tmp; +} + +int ConfigurationManager::getFileSize(const string & path) const +{ + //get source file size + struct stat sourceStat; + int sourceLength; + if (stat(path.c_str(), &sourceStat) == -1) { + LogError("Reading file properties failed"); + sourceLength = -1; + } else { + sourceLength = sourceStat.st_size; + } + return sourceLength; +} + +bool ConfigurationManager::copyFile(FILE * sourceDesc, + FILE * destinationDesc, + int lenght) const +{ + int rv; + char *buffer = static_cast(malloc(lenght)); + bool result = true; + + if (!buffer) { + return false; + } + + while ((rv = fread(buffer, sizeof (char), lenght, sourceDesc)) > 0) { + if (rv != lenght) { + if (!feof(sourceDesc)) { + LogError("Error while reading file "); + result = false; + break; + } + } + + rv = fwrite(buffer, sizeof (char), lenght, destinationDesc); + if (rv != lenght) { + LogError("Write file failed "); + result = false; + break; + } + } + free(buffer); + return result; +} + +bool ConfigurationManager::checkIfFileExistst(const string & newFilePath) const +{ + bool exists = false; + struct stat stats; + if (stat(newFilePath.c_str(), &stats) == 0) { + exists = true; + } + return exists; +} + +int ConfigurationManager::changeCurrentPolicyFile(const std::string & fileName) +{ + int result = CM_OPERATION_SUCCESS; + string oldPolicyFile = currentPolicyFile; + + string filePath(getStoragePath() + "/"); + filePath += fileName; + + if (!checkIfFileExistst(filePath)) { + //Policy file doesn't exist + result = CM_GENERAL_ERROR; + LogError("Error: policy file " << filePath << " doesn't exist"); + goto end; + } + if (result == CM_OPERATION_SUCCESS) { + //Adding file to storage succeeded + currentPolicyFile = extractFilename(filePath); + //Try to save the configuration file + result = saveConfig(); + } + if (result != CM_OPERATION_SUCCESS) { + //rollback changes + currentPolicyFile = oldPolicyFile; + LogError("Error while saving policy file"); + //TODO HOW TO ROLLBACK save config? + } + +end: + return result; +} + +int ConfigurationManager::saveConfig() +{ + std::ofstream output(configFile.c_str()); + int errorFlag = CM_OPERATION_SUCCESS; + + output << "" << std::endl; + output << + "" << + std::endl; + output << "" << std::endl; + output << " " << storagePath << "" << + std::endl; + output << " " << std::endl; + for (list::const_iterator it = policyFiles.begin(); + it != policyFiles.end(); + ++it) { + output << "\t" << *it << "" << std::endl; + } + output << " \n"; + output << " "; + output.close(); + + //TODO should we add 'eof' here? + if (output.bad() || output.fail()) { + //There were some errors while writing to file + errorFlag = CM_GENERAL_ERROR; + } + + return errorFlag; +} diff --git a/modules/ace/engine/NodeFactory.cpp b/modules/ace/engine/NodeFactory.cpp new file mode 100644 index 0000000..a0b93f1 --- /dev/null +++ b/modules/ace/engine/NodeFactory.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +NodeFactory* NodeFactory::pInstance = NULL; + +AbstractTreeElement* NodeFactory::create(std::istream& is, + TreeNode::TypeID id) +{ + AbstractTreeElement* node; + + switch (id) { + case TreeNode::Policy: + node = new Policy(is); + break; + case TreeNode::PolicySet: + node = new PolicySet(is); + break; + case TreeNode::Rule: + node = new Rule(is); + break; + default: + node = NULL; + break; + } + + return node; +} + +NodeFactory* NodeFactory::getInstance() +{ + if (pInstance == NULL) { + pInstance = new NodeFactory; + } + return pInstance; +} diff --git a/modules/ace/engine/Policy.cpp b/modules/ace/engine/Policy.cpp new file mode 100644 index 0000000..1b47a89 --- /dev/null +++ b/modules/ace/engine/Policy.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : Policy.cpp +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#include +#include + +Policy::Policy(std::istream& is) +{ + Serializer* serializer = Serializer::getInstance(); + + std::list* pList; + pList = serializer->deserializeListSubjects(is); + subjects = pList; + + combineAlgorithm = serializer->deserializeCombineAlgorithm(is); +} + +void Policy::serialize(std::ostream& os) +{ + Serializer* serializer = Serializer::getInstance(); + + serializer->serializeListSubjects(os, *subjects); + serializer->serializeCombineAlgorithm(os, combineAlgorithm); +} + +Policy::~Policy() +{ + for (std::list::iterator it = subjects->begin(); + it != subjects->end(); + ++it) { + delete *it; + } + delete subjects; +} + +void Policy::printData() +{ + std::string subject; + if (subjects != NULL && subjects->size()) { + subject = (subjects->front())->getSubjectId(); + } + std::string algorithm = printCombineAlgorithm(this->combineAlgorithm); + + std::cout << "subject: " << subject << " algorithm: " << algorithm << + std::endl; +} + +std::string Policy::printCombineAlgorithm(CombineAlgorithm algorithm) +{ + switch (algorithm) { + case DenyOverride: + return "DenyOverride"; + case PermitOverride: + return "PermitOverride"; + case FirstApplicable: + return "FirstApplicable"; + case FirstTargetMatching: + return "FirstTargetMatching"; + default: + return "ERROR: Wrong Algorithm"; + } +} + +const char * toString(Policy::CombineAlgorithm algorithm) +{ + switch (algorithm) { + case Policy::DenyOverride: + return "DenyOverride"; + case Policy::PermitOverride: + return "PermitOverride"; + case Policy::FirstApplicable: + return "FirstApplicable"; + case Policy::FirstTargetMatching: + return "FirstTargetMatching"; + default: + return "ERROR: Wrong Algorithm"; + } +} diff --git a/modules/ace/engine/PolicyEnforcementPoint.cpp b/modules/ace/engine/PolicyEnforcementPoint.cpp new file mode 100644 index 0000000..d593f02 --- /dev/null +++ b/modules/ace/engine/PolicyEnforcementPoint.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file security_logic.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Ming Jin(ming79.jin@samsung.com) + * @version 1.0 + * @brief Implementation file for security logic + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +using namespace WrtDB; + +PolicyEnforcementPoint::PolicyEnforcementPoint() : + m_wrt(0), + m_res(0), + m_sys(0), + m_pdp(0), + m_pip(0) +{} + +void PolicyEnforcementPoint::terminate() +{ + LogInfo("PolicyEnforcementPoint is being deinitialized."); + + delete m_sys; + delete m_res; + delete m_wrt; + delete m_pdp; + delete m_pip; + m_sys = 0; + m_res = 0; + m_wrt = 0; + m_pdp = 0; + m_pip = 0; +} + +PolicyEnforcementPoint::~PolicyEnforcementPoint() +{ + Assert((m_sys == 0) && "You must run " + "PolicyEnforcementPoint::Deinitialize before exit program!"); +} + +void PolicyEnforcementPoint::initialize( + IWebRuntime *wrt, + IResourceInformation *resource, + IOperationSystem *operation) +{ + if (m_wrt) { + ThrowMsg(PEPException::AlreadyInitialized, + "Policy Enforcement Point is already initialzed"); + } + + m_wrt = wrt; + m_res = resource; + m_sys = operation; + + if (this->m_pip != NULL) { + this->m_pip->update(m_wrt, m_res, m_sys); + return; + } + + this->m_pip = new PolicyInformationPoint(wrt, m_res, m_sys); + this->m_pdp = new PolicyEvaluator(m_pip); + + if (!this->m_pdp->initPDP()) { + Assert(0); + } +} + +PolicyResult PolicyEnforcementPoint::check(Request &request) +{ + return m_pdp->getPolicyForRequest(request); +} + +void PolicyEnforcementPoint::updatePolicy(const std::string &policy) +{ + LogDebug("ACE updatePolicy: " << policy); + int errorCode = 0; + + if (m_pdp == NULL) { + LogError("Evaluator not set. Ignoring message."); + Assert(false && "UpdateClient error on receiving event"); + } else { + LogDebug("Emitting update signal."); + errorCode = m_pdp->updatePolicy(policy.c_str()); + } + + LogDebug("Sending reponse: " << errorCode); +} + +OptionalPolicyResult PolicyEnforcementPoint::checkFromCache(Request &request) +{ + return m_pdp->getPolicyForRequestFromCache(request); + // return OptionalPolicyResult(); +} + +OptionalPolicyResult PolicyEnforcementPoint::check(Request &request, + bool fromCacheOnly) +{ + return m_pdp->getPolicyForRequest(request, fromCacheOnly); + // return OptionalPolicyResult(); +} diff --git a/modules/ace/engine/PolicyEvaluator.cpp b/modules/ace/engine/PolicyEvaluator.cpp new file mode 100644 index 0000000..609d0fc --- /dev/null +++ b/modules/ace/engine/PolicyEvaluator.cpp @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : PolicyEvaluator.cpp +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#include +#include + +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include + +using namespace AceDB; + +PolicyEvaluator::~PolicyEvaluator() +{ + delete m_combiner; +} + +bool PolicyEvaluator::initPDP() +{ + ConfigurationManager * configMgr = ConfigurationManager::getInstance(); + if (configMgr == NULL) { + LogError("ACE fatal error: failed to create configuration manager"); + return false; + } + + Parser parser; + m_root = parser.parse( + configMgr->getFullPathToCurrentPolicyFile(), + configMgr->getFullPathToCurrentPolicyXMLSchema()); + + if (NULL == m_root) { + LogError("ACE fatal error: cannot parse XML file"); + } + + return true; +} + +bool PolicyEvaluator::fillAttributeWithPolicy() +{ + if (m_attributeSet.empty()) { + if (!extractAttributes()) { + LogInfo( + "Warning attribute set cannot be extracted. Returning Deny"); + return false; + } + } else { + LogDebug("Required attribute set found in database"); + } + return true; +} + +PolicyResult PolicyEvaluator::effectToPolicyResult(Effect effect){ + if (Effect::Deny == effect) { + return PolicyEffect::DENY; + } + if (Effect::Undetermined == effect) { + return PolicyResult::Value::UNDETERMINED; + } + if (Effect::PromptOneShot == effect) { + return PolicyEffect::PROMPT_ONESHOT; + } + if (Effect::PromptSession == effect) { + return PolicyEffect::PROMPT_SESSION; + } + if (Effect::PromptBlanket == effect) { + return PolicyEffect::PROMPT_BLANKET; + } + if (Effect::Permit == effect) { + return PolicyEffect::PERMIT; + } + if (Effect::Inapplicable == effect) { + return PolicyDecision::Value::NOT_APPLICABLE; + } + return PolicyEffect::DENY; +} + +OptionalPolicyResult PolicyEvaluator::getPolicyForRequestInternal(bool fromCacheOnly) +{ + //ADD_PROFILING_POINT("Search cached verdict in database", "start"); + + OptionalPolicyResult result = AceDAO::getPolicyResult( + m_attributeSet); + + //ADD_PROFILING_POINT("Search cached verdict in database", "stop"); + + if (fromCacheOnly || !result.IsNull()) { + return result; + } + + //ADD_PROFILING_POINT("EvaluatePolicy", "start"); + + Effect policyEffect = evaluatePolicies(m_root); + + //ADD_PROFILING_POINT("EvaluatePolicy", "stop"); + + LogDebug("Policy effect is: " << toString(policyEffect)); + + result = effectToPolicyResult(policyEffect); + + AceDAO::setPolicyResult(this->m_attributeSet, *result); + return result; +} + +// +----------------+---------+---------+------+--------+ +// |\User setting | PERMIT | PROMPT* | DENY | DEF | +// | \ | | | | | +// |Policy result\ | | | | | +// |----------------+---------+---------+------+--------+ +// |PERMIT | PERMIT | PROMPT* | DENY | PERMIT | +// |----------------+---------+---------+------+--------+ +// |PROMPT* | PROMPT* | PR MIN | DENY | PROMPT*| +// |----------------+---------+---------+------+--------+ +// |DENY | DENY | DENY | DENY | DENY | +// |----------------+---------+---------+------+--------+ +// |UNDETERMIND | UNDET | UNDET | DENY | UNDET | +// |----------------+---------+---------+------+--------+ +// |NOT_AP | PEMIT | PROMPT* | DENY | NOT_AP | +// +----------------+---------+---------+------+--------+ + +static PolicyResult getMostRestrict(PreferenceTypes globalPreference, + const PolicyResult &policyResult) +{ + if (globalPreference == PreferenceTypes::PREFERENCE_PERMIT && + policyResult == PolicyEffect::PERMIT) + { + return PolicyEffect::PERMIT; + } + + if (globalPreference == PreferenceTypes::PREFERENCE_DENY || + policyResult == PolicyEffect::DENY) + { + return PolicyEffect::DENY; + } + + if (policyResult == PolicyResult::UNDETERMINED) { + return PolicyResult::UNDETERMINED; + } + + if (globalPreference == PreferenceTypes::PREFERENCE_DEFAULT) + { + return policyResult; + } + + if (globalPreference == PreferenceTypes::PREFERENCE_ONE_SHOT_PROMPT || + policyResult == PolicyEffect::PROMPT_ONESHOT) + { + return PolicyEffect::PROMPT_ONESHOT; + } + + if (globalPreference == PreferenceTypes::PREFERENCE_SESSION_PROMPT || + policyResult == PolicyEffect::PROMPT_SESSION) + { + return PolicyEffect::PROMPT_SESSION; + } + + if (globalPreference == PreferenceTypes::PREFERENCE_BLANKET_PROMPT || + policyResult == PolicyEffect::PROMPT_BLANKET) + { + return PolicyEffect::PROMPT_BLANKET; + } + + return PolicyEffect::PERMIT; +} + +OptionalPolicyResult PolicyEvaluator::getPolicyForRequestFromCache( + const Request &request) +{ + return getPolicyForRequest(request, true); +} + +PolicyResult PolicyEvaluator::getPolicyForRequest(const Request &request) +{ + auto result = this->getPolicyForRequest(request, false); + Assert(!result.IsNull() && + "Policy always has to be evaluated to valid state"); + return *result; +} + +OptionalPolicyResult PolicyEvaluator::getPolicyForRequest( + const Request &request, + bool fromCacheOnly) +{ + //ADD_PROFILING_POINT("getPolicyForRequest", "start"); + m_attributeSet.clear(); + + try { + // Check which attributes should be used + // memory alocated, free in destructor + //ADD_PROFILING_POINT("getAttributes", "start"); + AceDB::AceDAO::getAttributes(&m_attributeSet); + //ADD_PROFILING_POINT("getAttributes", "stop"); + + // If attributes can't be resolved then check the policy + if (!fillAttributeWithPolicy()) { + //ADD_PROFILING_POINT("getPolicyForRequest", "stop"); + return OptionalPolicyResult(PolicyEffect::DENY); + } + + //ADD_PROFILING_POINT("getAttributesValues", "start"); + m_pip->getAttributesValues(&request, &m_attributeSet); + //ADD_PROFILING_POINT("getAttributesValues", "stop"); + LogDebug("==== Attributes set by PIP ===="); + printAttributes(m_attributeSet); + LogDebug("==== End of attributes set by PIP ===="); + + OptionalPolicyResult policyResult = + getPolicyForRequestInternal(fromCacheOnly); + + LogDebug("==== getPolicyForRequestInternal result (PolicyResult): " + << policyResult << "====="); + + if (policyResult.IsNull()) { + if (fromCacheOnly) { + return OptionalPolicyResult::Null; + } else { + LogError("Policy evaluated to NULL value"); + Assert(false && "Policy evaluated to NULL value"); + return OptionalPolicyResult::Null; + } + } + + PreferenceTypes globalPreference = + SettingsLogic::findGlobalUserSettings(request); + + auto ret = getMostRestrict(globalPreference, *policyResult); + //ADD_PROFILING_POINT("getPolicyForRequest", "stop"); + return OptionalPolicyResult(ret); + + } catch (AceDB::AceDAO::Exception::DatabaseError &e) { + LogError("Database error"); + DPL::Exception::DisplayKnownException(e); + //ADD_PROFILING_POINT("getPolicyForRequest", "stop"); + return OptionalPolicyResult(PolicyEffect::DENY); + } +} + + +bool PolicyEvaluator::extractAttributes() +{ + if (m_root == NULL) { + return false; + } + + //We check if root target matches. In general the root's target should be empty + //Otherwise it would have to have all the subjects available specified + //But just to be on the safe side ( and for tests) add this checking + const Policy * policy = dynamic_cast(m_root->getElement()); + Assert( + policy != NULL && + "Policy element has been null while attribute extracting"); + + extractTargetAttributes(policy); + extractAttributesFromSubtree(m_root); //Enter recursion + + return true; +} + +void PolicyEvaluator::extractTargetAttributes(const Policy *policy) +{ + std::list::const_iterator it = + policy->getSubjects()->begin(); + for (; it != policy->getSubjects()->end(); ++it) + { + const std::list & attrList = (*it)->getTargetAttributes(); + FOREACH(it2, attrList) + { + BaseAttributePtr attr(new Attribute((*it2).getName(), + (*it2).getMatchFunction(), (*it2).getType())); + m_attributeSet.insert(attr); + } + } +} + +/** + * + * @param *root - the root of the original (full) subtree of politics + * @param *newRoot - the pointer to the root of the copy (reduced) subtree of politics + */ +void PolicyEvaluator::extractAttributesFromSubtree(const TreeNode *root) +{ + const ChildrenSet & children = root->getChildrenSet(); + + for ( + std::list::const_iterator it = children.begin(); + it != children.end(); + ++it + ) { + TreeNode * node = *it; + if (node->getTypeID() != TreeNode::Policy && node->getTypeID() != + TreeNode::PolicySet) { + //It is not a policy so we may be sure that we have already checked that SubjectId matches + //Add new node to new tree and extract attributes + + extractAttributesFromRules(node); + } else { //TreeNode is a Policy or PolicySet + const Policy * policy = + dynamic_cast(node->getElement()); + //We will be needing also the attributes from target + if (policy) { + extractTargetAttributes(policy); + } else { + LogError(" extractAttributesFromSubtree policy=NULL"); + } + //Enter recursion + extractAttributesFromSubtree(node); + } + } +} + +bool PolicyEvaluator::extractAttributesFromRules(const TreeNode *root) +{ + Assert( + root->getTypeID() == TreeNode::Rule + && + "Tree structure, extracting attributes from node that is not a rule"); + Rule * rule = dynamic_cast(root->getElement()); + Assert(rule != NULL); + //Get attributes from rule + rule->getAttributes(&m_attributeSet); + + //[CR] consider returned value, because its added only to eliminate errors + return true; +} + +Effect PolicyEvaluator::evaluatePolicies(const TreeNode * root) +{ + if (root == NULL) { + LogInfo("Error: policy tree doesn't exist. " + "Probably xml file is missing"); + return Deny; + } + + if (m_attributeSet.empty()) { + LogInfo("Warning: evaluatePolicies: attribute set was empty"); + } + m_combiner->setAttributeSet(&m_attributeSet); + return m_combiner->combinePolicies(root); +} + +int PolicyEvaluator::updatePolicy(const char* newPolicy) +{ + ConfigurationManager* configMgr = ConfigurationManager::getInstance(); + + if (NULL == configMgr) { + LogError("ACE fatal error: failed to create configuration manager"); + return POLICY_PARSING_ERROR; + } + + int result = POLICY_PARSING_SUCCESS; + if (newPolicy == NULL) { + LogError("Policy Update: incorrect policy name"); + return POLICY_FILE_ERROR; + } + LogDebug("Starting update policy: " << newPolicy); + + Parser parser; + TreeNode *backup = m_root; + + m_root = parser.parse( + newPolicy, + configMgr->getFullPathToCurrentPolicyXMLSchema()); + + if (NULL == m_root) { + m_root = backup; + LogError("Policy Update: corrupted policy file"); + result = POLICY_PARSING_ERROR; + } else { + m_currentPolicyFile = newPolicy; + backup->releaseResources(); + LogInfo("Policy Update: successful."); + try { + AceDAO::resetDatabase(); + } catch (AceDAO::Exception::DatabaseError &e) { + } + } + return result; +} + +std::string PolicyEvaluator::getCurrentPolicy(){ + return m_currentPolicyFile; +} + +const char * toString(Validity validity) +{ + switch (validity) { + case Validity::ONCE: + return "Once"; + break; + case Validity::SESSION: + return "Session"; + case Validity::ALWAYS: + return "Always"; + default: + return "WRONG VALIDITY"; + } +} + +const char * toString(Verdict verdict) +{ + switch (verdict) { + case Verdict::VERDICT_PERMIT: + return "Permit"; + case Verdict::VERDICT_DENY: + return "Deny"; + case Verdict::VERDICT_INAPPLICABLE: + return "Inapplicable"; + case Verdict::VERDICT_UNKNOWN: + return "Unknown"; + case Verdict::VERDICT_UNDETERMINED: + return "Undetermined"; + case Verdict::VERDICT_ERROR: + return "Error"; + case Verdict::VERDICT_ASYNC: + return "Async"; + default: + return "Wrong verdict value"; + } +} diff --git a/modules/ace/engine/PolicyInformationPoint.cpp b/modules/ace/engine/PolicyInformationPoint.cpp new file mode 100644 index 0000000..8727d30 --- /dev/null +++ b/modules/ace/engine/PolicyInformationPoint.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// +// @ Project : Access Control Engine +// @ File Name : PolicyInformationPoint.cpp +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// +#include + +#include +#include + +#include +#include + +#include +#include + +using namespace AceDB; + +PolicyInformationPoint::~PolicyInformationPoint() +{ +} + +/* gather attributes values from adequate interfaces */ +PipResponse PolicyInformationPoint::getAttributesValues(const Request* request, + AttributeSet* attributes) +{ + int subjectReturn = 0; + int resourceReturn = 0; + int operationReturn = 0; + int functionReturn = 0; + /* create query lists */ + createQueries(attributes); + + /* check if subject attributes query has any elements*/ + if (!subjectAttributesQuery.empty()) { + /* get Subject Attributes */ + subjectReturn = wrtInterface->getAttributesValues( + *request, + &subjectAttributesQuery); + } + + AttributeSet::const_iterator iter2; + FOREACH(iter, subjectAttributesQuery) + { + if (iter->second == NULL) { + Attribute attr(*(iter->first)); + attr.setType(Attribute::Type::Subject); + iter2 = std::find_if(attributes->begin(), + attributes->end(), + BaseAttribute::UnaryPredicate(&attr)); + Assert(iter2 != attributes->end() && "This should not happen, " + "the attribute MUST be in attribute set"); + (*iter2)->setUndetermind(true); + } + } + + /* check if resource attributes query has any elements*/ + if (!resourceAttributesQuery.empty()) { + /* get Resource Attributes */ + resourceReturn = resourceInformation->getAttributesValues( + *request, + &resourceAttributesQuery); + /* error analyzys*/ + resourceReturn <<= ERROR_SHIFT_RESOURCE; + } + + FOREACH(iter, resourceAttributesQuery) + { + if (iter->second == NULL) { + LogInfo("Found undetermined attribute"); + Attribute attr(*(iter->first)); + attr.setType(Attribute::Type::Resource); + iter2 = std::find_if(attributes->begin(), + attributes->end(), + BaseAttribute::UnaryPredicate(&attr)); + Assert(iter2 != attributes->end() && "This should not happen, " + "the attribute MUST be in attribute set"); + (*iter2)->setUndetermind(true); + } + } + + /* check if resource attributes query has any elements*/ + if (!environmentAttributesQuery.empty()) { + /* get enviroment attributes */ + operationReturn = operationSystem->getAttributesValues( + *request, + &environmentAttributesQuery); + /* error analyzys*/ + operationReturn <<= ERROR_SHIFT_OS; + } + + FOREACH(iter, environmentAttributesQuery) + { + if (iter->second == NULL) { + //it doesnt change uniqueness of a set element so we can const_cast + Attribute attr(*(iter->first)); + attr.setType(Attribute::Type::Environment); + iter2 = find_if(attributes->begin(), + attributes->end(), + BaseAttribute::UnaryPredicate(&attr)); + Assert(iter2 != attributes->end() && "This should not happen, " + "the attribute MUST be in attribute set"); + (*iter2)->setUndetermind(true); + } + } + + /* check if functionParam attributes query has any elements*/ + if (!functionParamAttributesQuery.empty() && request->getFunctionParam()) { + /* get params attributes */ + functionReturn = request->getFunctionParam()->getAttributesValues( + *request, + &functionParamAttributesQuery); + /* error analyzys*/ + functionReturn <<= ERROR_SHIFT_FP; + } + + FOREACH(iter, functionParamAttributesQuery) + { + if (iter->second == NULL) { + //it doesnt change uniqueness of a set element so we can const_cast + Attribute attr(*(iter->first)); + attr.setType(Attribute::Type::FunctionParam); + iter2 = find_if(attributes->begin(), + attributes->end(), + BaseAttribute::UnaryPredicate(&attr)); + Assert(iter2 != attributes->end() && "This should not happen, " + "the attribute MUST be in attribute set"); + (*iter2)->setUndetermind(true); + } + } + + /** clear query lists*/ + resourceAttributesQuery.clear(); + environmentAttributesQuery.clear(); + subjectAttributesQuery.clear(); + functionParamAttributesQuery.clear(); + + return subjectReturn | resourceReturn | operationReturn | functionReturn; +} + +/** create query lists */ +void PolicyInformationPoint::createQueries(AttributeSet* attributes) +{ + AttributeSet::const_iterator it; + + enum Attribute::Type type; + + /**iterate all attributes and split them into adequate query */ + FOREACH (it, *attributes) { + type = (*it)->getType(); + + switch (type) { + case Attribute::Type::Subject: + subjectAttributesQuery.push_back(ATTRIBUTE((*it)->getName(), + (*it)->getValue())); + break; + + case Attribute::Type::Environment: + environmentAttributesQuery.push_back(ATTRIBUTE((*it)->getName(), + (*it)->getValue())); + break; + + case Attribute::Type::Resource: + resourceAttributesQuery.push_back(ATTRIBUTE((*it)->getName(), + (*it)->getValue())); + break; + + case Attribute::Type::FunctionParam: + functionParamAttributesQuery.push_back(ATTRIBUTE((*it)->getName(), + (*it)->getValue())); + break; + + default: + break; + } + } +} + diff --git a/modules/ace/engine/Rule.cpp b/modules/ace/engine/Rule.cpp new file mode 100644 index 0000000..3fe4051 --- /dev/null +++ b/modules/ace/engine/Rule.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : Rule.h +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#include +#include + +#include +#include + +void Rule::printData() +{ + std::cout << "Rule: effect: " << printEffect(this->effect) << + " condition: " << this->condition; +} + +std::string Rule::printEffect(const Effect effect) const +{ + switch (effect) { + case Deny: + return "Deny"; + case PromptBlanket: + return "PromptBlanket"; + case PromptOneShot: + return "PromptOneShot"; + case PromptSession: + return "PromptSession"; + case Permit: + return "Permit"; + case Inapplicable: + return "Inapplicable"; + case Error: + return "Error"; + default: + return "ERROR"; + } +} + +Effect Rule::evaluateRule(const AttributeSet * attrSet) const +{ + Attribute::MatchResult result = condition.evaluateCondition(attrSet); + + if (result == Attribute::MatchResult::MRUndetermined) { + // LogInfo("Rule is undetermined"); + return Undetermined; + } else if (result == Attribute::MatchResult::MRTrue) { + // LogInfo("Rule effect "<deserializeCondition(is); + effect = serializer->deserializeEffect(is); +} + +void Rule::serialize(std::ostream& os) +{ + Serializer* serializer = Serializer::getInstance(); + + serializer->serializeCondition(os, condition); + serializer->serializeEffect(os, effect); +} + diff --git a/modules/ace/engine/Serializer.cpp b/modules/ace/engine/Serializer.cpp new file mode 100644 index 0000000..a3141be --- /dev/null +++ b/modules/ace/engine/Serializer.cpp @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +Serializer* Serializer::pInstance = NULL; + +Serializer* Serializer::getInstance() +{ + if (pInstance == NULL) { + pInstance = new Serializer; + } + return pInstance; +} + +void Serializer::serializeInt(std::ostream& os, + int value) +{ + os.write((char*)(&value), sizeof(value)); + //LogInfo("SerializeInt: " <& inputList) +{ + int dataLength = inputList.size(); + os.write((char*)(&dataLength), sizeof(dataLength)); + + //LogInfo("SerializeListAttributes SIZE: " <serialize(os); + } + + Assert(os.bad() == false && "Error Writing to file failure"); +} + +std::list Serializer::deserializeListAttributes(std::istream& is) +{ + std::list outputList; + + int numElem = 0; + is.read((char*)(&numElem), sizeof(numElem)); + + //LogInfo("DESerializeListAttributes SIZE: " <& inputList) const +{ + int dataLength = inputList.size(); + os.write((char*)(&dataLength), sizeof(dataLength)); + + FOREACH(it, inputList) + { + serializeString(os, *it); + } + + Assert(os.bad() == false && "Error Writing to file failure"); +} + +std::list Serializer::deserializeListStrings(std::istream& is) +{ + std::list outputList; + + int numElem = 0; + is.read((char*)(&numElem), sizeof(numElem)); + + for (int i = 0; i < numElem; i++) { + outputList.push_back(this->deserializeString(is)); + } + + return outputList; +} + +void Serializer::serializeMatch(std::ostream& os, + const Attribute::Match& match) const +{ + os.write((char*)(&match), sizeof(match)); + // LogInfo("SerializeMatch: " << match); + Assert(os.bad() == false && "Error Writing to file failure"); +} + +Attribute::Match Serializer::deserializeMatch(std::istream& is) +{ + Attribute::Match match(Attribute::Match::Error); + is.read((char*)(&match), sizeof(match)); + // LogInfo("DESerializeMatch: " << match); + return match; +} + +bool Serializer::serializeModifier(std::ostream& os, + const Attribute::Modifier& modifier) const +{ + os.write((char*)(&modifier), sizeof(modifier)); + // LogInfo("SerializeModifier: " << modifier); + return 0; +} + +Attribute::Modifier Serializer::deserializeModifier(std::istream& is) +{ + Attribute::Modifier modifier(Attribute::Modifier::Non); + is.read((char*)(&modifier), sizeof(modifier)); + // LogInfo("DESerializeModifer: " << modifier); + return modifier; +} + +//TODO zrobic z tego szablon - z list jezeli sie da +void Serializer::serializeListTreeNode(std::ostream& os, + std::list& inputList) +{ + int dataLength = inputList.size(); + os.write((char*)(&dataLength), sizeof(dataLength)); + + // LogInfo("SerializeListTreeNode SIZE: " <serialize(os); + } + + Assert(os.bad() == false && "Error Writing to file failure"); +} + +std::list Serializer::deserializeListTreeNode(std::istream&is, + TreeNode* parent) +{ + std::list outputList; + + int numElem = 0; + is.read((char*)(&numElem), sizeof(numElem)); + + // LogInfo("DESerializeListTreeNode SIZE: " <serialize(os); + + // LogInfo("Serialize AbstractElem: " << element); + Assert(os.bad() == false && "Error Writing to file failure"); +} + +AbstractTreeElement* Serializer::deserializeAbstractElement(std::istream& is, + TreeNode::TypeID& typeId) +{ + NodeFactory* factory = NodeFactory::getInstance(); + + AbstractTreeElement* element; + + element = factory->create(is, typeId); + + //LogInfo("DESerialize AbstractElem: " << typeId); + return element; +} + +void Serializer::serializeListSubjects(std::ostream& os, + std::list& inputList) +{ + int dataLength = inputList.size(); + os.write((char*)(&dataLength), sizeof(dataLength)); + + //LogInfo("Serialize list Subjects SIZE: " << dataLength); + + FOREACH (it, inputList) + { + //serialize method doesn't change anything in object + const_cast((*it))->serialize(os); + } + + Assert(os.bad() == false && "Error Writing to file failure"); +} + +std::list* Serializer::deserializeListSubjects(std::istream& is) +{ + std::list* outputList = new std::list; + + int numElem = 0; + is.read((char*)(&numElem), sizeof(numElem)); + + //LogInfo("DESerialize list Subjects SIZE: " <push_back(subject); + } + + return outputList; +} + +void Serializer::serializeCombineAlgorithm(std::ostream& os, + const Policy::CombineAlgorithm& combAlg) +{ + os.write((char*)(&combAlg), sizeof(combAlg)); + //LogInfo("Serialize Combine Alg:" << combAlg); + + Assert(os.bad() == false && "Error Writing to file failure"); +} + +Policy::CombineAlgorithm Serializer::deserializeCombineAlgorithm( + std::istream& is) +{ + Policy::CombineAlgorithm combAlg(Policy::DenyOverride); + is.read((char*)(&combAlg), sizeof(combAlg)); + //LogInfo("DESerialize Combine Alg:" << combAlg); + return combAlg; +} + +void Serializer::serializeCombineType(std::ostream& os, + const Condition::CombineType& combType) +{ + os.write((char*)(&combType), sizeof(combType)); + //LogInfo("Serialize CombineType: " <& inputList) +{ + int dataLength = inputList.size(); + os.write((char*)(&dataLength), sizeof(dataLength)); + + //LogInfo("Serialize List Conditions SIZE:" <serialize(os); + } + + Assert(os.bad() == false && "Error Writing to file failure"); +} + +std::list Serializer::deserializeListConditions(std::istream& is, + Condition* parent) +{ + std::list outputList; + + int numElem = 0; + is.read((char*)(&numElem), sizeof(numElem)); + + //LogInfo("DESerialize list Condition SIZE: " < + +#include +#include + +#include + +using namespace AceDB; + +Preference SettingsLogic::findGlobalUserSettings( + const std::string &resource, + WidgetHandle handler) +{ + Preference p = AceDAO::getWidgetDevCapSetting(resource, handler); + if (PreferenceTypes::PREFERENCE_DEFAULT == p) { + return AceDAO::getDevCapSetting(resource); + } else { + return p; + } +} + +Preference SettingsLogic::findGlobalUserSettings( + const Request &request) +{ + Request::DeviceCapabilitySet devset = request.getDeviceCapabilitySet(); + Assert(!devset.empty() && "No device cap set in request"); + return findGlobalUserSettings( + *(devset.begin()), + request.getWidgetHandle()); +} + +Preference SettingsLogic::getDevCapSetting(const std::string &resource) +{ + return AceDAO::getDevCapSetting(resource); +} + +void SettingsLogic::getDevCapSettings(PreferenceMap *globalSettingsMap) +{ + AceDAO::getDevCapSettings(globalSettingsMap); // NULL check inside +} + + +void SettingsLogic::setDevCapSetting(const std::string &resource, + Preference preference) +{ + if (resource.empty()) { + LogInfo("WARNING: setting resource settings for empty resource name"); + } + + AceDAO::addResource(resource); + + if (preference == PreferenceTypes::PREFERENCE_DEFAULT) { + return; + } + + Assert((PreferenceTypes::PREFERENCE_PERMIT == preference || + PreferenceTypes::PREFERENCE_DENY == preference || + PreferenceTypes::PREFERENCE_BLANKET_PROMPT == preference || + PreferenceTypes::PREFERENCE_ONE_SHOT_PROMPT == preference || + PreferenceTypes::PREFERENCE_SESSION_PROMPT == preference)); + + AceDAO::setDevCapSetting(resource,preference); +} + +void SettingsLogic::setAllDevCapSettings( + const std::list < std::pair < const std::string*, + Preference > > &resourcesList) +{ + std::list < std::pair < const std::string*, + Preference > >::const_iterator iter; + for (iter = resourcesList.begin(); iter != resourcesList.end(); ++iter) { + SettingsLogic::setDevCapSetting(*(iter->first), iter->second); + } +} + +void SettingsLogic::removeDevCapSetting(const std::string &resource) +{ + AceDAO::removeDevCapSetting(resource); +} + +void SettingsLogic::updateDevCapSetting(const std::string &resource, + Preference p) +{ + if (PreferenceTypes::PREFERENCE_DEFAULT == p) { + SettingsLogic::removeDevCapSetting(resource); + } else { + SettingsLogic::setDevCapSetting(resource, p); + } +} + +Preference SettingsLogic::getWidgetDevCapSetting( + const std::string &resource, + WidgetHandle handler) +{ + return AceDAO::getWidgetDevCapSetting(resource, handler); +} + +void SettingsLogic::getWidgetDevCapSettings(PermissionList *outputList) +{ + AceDAO::getWidgetDevCapSettings(outputList); // NULL check inside +} + + +void SettingsLogic::setWidgetDevCapSetting( + const std::string &resource, + WidgetHandle handler, + Preference preference) +{ + if (resource.empty()) { + LogError("Empty resource"); + return; + } + + LogDebug("userSetting, resource: " << resource << + " app_id: " << handler); + + AceDAO::addResource(resource); + SettingsLogic::removeWidgetDevCapSetting(resource, handler); + + if (PreferenceTypes::PREFERENCE_DEFAULT == preference) { + return; + } + + Assert((PreferenceTypes::PREFERENCE_PERMIT == preference || + PreferenceTypes::PREFERENCE_DENY == preference || + PreferenceTypes::PREFERENCE_BLANKET_PROMPT == preference || + PreferenceTypes::PREFERENCE_ONE_SHOT_PROMPT == preference || + PreferenceTypes::PREFERENCE_SESSION_PROMPT == preference)); + + AceDAO::setWidgetDevCapSetting(resource, handler, preference); +} + + +void SettingsLogic::setWidgetDevCapSettings(const PermissionList &permissionsList) +{ + FOREACH(i, permissionsList) { + SettingsLogic::setWidgetDevCapSetting(i->devCap, + i->appId, + i->access); + } +} + + +void SettingsLogic::removeWidgetDevCapSetting(const std::string &resource, + WidgetHandle handler) +{ + AceDAO::removeWidgetDevCapSetting(resource, handler); +} diff --git a/modules/ace/engine/Subject.cpp b/modules/ace/engine/Subject.cpp new file mode 100644 index 0000000..760b93d --- /dev/null +++ b/modules/ace/engine/Subject.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include +#include +#include + +bool Subject::matchSubject(const AttributeSet *attrSet, + bool &isUndetermined) const +{ + bool result = true; + Attribute::MatchResult match = Attribute::MatchResult::MRUndetermined; + + FOREACH(it, targetAttributes) + { + AttributeSet::const_iterator attr = + std::find_if(attrSet->begin(), + attrSet->end(), + AceDB::BaseAttribute::UnaryPredicate(&(*it))); + if (attr == attrSet->end()) { + LogError("Cannot find attribute value for " << *(it->getName())); + Assert(false && + "Attribute for subject hasn't been found." + "It shoud not happen. This attribute should be undetermined," + "not missing"); + result = false; //According to BONDI 1.0 for signle subject all attributes must match + isUndetermined = true; + break; + } + + match = it->matchAttributes(&(*(*attr))); + + if (match == Attribute::MatchResult::MRUndetermined) { + result = false; + isUndetermined = true; + /// LogError("Subject doesn match and UNDETERMINED"); + break; //According to BONDI 1.0 for signle subject all attributes must match + } else if (match == Attribute::MatchResult::MRFalse) { + result = false; + // LogError("Subject doesn match and DETERMINED"); + break; //According to BONDI 1.0 for signle subject all attributes must match + } + } + + return result; +} + +const std::list& Subject::getTargetAttributes() const +{ + return targetAttributes; +} + +Subject::Subject(std::istream& is) +{ + Serializer* serializer = Serializer::getInstance(); + + subjectId = serializer->deserializeString(is); + targetAttributes = serializer->deserializeListAttributes(is); +} + +bool Subject::serialize (std::ostream& os) +{ + Serializer* serializer = Serializer::getInstance(); + + serializer->serializeString(os, subjectId); + serializer->serializeListAttributes(os, targetAttributes); + + return 0; +} diff --git a/modules/ace/engine/TreeNode.cpp b/modules/ace/engine/TreeNode.cpp new file mode 100644 index 0000000..5bcc136 --- /dev/null +++ b/modules/ace/engine/TreeNode.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +//Tree node destructor is a tricky part, only the original tree should remove the elements +//release resources should be called when we want to destroy the whole tree +TreeNode::~TreeNode() +{ +} + +bool TreeNode::serialize(std::ostream& os) +{ + Serializer* serializer = Serializer::getInstance(); + + //does node have any child - anty Inf recursion + if (!children.empty()) { + serializer->serializeInt(os, 1); + serializer->serializeListTreeNode(os, children); + } else { + serializer->serializeInt(os, 0); + } + + serializer->serializeTypeAbstract(os, typeID); + serializer->serializeAbstractElement(os, element); + + //LOG << "TreeNode Serialized" << std::endl; + return 0; +} + +TreeNode::TreeNode(std::istream& is, + TreeNode* parent) +{ + Serializer* serializer = Serializer::getInstance(); + + this->parent = parent; + + //check if this node has any child + int hasChild = 0; + hasChild = serializer->deserializeInt(is); + + if (hasChild) { //[CR] - 0 remove copying list .... + children = serializer->deserializeListTreeNode(is, this); + } + + typeID = serializer->deserializeTypeAbstract(is); + element = serializer->deserializeAbstractElement(is, typeID); + //LOG << "TreeNode DESERIALIZED" << std::endl; +} + +//TODO release resources is releaseTheSubtree and delete the element +void TreeNode::releaseResources() +{ + Assert(this != 0); + delete element; + std::list::iterator it = this->children.begin(); + while (it != children.end()) { + (*it)->releaseResources(); + ++it; + } + delete this; +} + +// KW void TreeNode::releaseTheSubtree(){ +// KW +// KW std::list::iterator it = children.begin(); +// KW +// KW for(; it != children.end();it++){ +// KW (*it)->releaseTheSubtree(); +// KW } +// KW delete this; +// KW } + +int TreeNode::level = 0; + +// KW void TreeNode::printSubtree(){ +// KW +// KW TreeNode::level++; +// KW +// KW for(int i=0;ichildren.begin(); +// KW +// KW for(;it != children.end();++it){ +// KW (*it)->printSubtree(); +// KW } +// KW +// KW TreeNode::level--; +// KW } + +std::ostream & operator<<(std::ostream & out, + const TreeNode * node) +{ + std::string tmp; + + switch (node->getTypeID()) { + case TreeNode::Policy: + tmp = "Policy"; + break; + case TreeNode::PolicySet: + tmp = "PolicySet"; + break; + case TreeNode::Rule: + tmp = "Rule"; + break; + default: + break; + } + + out << "" << tmp << "-> children count: " << node->children.size() << + ": " << std::endl; + AbstractTreeElement * el = node->getElement(); + if (el != NULL) { + el->printData(); + } else { + std::cout << "Empty element!" << std::endl; + } + + return out; +} + diff --git a/modules/ace/engine/parser.cpp b/modules/ace/engine/parser.cpp new file mode 100644 index 0000000..1b20a6a --- /dev/null +++ b/modules/ace/engine/parser.cpp @@ -0,0 +1,744 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include + +#include +#include + +namespace { + +class ParserWarningLogger +{ + public: + void operator()(const std::string& logMsg) + { + LogWarning(logMsg); + } +}; + +class ParserErrorLogger +{ + public: + void operator()(const std::string& logMsg) + { + LogError(logMsg); + } +}; + +template +void xmlLogFunction(void* /*ctx*/, const char *msg, ...) +{ + const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + buffer[BUFFER_SIZE - 1] = '\0'; + Logger l; + + va_list va; + va_start(va, msg); + vsnprintf(buffer, BUFFER_SIZE - 1, msg, va); + va_end(va); + + std::string logmsg(buffer); + l(logmsg); +} + +} + +const char *Parser::TOKEN_PARAM = "param:"; + +Parser::Parser() : + reader(NULL), + root(NULL), + currentRoot(NULL), + currentSubject(NULL), + currentCondition(NULL), + currentAttribute(NULL), + currentText(NULL), + processingSignature(false), + canonicalizeOnce(false) +{ + processingSignature = true; + canonicalizeOnce = true; +} + +Parser::~Parser() +{ + /* parse function destroys reader */ + // free(this->xmlFilename); +} + +TreeNode* Parser::parse(const std::string& filename, const std::string& schema) +{ + if(root != NULL) { + root->releaseResources(); + root = NULL; + } + + LogDebug("Parser: opening file " << filename); + + xmlDocPtr xmlDocument = xmlParseFile(filename.c_str()); + if (!xmlDocument) { + LogError("Couldn't parse file " << filename); + return root; + } + + std::unique_ptr > + doc(xmlDocument, xmlFreeDoc); + + xmlSchemaParserCtxtPtr xmlSchemaParserContext = + xmlSchemaNewParserCtxt(schema.c_str()); + + if (!xmlSchemaParserContext) { + LogError("Couldn't load xml schema: " << schema); + return root; + } + + std::unique_ptr < + xmlSchemaParserCtxt, + std::function > + schemaContext( + xmlSchemaParserContext, + xmlSchemaFreeParserCtxt); + + LogDebug("Setting callbacks"); + + xmlSchemaSetParserErrors( + schemaContext.get(), + static_cast + (&xmlLogFunction), + static_cast + (&xmlLogFunction), + NULL); + + xmlSchemaPtr xmlSchema = xmlSchemaParse(schemaContext.get()); + + if (!xmlSchema) { + LogError("Couldn't parse xml schema: " << xmlSchema); + return root; + } + + xmlSchemaValidCtxtPtr xmlValidContext = xmlSchemaNewValidCtxt(xmlSchema); + + if (!xmlValidContext) { + LogError("Couldn't create validation context!"); + return root; + } + + std::unique_ptr < + xmlSchemaValidCtxt, + std::function > + schemaValidContext( + xmlValidContext, + xmlSchemaFreeValidCtxt); + + xmlSchemaSetValidErrors( + schemaValidContext.get(), + static_cast + (&xmlLogFunction), + static_cast + (&xmlLogFunction), + NULL); + + xmlSchemaSetValidOptions( + schemaValidContext.get(), + XML_SCHEMA_VAL_VC_I_CREATE); + + bool result = + (xmlSchemaValidateDoc( + schemaValidContext.get(), + xmlDocument) == 0 ? true : false); + + if (!result) { + LogError("Couldn't validate policy file: " << filename << + " against xml schema: " << schema); + + return root; + } + + LogInfo("Policy file: " << filename << " validated!"); + + xmlTextReaderPtr xmlReader = xmlReaderWalker(xmlDocument); + + //[CR] consider using ASSERT/DASSERT + if (NULL == xmlReader) { + LogError("Error, xml reader cannot be created. Probably xml file is missing (opening file " << filename << ")"); + return root; + } + + std::unique_ptr > + reader(xmlReader, xmlFreeTextReader); + + int ret; + ret = xmlTextReaderRead(reader.get()); + while (ret == 1) { + std::unique_ptr > + name(xmlTextReaderName(reader.get()), xmlFree); + + if (!strcmp("policy-set", (const char *)name.get())) { + processingSignature = false; + } else if (!strcmp("SignedInfo", + (const char *)name.get()) && canonicalizeOnce) { + #if 0 //TODO I think we don't need canonicalization in ACE only in PM, + //we have to verify it tough + extractNodeToFile(reader, "output.xml"); + //TODO we should be able to handle more than one canonicalization algorithm + canonicalize("output.xml", "canon.xml", Canonicalization::C14N); + canonicalizeOnce = false; + #endif + } + //Do not process signature of xml file + if(!processingSignature) { + processNode(reader.get()); + } + ret = xmlTextReaderRead(reader.get()); + } + + if (ret != 0) { + LogError("Error while parsing XML file"); + if (root) { + root->releaseResources(); + root = NULL; + } + } + + return root; +} + +void Parser::processNode(xmlTextReaderPtr reader) +{ + //TODO this is interesting, xmlTextReaderNodeType returns int but I am pretty sure + //those integers coresponds to xmlReaderTypes + xmlReaderTypes type = + static_cast(xmlTextReaderNodeType(reader)); + + switch (type) { + //Start element + case XML_READER_TYPE_ELEMENT: + startNodeHandler(reader); + break; + //End element + case XML_READER_TYPE_END_ELEMENT: + endNodeHandler(reader); + break; + //Text element + case XML_READER_TYPE_TEXT: + textNodeHandler(reader); + break; + default: + //Do not handle other xml tags + break; + } +} + +void Parser::startNodeHandler(xmlTextReaderPtr reader) +{ + xmlChar *name = xmlTextReaderName(reader); + + switch (*name) { + case 'p': //policy and policy-set + if (*(name + 6) == 0) { + handlePolicy(reader, TreeNode::Policy); + } else { + handlePolicy(reader, TreeNode::PolicySet); + } + break; + case 'r': //rule and resource-match + if (*(name + 1) == 'u') { + handleRule(reader); + } else if (*(name + 9) == 'm') { + handleMatch(reader, Attribute::Type::Resource); + } else { + handleAttr(reader); + } + break; + case 's': //subject and subject-match + if (*(name + 7) == 0) { + handleSubject(); + } else if (*(name + 8) == 'm') { //subject match + handleSubjectMatch(reader); + } else { //subject attr + handleAttr(reader); + } + break; + case 'c': //condition + handleCondition(reader); + break; + case 'e': //environment-match + if (*(name + 12) == 'm') { + handleMatch(reader, Attribute::Type::Environment); + } else { //env-attr + handleAttr(reader); + } + break; + } + xmlFree(name); +} + +void Parser::endNodeHandler(xmlTextReaderPtr reader) +{ + xmlChar *name = xmlTextReaderName(reader); + + switch (*name) { + case 'p': //policy and policy-set + //Restore old root + currentRoot = currentRoot->getParent(); + break; + case 'r': //Rule and resource match + if (*(name + 1) == 'u') { //Rule + currentRoot = currentRoot->getParent(); + } else { //Resource-match + consumeCurrentText(); //consume text if any available + consumeCurrentAttribute(); //consume attribute + } + break; + case 's': //subject and subject-match + if (*(name + 7) == 0) { //handle subject + consumeCurrentSubject(); + } else if (*(name + 8) == 'm') { //handle subject match + consumeCurrentText(); + consumeSubjectMatch(); + } + //Subject-match end doesn't require handling + break; + case 'c': //condition + consumeCurrentCondition(); + break; + case 'e': //environment-match + consumeCurrentText(); //consume text if any available + consumeCurrentAttribute(); //consume attribute + break; + } + xmlFree(name); +} + +void Parser::textNodeHandler(xmlTextReaderPtr reader) +{ + delete currentText; + xmlChar * text = xmlTextReaderValue(reader); + Assert(text != NULL && "Parser couldn't parse PCDATA"); + + currentText = new std::string(reinterpret_cast(text)); + trim(currentText); + xmlFree(text); +} + +void Parser::handlePolicy(xmlTextReaderPtr reader, + TreeNode::TypeID type) +{ + Policy::CombineAlgorithm algorithm; + + //Get first attribute + xmlChar * combAlg = xmlTextReaderGetAttribute(reader, BAD_CAST("combine")); + + Assert(combAlg != NULL && "Parser error while getting attributes"); + algorithm = convertToCombineAlgorithm(combAlg); + + //Create TreeNode element + Policy * policy = NULL; + if (type == TreeNode::Policy) { + policy = new Policy(); + } else { + policy = new PolicySet(); + } + policy->setCombineAlgorithm(algorithm); + TreeNode * node = new TreeNode(currentRoot, type, policy); + //Add new tree node to current's root children set + if (currentRoot != NULL) { + currentRoot->addChild(node); + } + + //Switch the current root to the new node + if (!xmlTextReaderIsEmptyElement(reader)) { + //Current root switching is necessary only if tag is not empty + currentRoot = node; + } + if (root == NULL) { + root = currentRoot; + } + + if (NULL == currentRoot) { + node->releaseResources(); + } + + xmlFree(combAlg); +} + +void Parser::handleRule(xmlTextReaderPtr reader) +{ + Effect effect = Inapplicable; + + //[CR] create macros for attribute names + xmlChar * eff = xmlTextReaderGetAttribute(reader, BAD_CAST("effect")); //get the rule attribute + + Assert(eff != NULL && "Parser error while getting attributes"); + effect = convertToEffect(eff); + + Rule * rule = NULL; + rule = new Rule(); + rule->setEffect(effect); + + TreeNode * node = new TreeNode(currentRoot, TreeNode::Rule, rule); + //Add new tree node to current's root children set + if (currentRoot != NULL) { // + currentRoot->addChild(node); + } + + if (!xmlTextReaderIsEmptyElement(reader)) { + currentRoot = node; + } + + if (NULL == currentRoot) { + node->releaseResources(); + } + + xmlFree(eff); +} + +void Parser::handleSubject() +{ + currentSubject = new Subject(); + //TODO what about empty subject tag +} + +void Parser::handleCondition(xmlTextReaderPtr reader) +{ + Condition::CombineType combineType = Condition::AND; + + xmlChar * combine = xmlTextReaderGetAttribute(reader, BAD_CAST("combine")); //get the rule attribute + + Assert(combine != NULL && "Parser error while getting attributes"); + + combineType = *combine == 'a' ? Condition::AND : Condition::OR; + + Condition * condition = new Condition(); + condition->setCombineType(combineType); + condition->setParent(currentCondition); + + currentCondition = condition; + //TODO what about empty condition tag? +} + +//Subject match is handled differently than resource or environment match +//Because it cannot have any children tags and can only include PCDATA +void Parser::handleSubjectMatch(xmlTextReaderPtr reader) +{ + //processing Subject + int attributes = xmlTextReaderAttributeCount(reader); + + xmlChar * func = NULL; + xmlChar * value = NULL; + xmlChar * attrName = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute + + if (attributes == 2) { + //match attribute ommited, text value will be used + func = xmlTextReaderGetAttribute(reader, BAD_CAST("func")); + } else if (attributes == 3) { + value = xmlTextReaderGetAttribute(reader, BAD_CAST("match")); + func = xmlTextReaderGetAttribute(reader, BAD_CAST("func")); + } else { + Assert(false && "Wrong XML file format"); + } + + // creating temporiary object is not good idea + // but we have no choice untill Attribute have constructor taking std::string* + std::string temp(reinterpret_cast(attrName)); + Attribute * attr = new Attribute(&temp, convertToMatchFunction( + func), Attribute::Type::Subject); + if (value != NULL) { //add value of the attribute if possible + //[CR] consider create Attribute::addValue(char *) function + std::string temp(reinterpret_cast(value)); + attr->addValue(&temp); + } + currentAttribute = attr; + + if (xmlTextReaderIsEmptyElement(reader)) { + Assert(value != NULL && "XML file format is wrong"); + //Attribute value is required to obtain the match value easier + consumeSubjectMatch(value); + } + + if (attributes == 2 || attributes == 3) { + xmlFree(func); + } + xmlFree(value); + xmlFree(attrName); +} + +void Parser::handleMatch(xmlTextReaderPtr reader, + Attribute::Type type) +{ + int attributes = xmlTextReaderAttributeCount(reader); + + xmlChar * func = NULL; + xmlChar * value = NULL; + xmlChar * attrName = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute + + if (attributes == 2) { + //match attribute ommited, text value will be used + func = xmlTextReaderGetAttribute(reader, BAD_CAST("func")); + //the content may be resource-attr or PCDATA + } else if (attributes == 3) { + value = xmlTextReaderGetAttribute(reader, BAD_CAST("match")); + func = xmlTextReaderGetAttribute(reader, BAD_CAST("func")); + } else { + Assert(false && "Wrong XML file format"); + } + + // FunctionParam type is sybtype of Resource. + // FunctionParam is used to storage attriburess of call functions. + if (0 == + xmlStrncmp(attrName, BAD_CAST(TOKEN_PARAM), + xmlStrlen(BAD_CAST(TOKEN_PARAM))) && type == + Attribute::Type::Resource) { + type = Attribute::Type::FunctionParam; + } + + std::string temp(reinterpret_cast(attrName)); + Attribute * attr = new Attribute(&temp, convertToMatchFunction(func), type); + currentAttribute = attr; + + if (xmlTextReaderIsEmptyElement(reader)) { + Assert(value != NULL && "XML is currupted"); + std::string tempVal(reinterpret_cast(value)); + currentAttribute->addValue(&tempVal); + consumeCurrentAttribute(); + } + + if (attributes == 2 || attributes == 3) { + xmlFree(func); + } + xmlFree(value); + xmlFree(attrName); +} + +Policy::CombineAlgorithm Parser::convertToCombineAlgorithm(xmlChar* algorithm) +{ + switch (*algorithm) { + case 'f': + if (*(algorithm + 6) == 'a') { //first applicable + return Policy::FirstApplicable; + } + return Policy::FirstTargetMatching; + case 'd': + return Policy::DenyOverride; + case 'p': + return Policy::PermitOverride; + default: + Assert(false && "Wrong combine algorithm name"); + return Policy::DenyOverride; + } +} + +Effect Parser::convertToEffect(xmlChar *effect) +{ + switch (*effect) { + case 'd': //deny + return Deny; + break; + case 'p': + //permit, prompt-blanket, prompt-session, prompt-oneshot + if (*(effect + 1) == 'e') { + return Permit; + } + switch (*(effect + 7)) { + case 'b': + return PromptBlanket; + case 's': + return PromptSession; + case 'o': + return PromptOneShot; + default: + Assert(false && "Effect is Error"); + return Error; + } + break; + default: + Assert(false && "Effect is Error"); + return Error; + } + return Inapplicable; +} + +Attribute::Match Parser::convertToMatchFunction(xmlChar * func) +{ + if (func == NULL) { + LogError("[ERROR] match function value is NULL"); + return Attribute::Match::Error; + } + + if (*func == 'g') { + return Attribute::Match::Glob; + } else if (*func == 'e') { + return Attribute::Match::Equal; + } else if (*func == 'r') { + return Attribute::Match::Regexp; + } else { + LogError("[ERROR] match function value is NULL"); + return Attribute::Match::Error; + } + Assert(false); +} + +void Parser::handleAttr(xmlTextReaderPtr reader) +{ + xmlChar * attrValue = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute + Assert(attrValue != NULL && "Error while obtaining attribute"); + + std::string temp(reinterpret_cast(attrValue)); + currentAttribute->addValue(&temp); + + xmlFree(attrValue); +} + +void Parser::consumeCurrentText() +{ + Assert(currentText != NULL); + currentAttribute->addValue(currentText); + delete currentText; + + currentText = NULL; +} + +void Parser::consumeCurrentAttribute() +{ + Assert(currentAttribute != NULL); + + currentCondition->addAttribute(*currentAttribute); + delete currentAttribute; + + currentAttribute = NULL; +} + +void Parser::consumeCurrentSubject() +{ + Policy * policy = dynamic_cast(currentRoot->getElement()); + Assert(policy != NULL); + policy->addSubject(currentSubject); + //TODO maybe keep subjects not subject pointers in Policies and consume subjects here + currentSubject = NULL; +} + +void Parser::consumeCurrentCondition() +{ + Condition * temp = NULL; + if (currentCondition != NULL) { + if (currentCondition->getParent() != NULL) { //Condition is a child of another condition + currentCondition->getParent()->addCondition(*currentCondition); + } else { //Condition parent is a Rule + Rule * rule = dynamic_cast(currentRoot->getElement()); + Assert(rule != NULL); + rule->setCondition(*currentCondition); + } + temp = currentCondition->getParent(); + delete currentCondition; + } + currentCondition = temp; //switch current condition ( it may be switched to NULL if condition's parent was rule +} + +void Parser::consumeSubjectMatch(xmlChar * value) +{ + Assert( + currentAttribute != NULL && + "consuming subject match without attribute set"); + + if (currentSubject != NULL) { + currentSubject->addNewAttribute(*currentAttribute); + //[CR] matching/modyfing functions transform uri.host to uri ( etc. ) so strncmp is not needed, string equality will do + if (!strncmp(currentAttribute->getName()->c_str(), "uri", + 3) || + !strncmp(currentAttribute->getName()->c_str(), "id", 2)) { + if (value != NULL) { + currentSubject->setSubjectId(reinterpret_cast( + value)); + } else if (currentAttribute->getValue()->size()) { + currentSubject->setSubjectId( + currentAttribute->getValue()->front()); + } else { + Assert(false); + } + } + } else if (currentCondition != NULL) { + currentCondition->addAttribute(*currentAttribute); + } + + delete currentAttribute; + currentAttribute = NULL; +} + +void Parser::trim(std::string * str) +{ + std::string::size_type pos = str->find_last_not_of(whitespaces); + if (pos != std::string::npos) { + str->erase(pos + 1); + pos = str->find_first_not_of(whitespaces); + if (pos != std::string::npos) { + str->erase(0, pos); + } + } else { + str->erase(str->begin(), str->end()); + LogInfo("Warning, empty string as attribute value"); + } +} + +// KW void Parser::canonicalize(const char * input, const char * output, CanonicalizationAlgorithm canonicalizationAlgorithm){ +// KW +// KW xmlDocPtr doc = xmlParseFile(input); +// KW //xmlDocDump(stdout, doc); +// KW +// KW if(doc == NULL) +// KW { +// KW LogError("Canonicalization error, cannot parser xml file"); +// KW } +// KW +// KW +// KW int mode = -1; +// KW if(canonicalizationAlgorithm == C14N) +// KW { +// KW mode = 0; +// KW } +// KW else if(canonicalizationAlgorithm == C14NEXCLUSIVE) +// KW { +// KW mode = 1; +// KW } +// KW +// KW +// KW xmlC14NDocSave(doc, NULL, mode, NULL, 0, output, 0); +// KW +// KW xmlFreeDoc(doc); +// KW +// KW } + +// KW int Parser::extractNodeToFile(xmlTextReaderPtr reader, const char * filename){ +// KW +// KW xmlNodePtr node = xmlTextReaderExpand(reader); +// KW xmlBufferPtr buff = xmlBufferCreate(); +// KW xmlNodeDump(buff, node->doc, node, 0, 0); +// KW FILE * file = fopen(filename, "w"); +// KW if(file == NULL){ +// KW LogError("Error while trying to open file "< +#include + +namespace AceDB { +namespace AceDaoConversions { + +DPL::String convertToHash(const BaseAttributeSet &attributes); + +} +} + +#endif diff --git a/modules/ace/include/dpl/ace-dao-ro/AceDAOReadOnly.h b/modules/ace/include/dpl/ace-dao-ro/AceDAOReadOnly.h new file mode 100644 index 0000000..da94943 --- /dev/null +++ b/modules/ace/include/dpl/ace-dao-ro/AceDAOReadOnly.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file AceDAOReadOnly.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 0.1 + * @brief + */ + +#ifndef ACE_DAO_READ_ONLY_H_ +#define ACE_DAO_READ_ONLY_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AceDB { + +class AceDAOReadOnly +{ + public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, DatabaseError) + }; + + AceDAOReadOnly() {} + + static void attachToThread(); + static void detachFromThread(); + + // policy effect/decision + static OptionalPolicyResult getPolicyResult( + const BaseAttributeSet &attributes); + + static OptionalPolicyResult getPolicyResult( + const DPL::String &attrHash); + + // prompt decision + static OptionalCachedPromptDecision getPromptDecision( + const DPL::String &hash, + const std::string &userParam); + static OptionalCachedPromptDecision getPromptDecision( + const BaseAttributeSet &attribites, + const std::string &userParam); + + // resource settings + static PreferenceTypes getDevCapSetting(const std::string &resource); + static void getDevCapSettings(PreferenceTypesMap *preferences); + + // user settings + static void getWidgetDevCapSettings(BasePermissionList *permissions); + static PreferenceTypes getWidgetDevCapSetting( + const std::string &resource, + WidgetHandle handler); + + static void getAttributes(BaseAttributeSet *attributes); + + // static dev cap permissions + // + // (For a given widget handle, a set of device caps is + // granted "statically", i.e. it is determined at installation + // time that the widget will always get (at launch) the SMACK + // permissions needed to use those device caps). + // + // 'permissions' is an output parameter - it must point to + // an existing set and the function will clear it and fill + // with the device cap names as described. + static void getStaticDevCapPermissions( + int widgetHandle, + std::set *permissions); + + protected: + static int promptDecisionToInt(PromptDecision decision); + static PromptDecision intToPromptDecision(int decision); +}; + +} + +#endif diff --git a/modules/ace/include/dpl/ace-dao-ro/AceDAOUtilities.h b/modules/ace/include/dpl/ace-dao-ro/AceDAOUtilities.h new file mode 100644 index 0000000..0e0ec00 --- /dev/null +++ b/modules/ace/include/dpl/ace-dao-ro/AceDAOUtilities.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file AceDAOUtil.h + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 0.1 + * @brief + */ + +#ifndef WRT_ACE_DAO_UTILITIES_H_ +#define WRT_ACE_DAO_UTILITIES_H_ + +#include +#include +#include +#include +#include + +namespace AceDB { + +namespace AceDaoUtilities { + +BaseAttribute::Type intToAttributeType(int val); +int attributeTypeToInt(BaseAttribute::Type type); +int preferenceToInt(PreferenceTypes p); +PreferenceTypes intToPreference(int p); +VerdictTypes intToVerdict(int v); +int verdictToInt(VerdictTypes v); +bool getSubjectByUri(const std::string &uri, + DPL::DB::ORM::ace::AceSubject::Row &row); +bool getResourceByUri(const std::string &uri, + DPL::DB::ORM::ace::AceDevCap::Row &row); + +extern DPL::DB::ThreadDatabaseSupport m_databaseInterface; + +} + +} + +#endif diff --git a/modules/ace/include/dpl/ace-dao-ro/AceDatabase.h b/modules/ace/include/dpl/ace-dao-ro/AceDatabase.h new file mode 100644 index 0000000..d5b2838 --- /dev/null +++ b/modules/ace/include/dpl/ace-dao-ro/AceDatabase.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file AceDatabase.h + * @author Lukasz Marek (l.marek@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of ace database + */ + +#ifndef WRT_ENGINE_SRC_ACCESS_CONTROL_ACE_DATABASE_H +#define WRT_ENGINE_SRC_ACCESS_CONTROL_ACE_DATABASE_H + +#include +#include + +extern DPL::Mutex g_aceDbQueriesMutex; + +#define ACE_DB_INTERNAL(tlsCommand, InternalType, interface) \ + static DPL::ThreadLocalVariable *tlsCommand ## Ptr = NULL; \ + { \ + DPL::Mutex::ScopedLock lock(&g_aceDbQueriesMutex); \ + if (!tlsCommand ## Ptr) { \ + static DPL::ThreadLocalVariable tmp; \ + tlsCommand ## Ptr = &tmp; \ + } \ + } \ + DPL::ThreadLocalVariable &tlsCommand = *tlsCommand ## Ptr; \ + if (tlsCommand.IsNull()) { tlsCommand = InternalType(interface); } + +#define ACE_DB_SELECT(name, type, interface) \ + ACE_DB_INTERNAL(name, type::Select, interface) + +#define ACE_DB_INSERT(name, type, interface) \ + ACE_DB_INTERNAL(name, type::Insert, interface) + +#define ACE_DB_UPDATE(name, type, interface) \ + ACE_DB_INTERNAL(name, type::Update, interface) + +#define ACE_DB_DELETE(name, type, interface) \ + ACE_DB_INTERNAL(name, type::Delete, interface) + + +#endif // WRT_ENGINE_SRC_ACCESS_CONTROL_ACE_DATABASE_H diff --git a/modules/ace/include/dpl/ace-dao-ro/BaseAttribute.h b/modules/ace/include/dpl/ace-dao-ro/BaseAttribute.h new file mode 100644 index 0000000..1956af5 --- /dev/null +++ b/modules/ace/include/dpl/ace-dao-ro/BaseAttribute.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file IAttribute.h + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 0.1 + * @brief + */ + +#ifndef ACCESS_CONTROL_DAO_BASEATTRIBUTE_H_ +#define ACCESS_CONTROL_DAO_BASEATTRIBUTE_H_ + +#include +#include +#include +#include +#include + +namespace AceDB { + +class BaseAttribute; +typedef DPL::SharedPtr BaseAttributePtr; + +class BaseAttribute +{ + + public: + /** + * Types of attributes + */ + enum class Type { Subject, Environment, Resource, FunctionParam, Undefined }; + + struct UnaryPredicate + { + public: + UnaryPredicate(const AceDB::BaseAttribute *comp = NULL) : + m_priv(comp) + { + } + + bool operator()(const AceDB::BaseAttributePtr &comp) + { + Assert(m_priv != NULL); + if (m_priv->getName()->compare(*comp->getName()) != 0) { + return false; + } + return m_priv->getType() == comp->getType(); + } + + bool operator()(const AceDB::BaseAttributePtr &comp1, + const AceDB::BaseAttributePtr &comp2) + { + if (comp1->getType() != comp2->getType()) { + return comp1->getType() < comp2->getType(); + } + return comp1->getName()->compare(*comp2->getName()) < 0; + } + + private: + const AceDB::BaseAttribute *m_priv; + }; + + public: + BaseAttribute() : + m_typeId(Type::Undefined), + m_undetermindState(false) + {} + + virtual void setName(const std::string& name) + { + m_name = name; + } + virtual void setName(const std::string* name) + { + m_name = *name; + } + + virtual void setType(const Type& type) + { + m_typeId = type; + } + virtual Type getType() const + { + return m_typeId; + } + + virtual const std::string* getName() const + { + return &m_name; + } + + //TODO think + virtual void setUndetermind(bool tmp) + { + m_undetermindState = tmp; + } + virtual bool isUndetermind() const + { + return m_undetermindState; + } + virtual std::list * getValue() const + { + return const_cast* >(&value); + } + virtual bool isValueEmpty() const + { + return value.empty(); + } + + virtual void setValue(const std::list& arg) + { + value = arg; + } + + virtual ~BaseAttribute() + { + } + + static const char * typeToString(Type type); + + virtual std::string toString() const; + + protected: + std::string m_name; + Type m_typeId; + bool m_undetermindState; + std::list value; //string bag list +}; + +typedef std::set BaseAttributeSet; + +} + +#endif diff --git a/modules/ace/include/dpl/ace-dao-ro/BasePermission.h b/modules/ace/include/dpl/ace-dao-ro/BasePermission.h new file mode 100644 index 0000000..8305f42 --- /dev/null +++ b/modules/ace/include/dpl/ace-dao-ro/BasePermission.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file IPermission.h + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 0.1 + * @brief + */ + +#ifndef ACCESS_CONTROL_DAO_BASEPERMISSION_H_ +#define ACCESS_CONTROL_DAO_BASEPERMISSION_H_ + +#include +#include + +namespace AceDB{ + +struct BasePermission +{ + BasePermission(WidgetHandle handler, + const std::string& devCap, + PreferenceTypes accessAllowed) : + appId(handler), + devCap(devCap), + access(accessAllowed) + { + } + + WidgetHandle appId; + std::string devCap; + PreferenceTypes access; +}; + +typedef std::list BasePermissionList; + +} + +#endif diff --git a/modules/ace/include/dpl/ace-dao-ro/IRequest.h b/modules/ace/include/dpl/ace-dao-ro/IRequest.h new file mode 100644 index 0000000..2975b8b --- /dev/null +++ b/modules/ace/include/dpl/ace-dao-ro/IRequest.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file IRequest.h + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 0.1 + * @brief + */ + +#ifndef ACCESS_CONTROL_DAO_IREQUEST_H_ +#define ACCESS_CONTROL_DAO_IREQUEST_H_ + +namespace AceDB{ + +class IRequest +{ +public: + virtual ~IRequest(){} +}; + +} + +#endif diff --git a/modules/ace/include/dpl/ace-dao-ro/PreferenceTypes.h b/modules/ace/include/dpl/ace-dao-ro/PreferenceTypes.h new file mode 100644 index 0000000..0f96dc5 --- /dev/null +++ b/modules/ace/include/dpl/ace-dao-ro/PreferenceTypes.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file PreferenceTypes.h + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 0.1 + * @brief + */ + +#ifndef ACCESS_CONTROL_DAO_PREFERENCETYPES_H_ +#define ACCESS_CONTROL_DAO_PREFERENCETYPES_H_ + +#include +#include + +namespace AceDB{ + +enum class PreferenceTypes +{ + PREFERENCE_PERMIT, + PREFERENCE_DENY, + PREFERENCE_DEFAULT, + PREFERENCE_BLANKET_PROMPT, + PREFERENCE_SESSION_PROMPT, + PREFERENCE_ONE_SHOT_PROMPT +}; + + +typedef std::map PreferenceTypesMap; + +} + +#endif diff --git a/modules/ace/include/dpl/ace-dao-ro/PromptModel.h b/modules/ace/include/dpl/ace-dao-ro/PromptModel.h new file mode 100644 index 0000000..e610e12 --- /dev/null +++ b/modules/ace/include/dpl/ace-dao-ro/PromptModel.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* @file PromptModel.h + * @author Justyna Mejzner (j.kwiatkowsk@samsung.com) + * @author Jaroslaw Osmanski (j.osmanski@samsung.com) + * @version 1.0 + * + */ + +#ifndef WRT_SRC_ACCESSCONTROL_ENGINE_PROMPT_MODEL_H_ +#define WRT_SRC_ACCESSCONTROL_ENGINE_PROMPT_MODEL_H_ + +#include +#include +#include + +#include +#include + +namespace Prompt { +typedef std::vector ButtonLabels; + +class PromptLabels +{ +public: + PromptLabels(int promptType, + const Prompt::ButtonLabels& questionLabel, + const std::string& mainLabel); + DPL::OptionalString getCheckLabel() const; + bool isAllowed(const size_t buttonNumber) const; + int getPromptType() const; + const ButtonLabels& getButtonLabels() const; + const std::string& getMainLabel() const; + +private: + int m_promptType; + ButtonLabels m_buttonLabels; + std::string m_mainLabel; +}; + +typedef std::unique_ptr PromptLabelsPtr; + +enum Validity +{ + ONCE, + SESSION, + ALWAYS +}; + +class PromptAnswer +{ +public: + PromptAnswer(bool isAccessAllowed, Validity validity); + PromptAnswer(int aPromptType, unsigned int buttonAns, bool checkAns); + bool isAccessAllowed() const; + Validity getValidity() const; + +private: + bool m_isAccessAllowed; + Validity m_validity; +}; + +class PromptModel +{ + public: + static PromptLabels* getOneShotModel(const std::string& resourceId); + static PromptLabels* getSessionModel(const std::string& resourceId); + static PromptLabels* getBlanketModel(const std::string& resourceId); + + enum PromptType + { + PROMPT_ONESHOT, + PROMPT_SESSION, + PROMPT_BLANKET + }; +}; + +} // Prompt + +#endif /* WRT_SRC_ACCESSCONTROL_ENGINE_PROMPT_MODEL_H_ */ diff --git a/modules/ace/include/dpl/ace-dao-ro/TimedVerdict.h b/modules/ace/include/dpl/ace-dao-ro/TimedVerdict.h new file mode 100644 index 0000000..1fbac52 --- /dev/null +++ b/modules/ace/include/dpl/ace-dao-ro/TimedVerdict.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file TimedVerdict.h + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 0.1 + * @brief + */ + +#ifndef ACCESS_CONTROL_DAO_TIMEDVERDICT_H_ +#define ACCESS_CONTROL_DAO_TIMEDVERDICT_H_ + +#include + +namespace AceDB{ + +struct TimedVerdict +{ + VerdictTypes decision; + /*Below values are optional,its filled only when verdict depend on session*/ + std::string session; + int subjectVerdictId; +}; + +} + +#endif diff --git a/modules/ace/include/dpl/ace-dao-ro/ValidityTypes.h b/modules/ace/include/dpl/ace-dao-ro/ValidityTypes.h new file mode 100644 index 0000000..1283cf1 --- /dev/null +++ b/modules/ace/include/dpl/ace-dao-ro/ValidityTypes.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file ValidityTypes.h + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 0.1 + * @brief + */ + +#ifndef ACCESS_CONTROL_DAO_VALIDITYTYPES_H_ +#define ACCESS_CONTROL_DAO_VALIDITYTYPES_H_ + +namespace AceDB{ + +enum class ValidityTypes +{ + ONCE, + SESSION, + ALWAYS, + UNWRITEABLE +}; + +} + +#endif diff --git a/modules/ace/include/dpl/ace-dao-ro/VerdictTypes.h b/modules/ace/include/dpl/ace-dao-ro/VerdictTypes.h new file mode 100644 index 0000000..8a312b5 --- /dev/null +++ b/modules/ace/include/dpl/ace-dao-ro/VerdictTypes.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file VerdictTypes.h + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 0.1 + * @brief + */ + +#ifndef ACCESS_CONTROL_DAO_VERDICTTYPES_H_ +#define ACCESS_CONTROL_DAO_VERDICTTYPES_H_ + +namespace AceDB{ + +enum class VerdictTypes +{ + VERDICT_PERMIT, + VERDICT_DENY, + //Verdict is innapplicable if policy evaluate to INAPPLICABLE, + //in this case WRT should decide what to do + VERDICT_INAPPLICABLE, + VERDICT_UNDETERMINED, + VERDICT_UNKNOWN, //Verdict is unknown if Verdicts manager cannot find it + VERDICT_ASYNC, + VERDICT_ERROR +}; + +} + +#endif diff --git a/modules/ace/include/dpl/ace-dao-ro/common_dao_types.h b/modules/ace/include/dpl/ace-dao-ro/common_dao_types.h new file mode 100644 index 0000000..6913766 --- /dev/null +++ b/modules/ace/include/dpl/ace-dao-ro/common_dao_types.h @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * @file common_dao_types.h + * @author Michal Ciepielski (m.ciepielski@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of common data types for wrtdb + */ + +#ifndef WRT_SRC_CONFIGURATION_COMMON_DAO_TYPES_H_ +#define WRT_SRC_CONFIGURATION_COMMON_DAO_TYPES_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace WrtDB { +namespace Powder { + +typedef std::set StringSet; +//! Widget description +struct Description +{ + //!Content level + typedef enum + { + Level0 = 0, + Level1, + Level2, + Level3, + Level4, + Level5, + LevelUnknown + } LevelEnum; + struct LevelEntry + { + LevelEnum level; //!< content level + + typedef StringSet Context; + + //! POWDER context + //! xa This material appears in an artistic context + //! xb This material appears in an educational context + //! xc This material appears in a medical context + //! xd This material appears in a sports context + //! xe This material appears in a violent context + Context context; + explicit LevelEntry(LevelEnum level = LevelUnknown); + //! Function checks if context is valid + //! \param[in] level POWDER content level + //! \param[in] context POWDER context + bool isContextValid(LevelEnum level, + const DPL::OptionalString& context) const; + }; + + struct CategoryEntry + { + //! Levels entries for POWDER description + typedef std::vector LevelsContainer; + LevelsContainer levels; + //! Function checks if context is valid + //! \param[out] reason set if context invalid + //! \param[in] level POWDER content level + //! \param[in] context POWDER context + bool isCategoryValid(LevelEntry& reason, + LevelEnum level, + const DPL::OptionalString& context) const; + }; + + //! POWDER Category -> Category entry map for Widget + //! + //! nu Nudity + //! se Sex + //! vi Violence + //! la Potentially offensive language + //! dr Drug use + //! ga Gambling + //! ha Hate or harmful activities + //! ug Use of user-generated content + typedef std::map CategoryEntries; + + CategoryEntries categories; + + //! Age rating for widget + //! If Null not set + DPL::OptionalInt ageRating; +}; +} // namespace Powder + +namespace ChildProtection { + +//! Blacklist with forbidden URLs +//! It should be stored in WidgetDAO +typedef std::vector BlackList; + +//! Widget Child protection record +//! Record should be stored in WingetDAO +struct Record +{ + //! Child protection enabled + bool enabled; + explicit Record(bool enabled) : + enabled(enabled) + { + } +}; + +//! Powder processing +struct PowderRules +{ + //! Rule set by parent about forbidden category + //! Powder category + //! nu Nudity + //! se Sex + //! vi Violence + //! la Potentially offensive language + //! dr Drug use + //! ga Gambling + //! ha Hate or harmful activities + //! ug Use of user-generated content + //! Powder context + //! xa This material appears in an artistic conteaxt + //! xb This material appears in an educational context + //! xc This material appears in a medical context + //! xd This material appears in a sports context + //! xe This material appears in a violent context + struct CategoryRule + { + DPL::String category; + Powder::Description::LevelEnum level; + DPL::OptionalString context; + explicit CategoryRule(const DPL::String& category = DPL::String(), + Powder::Description::LevelEnum level = + Powder::Description::LevelUnknown, + const DPL::OptionalString& context = DPL::OptionalString()); + }; + + struct PowderResult + { + //! Reasoning outcome: part of POWDER description used to invalidate + Powder::Description::LevelEntry invalidDescription; + //! Reasoning outcome: rule set by parent not full filed by description + CategoryRule invalidRule; + + //! Reasoning outcome: type of invalidity + enum InvalidReason + { + InvalidRule, //!< One of rules was not fulfilled + InvalidAge, //!< Age is invalid + AgeRatingNotSet, //!< Age rating for widget is not set + Valid //!< Description valid + }; + InvalidReason reason; + explicit PowderResult(InvalidReason reason = Valid, + const Powder::Description::LevelEntry& invalidDescription = + Powder::Description::LevelEntry(), + const CategoryRule& invalidRule = CategoryRule()); + }; + + typedef std::pair ResultPair; + + //! Function checks if rule is fulfilled by description + //! \param[in] rule checked rule + //! \param[in] description + //! \retval true rule is valid + //! \retval false rule is invalid + ResultPair isRuleValidForDescription(const CategoryRule& rule, + const Powder::Description& description) const; + //! Function checks if age limit is fulfilled by description + //! \param[in] description + //! \retval true age is valid + //! \retval false age is invalid + ResultPair isAgeValidForDescription( + const Powder::Description& description) const; + + //! It is the maximum age rating valid for child + //! Uniform age is stored in WidgetDAO + DPL::OptionalInt ageLimit; + + //! Set to true if age rating is required + //! If ageLimit is not set value is ignored + bool isAgeRatingRequired; + + //! Set of rules configured by parent + //! Rules are stored in WidgetDAO and are uniform for all widgets + typedef std::vector RulesContainer; + RulesContainer rules; + + //! Function check if Widget description is valid for ChildProtection + //! configuration + //! \param description widget description + //! \retval true widget is valid + //! \retval false widget is invalid + ResultPair isDescriptionValid(const Powder::Description& description) + const; + + PowderRules() : + isAgeRatingRequired(false) + { + } +}; +} // namespace ChildProtection + +class PluginMetafileData +{ + public: + struct Feature + { + std::string m_name; + std::set m_deviceCapabilities; + + bool operator< (const Feature& obj) const + { + return m_name < obj.m_name; + } + }; + typedef std::set FeatureContainer; + + public: + + PluginMetafileData() + { + } + + std::string m_libraryName; + std::string m_featuresInstallURI; + std::string m_featuresKeyCN; + std::string m_featuresRootCN; + std::string m_featuresRootFingerprint; + + FeatureContainer m_featureContainer; +}; + +class PluginObjectsDAO +{ + public: + typedef std::set Objects; + typedef DPL::SharedPtr ObjectsPtr; + + public: + explicit PluginObjectsDAO() {} + + protected: + ObjectsPtr m_implemented; + ObjectsPtr m_dependent; +}; + +/** + * @brief Widget id describes web-runtime global widget identifier. + * + * Notice that only up to one widget can exist at the same time. + * DbWidgetHandle can be translated into corresponding WidgetModel by invoking + * FindWidgetModel routine. + */ +typedef int DbWidgetHandle; + +/** + * @brief Structure to hold the information of widget's size + */ +struct DbWidgetSize +{ + DPL::OptionalInt width; + DPL::OptionalInt height; + + DbWidgetSize(DPL::OptionalInt w = DPL::OptionalInt::Null, + DPL::OptionalInt h = DPL::OptionalInt::Null) : + width(w), + height(h) + { + } +}; + +inline bool operator ==(const DbWidgetSize &objA, const DbWidgetSize &objB) +{ + if (!objA.height || !objA.width || !objB.width || !objB.height) { + return false; + } else { + return *objA.height == *objB.height && *objA.width == *objB.width; + } +} + +/** + * Widget [G]lobal [U]nique [ID]entifier + * Orginated from appstore ID + */ +typedef DPL::OptionalString WidgetGUID; + +struct WidgetAccessInfo +{ + DPL::String strIRI; /* origin iri */ + bool bSubDomains; /* do we want access to subdomains ? */ + + bool operator ==(const WidgetAccessInfo& info) const + { + return info.strIRI == strIRI && + info.bSubDomains == bSubDomains; + } +}; + +typedef std::list WidgetAccessInfoList; + +typedef std::list WindowModeList; + +/** + * @brief Widget configuration parameter key + */ +typedef DPL::String WidgetParamKey; + +/** + * @brief Widget configuration parameter value + */ +typedef DPL::String WidgetParamValue; + +/** + * @brief A map of widget configuration parameters. + * + * Widget configuration parameters are read from database and are stored + * along with feature that they describe. + */ +typedef std::multimap WidgetParamMap; + +/** + * @brief Widget feature host information about possible javascript extensions + * that widget may use + * + * Widget features are declared in configuration file in widget installation + * package. Each declared special feature is contained in some wrt-plugin that + * declares to implement it. After widget launch wrt searches for proper plugin + * libraries and load needed features. + * + * Widget features can be required or optional. It is possible to start widget + * without missing feature. When required feature cannot be loaded widget will + * not start. + */ + +enum { + INVALID_PLUGIN_HANDLE = -1 +}; +typedef int DbPluginHandle; + +struct DbWidgetFeature +{ + DPL::String name; /// Feature name + bool required; /// Whether feature is required + DbPluginHandle pluginId; /// Plugin id that implement this feature + WidgetParamMap params; /// Widget's params + + DbWidgetFeature() : + required(false), + pluginId(INVALID_PLUGIN_HANDLE) + { + } +}; + +inline bool operator < (const DbWidgetFeature &objA, + const DbWidgetFeature &objB) +{ + return objA.name.compare(objB.name) < 0; +} + +inline bool operator==(const DbWidgetFeature &featureA, + const DbWidgetFeature &featureB) +{ + return featureA.name == featureB.name && + featureA.required == featureB.required && + featureA.pluginId == featureB.pluginId; +} + +/** + * @brief Default container for features list + */ +typedef std::multiset DbWidgetFeatureSet; + +/** + * @brief Default container with DbWidgetHandle's + */ +typedef std::list DbWidgetHandleList; + +/** + * @brief Widget specific type + * + * Widget type describes belowed in WAC, TIZEN WebApp + */ +enum AppType +{ + APP_TYPE_UNKNOWN = 0, // unknown + APP_TYPE_WAC10, // WAC 1.0 + APP_TYPE_WAC20, // WAC 2.0 + APP_TYPE_TIZENWEBAPP, // Tizen webapp +}; + +class WidgetType +{ + public: + WidgetType() + :appType(APP_TYPE_UNKNOWN) + { + } + WidgetType(const AppType type) + :appType(type) + { + } + bool operator== (const AppType& other) const + { + return appType == other; + } + std::string getApptypeToString() + { + switch (appType) { +#define X(x) case x: return #x; + X(APP_TYPE_UNKNOWN) + X(APP_TYPE_WAC10) + X(APP_TYPE_WAC20) + X(APP_TYPE_TIZENWEBAPP) +#undef X + default: + return "UNKNOWN"; + } + } + + AppType appType; +}; + +} // namespace WrtDB + +struct WidgetSetting +{ + DPL::String settingName; + DPL::String settingValue; + + bool operator ==(const WidgetSetting& info) const + { + return (info.settingName == settingName && + info.settingValue == settingValue); + } + bool operator !=(const WidgetSetting& info) const + { + return (info.settingName != settingName || + info.settingValue != settingValue); + } +}; + +typedef std::list WidgetSettings; + +/** + * @brief Widget Application Service + * + * Application sercvice describes details of behaviour + * when widget receives aul bundle data. + */ +struct WidgetApplicationService +{ + public: + DPL::String src; /* start uri */ + DPL::String operation; /* service name */ + DPL::String scheme; /* scheme type*/ + DPL::String mime; /* mime type */ + + bool operator== (const WidgetApplicationService& other) const + { + return src == other.src && + operation == other.operation && + scheme == other.scheme && + mime == other.mime; + } +}; + +typedef std::list WidgetApplicationServiceList; +#endif /* WRT_SRC_CONFIGURATION_COMMON_DAO_TYPES_H_ */ diff --git a/modules/ace/include/dpl/ace-dao-ro/wrt_db_types.h b/modules/ace/include/dpl/ace-dao-ro/wrt_db_types.h new file mode 100644 index 0000000..a0dc08b --- /dev/null +++ b/modules/ace/include/dpl/ace-dao-ro/wrt_db_types.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * wrt_db_types.h + * + * Created on: Nov 21, 2011 + * Author: Krzysztof Jackiewicz(k.jackiewicz@samsung.com) + */ + +#ifndef WRT_DB_TYPES_H_ +#define WRT_DB_TYPES_H_ + +#include + +typedef WrtDB::DbWidgetHandle WidgetHandle; +typedef WrtDB::DbWidgetHandleList WidgetHandleList; + +typedef WrtDB::DbWidgetFeature WidgetFeature; +typedef WrtDB::DbWidgetFeatureSet WidgetFeatureSet; + +typedef WrtDB::DbWidgetSize WidgetSize; + +typedef WrtDB::DbPluginHandle PluginHandle; + +#endif /* WRT_DB_TYPES_H_ */ diff --git a/modules/ace/include/dpl/ace-dao-rw/AceDAO.h b/modules/ace/include/dpl/ace-dao-rw/AceDAO.h new file mode 100644 index 0000000..82b86f6 --- /dev/null +++ b/modules/ace/include/dpl/ace-dao-rw/AceDAO.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file AceDAO.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 0.1 + * @brief + */ + +#ifndef ACEDAO_H_ +#define ACEDAO_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace AceDB { +/* + * + */ +class AceDAO : public AceDAOReadOnly +{ + public: + + AceDAO() {} + + // Policy Decisions + static void setPolicyResult( + const BaseAttributeSet &attributes, + const PolicyResult &policyResult); + + static void removePolicyResult( + const BaseAttributeSet &attributes); + + // PromptDecision + static void setPromptDecision( + const DPL::String &hash, + const DPL::String &userParam, + const DPL::OptionalString &session, + PromptDecision decision); + static void setPromptDecision( + const BaseAttributeSet &attributes, + const DPL::String &userParam, + const DPL::OptionalString &session, + PromptDecision decision); + + static void clearPromptDecisions(void); + + // reseting database + static void clearWidgetDevCapSettings(void); + static void clearDevCapSettings(void); + static void clearAllSettings(void); + static void resetDatabase(void); + + // resource settings + static void setDevCapSetting(const std::string &resource, + PreferenceTypes preference); + static void removeDevCapSetting(const std::string &resource); + + // user settings + static void setWidgetDevCapSetting( + const std::string &resource, + WidgetHandle handler, + PreferenceTypes); + static void removeWidgetDevCapSetting( + const std::string &resource, + WidgetHandle handler); + + // resource and subject management + static int addResource(const std::string &request); + + // utilities + static void addAttributes(const BaseAttributeSet &attributes); + + // set static dev cap permissions + // + // (For a given widget handle, a set of device caps is + // granted "statically", i.e. it is determined at installation + // time that the widget will always get (at launch) the SMACK + // permissions needed to use those device caps). + // + // 'permissions' is the set of device cap names to be assigned + // as "statically permitted" to the given widget handle + static void setStaticDevCapPermissions( + int widgetHandle, + const std::set &permissions); + +}; +} +#endif /* ACEDAO_H_ */ diff --git a/modules/ace/include/dpl/ace/AbstractPolicyEnforcementPoint.h b/modules/ace/include/dpl/ace/AbstractPolicyEnforcementPoint.h new file mode 100644 index 0000000..b808a76 --- /dev/null +++ b/modules/ace/include/dpl/ace/AbstractPolicyEnforcementPoint.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WRT_SRC_ACCESS_CONTROL_LOGIC_ABSTRACT_POLICY_ENFORCEMENT_POINTS_H +#define WRT_SRC_ACCESS_CONTROL_LOGIC_ABSTRACT_POLICY_ENFORCEMENT_POINTS_H + +#include +#include +#include + +class AbstractPolicyEnforcementPoint +{ + public: + typedef DPL::Event::ICDelegate ResponseReceiver; + virtual PolicyResult check(Request &request) = 0; +}; + +#endif /* WRT_SRC_ACCESS_CONTROL_LOGIC_ABSTRACT_POLICY_ENFORCEMENT_POINTS_H */ diff --git a/modules/ace/include/dpl/ace/AbstractPolicyInformationPoint.h b/modules/ace/include/dpl/ace/AbstractPolicyInformationPoint.h new file mode 100644 index 0000000..e8d95ed --- /dev/null +++ b/modules/ace/include/dpl/ace/AbstractPolicyInformationPoint.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class AbstractPolicyInformationPoint +{ + public: + virtual ~AbstractPolicyInformationPoint() {} +}; diff --git a/modules/ace/include/dpl/ace/AbstractTreeElement.h b/modules/ace/include/dpl/ace/AbstractTreeElement.h new file mode 100644 index 0000000..634eea0 --- /dev/null +++ b/modules/ace/include/dpl/ace/AbstractTreeElement.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : AbstractTreeElement.h +// @ Date : 2009-05-25 +// @ Author : Samsung +// +// +#if !defined(_ABSTRACTTREEELEMENT_H) +#define _ABSTRACTTREEELEMENT_H + +#include +#include "Effect.h" +#include + +class AbstractTreeElement +{ + public: + + virtual ~AbstractTreeElement() + { + } + + virtual void printData() = 0; + + virtual void serialize(std::ostream& os) = 0; + protected: +}; + +#endif //_ABSTRACTTREEELEMENT_H diff --git a/modules/ace/include/dpl/ace/AsyncVerdictResultListener.h b/modules/ace/include/dpl/ace/AsyncVerdictResultListener.h new file mode 100644 index 0000000..9b0d4f6 --- /dev/null +++ b/modules/ace/include/dpl/ace/AsyncVerdictResultListener.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _ASYNCVERDICT_H +#define _ASYNCVERDICT_H + +#include +#include +#include + +class AsyncVerdictResultListener +{ + public: + virtual void onVerdict(const Verdict &verdict, + const Request *request) = 0; + virtual ~AsyncVerdictResultListener() + { + } +}; + +#endif diff --git a/modules/ace/include/dpl/ace/Attribute.h b/modules/ace/include/dpl/ace/Attribute.h new file mode 100644 index 0000000..ddf100b --- /dev/null +++ b/modules/ace/include/dpl/ace/Attribute.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : Attribute.h +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#if !defined(_ATTRIBUTE_H) +#define _ATTRIBUTE_H + +#include +#include +#include +#include + +#include + +class Attribute : public AceDB::BaseAttribute +{ + public: + /** + * Types of match functions + */ + enum class Match { Equal, Glob, Regexp, Error }; + /** + * Types of attribute value modifiers + */ + enum class Modifier { Non, Scheme, Authority, SchemeAuthority, Host, Path }; + /** + * Possible match results + */ + enum class MatchResult { MRUndetermined = -1, MRFalse = 0, MRTrue = 1}; + + public: + + /** + * New attribute constructor + * @param name name of the new attribute + * @param matchFunction match function used in the attribute + * @param type attribute type + */ + Attribute(const std::string *name, + const Match matchFunction, + const Type type); + + /** + * Constructor used by ACE serializer + * @param is Input stream to which the object will be serialized + * @deprecated + */ + Attribute(std::istream& is); + + /** + * Constructor used to create default attribute ( used for unit tests ) + * @param nm name of the default attribute + */ + Attribute(const std::string& nm) : + matchFunction(Match::Error), + modifierFunction(Modifier::Non) + { + m_name = nm; + m_typeId = Type::Subject; + m_undetermindState = false; + } + + /** + * Destructor + */ + virtual ~Attribute(); + + bool serialize(std::ostream& os) const; + + std::list * getValue() const + { + return AceDB::BaseAttribute::getValue(); + } + Match getMatchFunction() const + { + return matchFunction; + } + + /* --- Setters --- */ + void addValue (const std::string *value); + + MatchResult matchAttributes(const BaseAttribute *) const; + + /** + * Operator used in for attribute set,used to distinguished only attribute names + * It cannot take attribute type into consideration + */ + bool operator< (const Attribute & obj) const + { + int result = this->m_name.compare(*obj.getName()); + if (result == 0) { //If names are equal check attribute types + if (this->m_typeId < obj.getType()) { + result = -1; + } else if (this->m_typeId > obj.getType()) { + result = 1; + } + } + //If result is negative that means that 'this' was '<' than obj + return result < 0; + } + + /** Checks if object type is equal to argument */ + bool instanceOf(Type type_) + { + return type_ == m_typeId; + } + + friend std::ostream & operator<<(std::ostream & out, + const Attribute & attr); + + protected: + + bool searchAndCut(const char *); + + /* + * URI definition from rfc2396 + * + * ://? + * Each of the components may be absent, apart from the scheme. + * Host is a part of authority as in definition below: + * + * authority = server | reg_name + * server = [ [ userinfo "@" ] hostport ] + * @: + * + * Extract from rfc2396 + * The authority component is preceded by a double slash "//" and is + * terminated by the next slash "/", question-mark "?", or by the end of + * the URI. Within the authority component, the characters ";", ":", + * "@", "?", and "/" are reserved. + * + * Modifiers should return pointer to empty string if given part of string was empty. + * Modifiers should return NULL if the string to be modified was not an URI. + */ + std::string * uriScheme(const std::string *) const; + std::string * uriAuthority(const std::string *) const; + std::string * uriSchemeAuthority(const std::string *) const; + std::string * uriHost(const std::string *) const; + std::string * uriPath(const std::string *) const; + std::string * applyModifierFunction(const std::string * val) const; + + bool parse(const std::string *input, + std::string *part) const; + bool find_error(const std::string *part) const; + + bool checkScheme(const std::string *scheme) const; + bool checkAuthority(const std::string *scheme) const; + std::string * getHost(const std::string *scheme) const; + bool checkPath(const std::string *scheme) const; + + bool isSchemeAllowedCharacter(int c) const; + bool isSegmentAllowedCharacter(int c) const; + bool isUserInfoAllowedString(const std::string *str) const; + bool isHostAllowedString(const std::string *str) const; + bool isHostNameAllowedString(const std::string * str) const; + bool isIPv4AllowedString(const std::string * str) const; + bool isDomainLabelAllowedString(const char * data, + int lenght) const; + bool isTopLabelAllowedString(const char* data, + int lenght) const; + + bool isUnreserved(int c) const; + bool isAlphanum(int c) const; + bool isEscaped(const char esc[3]) const; + bool isHex(int c) const; + + MatchResult lists_comparator( + const std::list *first, + const std::list *second, + MatchResult (*comparator)(const std::string *, + const std::string *)) const; + + /** + * Map used to check if character is a 'mark' + */ + static const bool mark[256]; + /** + * Map used to check if character is a 'digit' + * + */ + static const bool digit[256]; + /** + * Map used to check if character is an 'alphanumeric' value + * + */ + static const bool alpha[256]; + + protected: + Match matchFunction; + Modifier modifierFunction; +}; + +typedef AceDB::BaseAttributeSet AttributeSet; + +//TODO remove later or ifdef debug methods +void printAttributes(const AttributeSet& attrs); +void printAttributes(const std::list & attrs); + +#endif //_ATTRIBUTE_H diff --git a/modules/ace/include/dpl/ace/Combiner.h b/modules/ace/include/dpl/ace/Combiner.h new file mode 100644 index 0000000..62a044f --- /dev/null +++ b/modules/ace/include/dpl/ace/Combiner.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : Combiner.h +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#if !defined(_COMBINER_H) +#define _COMBINER_H + +#include + +#include +#include + +class Combiner +{ + protected: + + const AttributeSet * attrSet; + + public: + + virtual Effect combineRules(const TreeNode * rule) = 0; + virtual Effect combinePolicies(const TreeNode * policy) = 0; + + const AttributeSet * getAttributeSet() const + { + return this->attrSet; + } + void setAttributeSet(const AttributeSet * attrSet) + { + this->attrSet = attrSet; + } + virtual ~Combiner() + { + } //attrSet is deleted elsewhere +}; + +#endif //_COMBINER_H diff --git a/modules/ace/include/dpl/ace/CombinerImpl.h b/modules/ace/include/dpl/ace/CombinerImpl.h new file mode 100644 index 0000000..02cd8c6 --- /dev/null +++ b/modules/ace/include/dpl/ace/CombinerImpl.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : CombinerImpl.h +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#ifndef _COMBINER_IMPL_H +#define _COMBINER_IMPL_H + +#include +#include + +#include "Combiner.h" +#include "Effect.h" +#include "Policy.h" +#include "Subject.h" + +class CombinerImpl : public Combiner +{ + public: + + virtual Effect combineRules(const TreeNode * rule); + virtual Effect combinePolicies(const TreeNode * policy); + + virtual ~CombinerImpl() + { + } + + protected: + + bool checkIfTargetMatches(const std::list * subjectsSet, + bool &isUndetermined); + + Effect combine(Policy::CombineAlgorithm algorithm, + std::list & effects); + + Effect denyOverrides(const std::list & effects); + Effect permitOverrides(const std::list & effects); + Effect firstApplicable(const std::list & effects); + Effect firstMatchingTarget(const std::list & effects); + + std::list * convertEffectsToInts(const std::list * effects); + Effect convertIntToEffect(int intEffect); + + void showEffectList(std::list & effects) + { + std::list::iterator it = effects.begin(); + for (; it != effects.end(); ++it) { + LogDebug(toString(*it)); + } + } + + private: + bool isError(const std::list & effects); + static const int + DenyInt, + UndeterminedInt, + PromptOneShotInt, + PromptSessionInt, + PromptBlanketInt, + PermitInt, + InapplicableInt, + NotMatchingTargetInt, + ErrorInt; +}; + +#endif //_COMBINERIMPL_H diff --git a/modules/ace/include/dpl/ace/Condition.h b/modules/ace/include/dpl/ace/Condition.h new file mode 100644 index 0000000..4574673 --- /dev/null +++ b/modules/ace/include/dpl/ace/Condition.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// File: Condition.h +// Author: notroot +// +// Created on June 3, 2009, 9:00 AM +// +#ifndef _CONDITION_H +#define _CONDITION_H + +#include +#include +#include +#include + +#include "Attribute.h" +#include "Effect.h" +#include "TreeNode.h" + +class Condition +{ + public: + enum CombineType + { + AND, OR + }; + + void addCondition(const Condition & condition) + { + this->conditions.push_back(condition); + } + + void addAttribute(const Attribute & attribute) + { + this->attributes.push_back(attribute); + } + + void setCombineType(CombineType type) + { + this->combineType = type; + } + + Condition() : combineType(AND), + parent(NULL) + { + } + + Condition(CombineType type) : combineType(type), + parent(NULL) + { + } + + virtual ~Condition() + { + } + + Condition * getParent() + { + return this->parent; + } + + void setParent(Condition * condition) + { + this->parent = condition; + } + + Attribute::MatchResult evaluateCondition( + const AttributeSet * attrSet) const; + + friend std::ostream & operator<<(std::ostream & out, + Condition & condition) + { + FOREACH (it, condition.attributes) + { + out << *it; + } + return out; + } + //[CR] change function name + void getAttributes(AttributeSet * attrSet); + + //deserializer, TODO - first condition doesnt have Condtion parent + Condition(std::istream&, + Condition* parent = NULL); + + bool serialize(std::ostream&); + + private: + Attribute::MatchResult evaluateChildConditions( + const AttributeSet * attrSet, + bool &isFinalMatch, + bool & undefinedMatchFound) const; + + Attribute::MatchResult evaluateAttributes( + const AttributeSet * attrSet, + bool& isFinalMatch, + bool & undefinedMatchFound) const; + + // KW Attribute::MatchResult performANDalgorithm(const std::set * attributes) const; + + // KW Attribute::MatchResult performORalgorithm(const std::set * attributes) const; + + bool isEmpty() const + { + return attributes.empty() && conditions.empty(); + } + + bool isAndCondition() const + { + return combineType == AND; + } + + bool isOrCondition() const + { + return combineType == OR; + } + + std::list conditions; + CombineType combineType; + std::list attributes; + Condition *parent; +}; + +#endif /* _CONDITION_H */ + diff --git a/modules/ace/include/dpl/ace/ConfigurationManager.h b/modules/ace/include/dpl/ace/ConfigurationManager.h new file mode 100644 index 0000000..9de311a --- /dev/null +++ b/modules/ace/include/dpl/ace/ConfigurationManager.h @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _CONFIGURATIONMANAGER_H_ +#define _CONFIGURATIONMANAGER_H_ + +#include +#include +#include +#include +#include "Constants.h" +#include +#include +#include + +#define ATTR_ACTIVE_POLICY BAD_CAST("active") + +#define PARSER_ERROR 1 +#define PARSER_SUCCESS 0 + +class ConfigurationManager +{ + public: + enum ConfigurationManagerResult + { + CM_OPERATION_SUCCESS = 0, + CM_GENERAL_ERROR = -1, + CM_FILE_EXISTS = -2, + CM_REMOVE_ERROR = -3, + CM_REMOVE_CURRENT = -4, + CM_REMOVE_NOT_EXISTING = -5 + }; + + /** + * Current policy file getter + * @return Name of the current ACE policy file + */ + std::string getCurrentPolicyFile(void) const; + + /** + * ACE policy file path getter + * @return Full path to ACE current policy file + */ + std::string getFullPathToCurrentPolicyFile(void) const; + + /** + * ACE policy dtd file path getter + * @return Full path to ACE current policy file + */ + std::string getFullPathToCurrentPolicyXMLSchema(void) const; + + /** + * ACE policy storage path getter + * @return Full path to ACE policy file storage + */ + std::string getStoragePath(void) const; + + /** + * Adds file to ACE policy storage + * @param filePath full path to policy to be added + * @return CM_OPERATION_SUCCESS on success, + * CM_FILE_EXISTS if file with the same name already exists in the storage (the new file is not added), + * other error code on other error + * + */ + int addPolicyFile(const std::string & filePath); + + /** + * Removes file with a given filename from ACE policy storage + * @param fileName name of the policy to be removed + * @return CM_OPERATION_SUCCESS on success, + * CM_REMOVE_CURRENT if file to be removed is a current file (it cannot be removed) + * CM_REMOVE_NOT_EXISTING if file already doesn't exists + * other error code on other error + * + */ + int removePolicyFile(const std::string& fileName); + + //change current PolicyFile + /** + * @param filePath Name of the policy file in the policy storage that should be made + * a current policy file + * @param CM_OPERATION_SUCCESS on success or error code on error + */ + int changeCurrentPolicyFile(const std::string& filePath); + + //TODO this getInstance() method is a little bit to complicated + /** + * Method to obtain instance of configuration manager + * @return retuns pointer to configuration manager or NULL in case of error + */ + static ConfigurationManager * getInstance() + { + if (!instance) { + instance = new ConfigurationManager(); + if (instance->parse(std::string(ACE_CONFIGURATION_PATH)) != + PARSER_SUCCESS) { + delete instance; + LogError( + "Couldn't parse configuration file " ACE_CONFIGURATION_PATH); + return NULL; + } + } + return instance; + } + + /** + * Extracts filename from full path + * @param path Full path from which filename should be extracted + * @return returns extracted filename + */ + std::string extractFilename(const std::string& path) const; + + protected: + + /** + * Parse given ACE configuration file + * @param configFileName full path to configuration file to be parsed + * @return PARSER_SUCCESS on succes, PARSER_ERROR on error + */ + int parse(const std::string &configFileName); + /** + * @param path full path to the file which size should be obtained + * @return size of a given file in bytes + */ + int getFileSize(const std::string & path) const; + bool copyFile(FILE * source, + FILE * destination, + int lenght = 1024) const; + bool checkIfFileExistst(const std::string & newFilePath) const; + + const std::list & getPolicyFiles() const + { + return policyFiles; + } + + const std::string & getConfigFile() const + { + return configFile; + } + + ConfigurationManager() : xmlActive(false) + { + } + virtual ~ConfigurationManager() + { + } + + private: + + /** + * Internal parser methods + */ + void extractFileAttributes(void); + void startNodeHandler(void); + void endNodeHandler(void); + void textNodeHandler(void); + void processNode(void); + //Save configuration file + int saveConfig(); + + //Private fields for parser state representation + xmlTextReaderPtr reader; + std::string currentText; + bool xmlActive; + + static ConfigurationManager * instance; + + /** + * Full path to ACE configuration file + */ + std::string configFile; + /** + * ACE policy file storage path + */ + std::string storagePath; + /** + * Name of the current ACE policy + */ + std::string currentPolicyFile; + /** + * List of available ACE policy files + */ + std::list policyFiles; + + //////////NEW +}; + +#endif + diff --git a/modules/ace/include/dpl/ace/Constants.h b/modules/ace/include/dpl/ace/Constants.h new file mode 100644 index 0000000..db1b798 --- /dev/null +++ b/modules/ace/include/dpl/ace/Constants.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file Constants.h + * @author Piotr Fatyga (p.fatyga@samsung.com) + * @version 0.1 + * @brief + */ + +#ifndef _CONSTANTS_H +#define _CONSTANTS_H + +#define ACE_MAIN_STORAGE "/usr/etc/ace" +#define ACE_CONFIGURATION_PATH ACE_MAIN_STORAGE "/config.xml" +#define ACE_CONFIGURATION_DTD ACE_MAIN_STORAGE "/config.dtd" +#define ACE_DTD_LOCATION ACE_MAIN_STORAGE "/bondixml.dtd" + +/////////////////FOR GUI////////////////////// + +#define MYSTERIOUS_BITMAP "/usr/apps/org.tizen.policy/d.png" +#define MYSTERIOUS_BITMAP2 "/usr/apps/org.tizen.policy/file.png" + +///////////////////FOR TESTS////////////////////////// + +#define COMBINER_TEST "/usr/etc/ace/CMTest/com_general-test.xml" +#define CONFIGURATION_MGR_TEST_PATH "/usr/etc/ace/CMTest/" +#define CONFIGURATION_MGR_TEST_CONFIG ACE_MAIN_STORAGE "/CMTest/pms_config.xml" +#define CONFIGURATION_MGR_TEST_POLICY_STORAGE ACE_MAIN_STORAGE "/CMTest/active" +#define CONFIGURATION_MGR_TEST_POLICY_STORAGE_MOVED ACE_MAIN_STORAGE \ + "/CMTest/activeMoved" +#define CONFIGURATION_MGR_TEST_POLICY CONFIGURATION_MGR_TEST_POLICY_STORAGE \ + "/pms_general-test.xml" +#define POLICIES_TO_SIGN_DIR ACE_MAIN_STORAGE "/SignerTests/" + +#define OUTPUT_DIR ACE_MAIN_STORAGE "/SignerTests/signedPolicies/" +#define PRIVATE_KEY_DIR ACE_MAIN_STORAGE "/SignerTests/PrvKey/" +#define X509_DATA_BASE_DIR ACE_MAIN_STORAGE "/SignerTests/X509Data/" + +#endif /* _CONSTANTS_H */ + diff --git a/modules/ace/include/dpl/ace/Effect.h b/modules/ace/include/dpl/ace/Effect.h new file mode 100644 index 0000000..301e821 --- /dev/null +++ b/modules/ace/include/dpl/ace/Effect.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : Effect.h +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#if !defined(_EFFECT_H) +#define _EFFECT_H + +enum Effect +{ + Deny =0, + Undetermined=1, // jk mb added this enum, so the ones below are inceremented!!!!!!! + PromptOneShot =2, + PromptSession =3, + PromptBlanket =4, + Permit =5, + Inapplicable =6, + NotMatchingTarget=7, + Error +}; + +const char * toString(Effect); + +#endif //_EFFECT_H diff --git a/modules/ace/include/dpl/ace/NodeFactory.h b/modules/ace/include/dpl/ace/NodeFactory.h new file mode 100644 index 0000000..9abb490 --- /dev/null +++ b/modules/ace/include/dpl/ace/NodeFactory.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if !defined(_NODE_FACTORY_H) +#define _NODE_FACTORY_H + +#include +#include "TreeNode.h" +#include "Policy.h" +#include "PolicySet.h" +#include "Rule.h" + +class NodeFactory +{ + public: + static NodeFactory* getInstance(); + AbstractTreeElement* create(std::istream&, + TreeNode:: TypeID); + private: + NodeFactory() + { + } + static NodeFactory* pInstance; +}; + +#endif + diff --git a/modules/ace/include/dpl/ace/PermissionTriple.h b/modules/ace/include/dpl/ace/PermissionTriple.h new file mode 100644 index 0000000..c55f9d7 --- /dev/null +++ b/modules/ace/include/dpl/ace/PermissionTriple.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : PermissionTriple.h +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#if !defined(_PERMISSION_TRIPLE_H) +#define _PERMISSION_TRIPLE_H + +#include +#include +#include +#include + +typedef AceDB::BasePermission PermissionTriple; +typedef AceDB::BasePermissionList PermissionList; + +struct GeneralSetting +{ + GeneralSetting(const std::string& resourceName, + AceDB::PreferenceTypes accessAllowed) : generalSettingName(resourceName), + access(accessAllowed) + { + } + std::string generalSettingName; + AceDB::PreferenceTypes access; +}; + +#endif //_PERMISSION_TRIPLE_H diff --git a/modules/ace/include/dpl/ace/Policy.h b/modules/ace/include/dpl/ace/Policy.h new file mode 100644 index 0000000..e70a355 --- /dev/null +++ b/modules/ace/include/dpl/ace/Policy.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : Policy.h +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#if !defined(_POLICY_H) +#define _POLICY_H + +#include + +#include +#include +#include +#include +#include +#include + +class Policy : public AbstractTreeElement, + DPL::Noncopyable +{ + public: + enum CombineAlgorithm { DenyOverride, PermitOverride, FirstApplicable, + FirstTargetMatching }; + + Policy(std::istream& is); + + void serialize(std::ostream& os); + + Policy() + { + combineAlgorithm = DenyOverride; + subjects = new std::list(); + } + + CombineAlgorithm getCombineAlgorithm() const + { + return this->combineAlgorithm; + } + + void setCombineAlgorithm(CombineAlgorithm algorithm) + { + this->combineAlgorithm = algorithm; + } + + const std::list * getSubjects() const + { + return this->subjects; + } + + void addSubject(const Subject * subject) + { + if (this->subjects == NULL) { + return; + } + this->subjects->push_back(subject); + } + + virtual ~Policy(); + + void printData(); + + std::string printCombineAlgorithm(CombineAlgorithm algorithm); + + private: + std::list *subjects; + CombineAlgorithm combineAlgorithm; +}; + +const char * toString(Policy::CombineAlgorithm algorithm); + +#endif //_POLICY_H diff --git a/modules/ace/include/dpl/ace/PolicyEffect.h b/modules/ace/include/dpl/ace/PolicyEffect.h new file mode 100644 index 0000000..43c79d7 --- /dev/null +++ b/modules/ace/include/dpl/ace/PolicyEffect.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file PolicyEffect.h + * @author B.Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of PolicyEffect type. + */ +#ifndef _SRC_ACCESS_CONTROL_COMMON_POLICY_EFFECT_H_ +#define _SRC_ACCESS_CONTROL_COMMON_POLICY_EFFECT_H_ + +enum class PolicyEffect { + DENY = 0, + PERMIT, + PROMPT_ONESHOT, + PROMPT_SESSION, + PROMPT_BLANKET +}; + +inline static std::ostream & operator<<(std::ostream& stream, + PolicyEffect effect) +{ + switch (effect) { + case PolicyEffect::DENY: stream << "DENY"; break; + case PolicyEffect::PERMIT: stream << "PERMIT"; break; + case PolicyEffect::PROMPT_ONESHOT: stream << "PROMPT_ONESHOT"; break; + case PolicyEffect::PROMPT_SESSION: stream << "PROMPT_SESSION"; break; + case PolicyEffect::PROMPT_BLANKET: stream << "PROMPT_BLANKET"; break; + default: Assert(false && "Invalid PolicyEffect constant"); + } + return stream; +} + +#endif // _SRC_ACCESS_CONTROL_COMMON_POLICY_EFFECT_H_ diff --git a/modules/ace/include/dpl/ace/PolicyEnforcementPoint.h b/modules/ace/include/dpl/ace/PolicyEnforcementPoint.h new file mode 100644 index 0000000..027e4d9 --- /dev/null +++ b/modules/ace/include/dpl/ace/PolicyEnforcementPoint.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This class simply redirects the access requests to access control engine. + * The aim is to hide access control engine specific details from WRT modules. + * It also implements WRT_INTERFACE.h interfaces, so that ACE could access + * WRT specific and other information during the decision making. + * + * @file security_logic.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Ming Jin(ming79.jin@samsung.com) + * @brief Implementation file for security logic + */ +#ifndef POLICY_ENFORCEMENT_POINT_H +#define POLICY_ENFORCEMENT_POINT_H + +#include +#include +#include + +//#include +//#include +//#include + +//#include +#include +#include + +#include +#include + +// Forwards +class IWebRuntime; +class IResourceInformation; +class IOperationSystem; +class PolicyEvaluator; +class PolicyInformationPoint; +class Request; + +class PolicyEnforcementPoint : public AbstractPolicyEnforcementPoint +{ + public: + OptionalPolicyResult checkFromCache(Request &request); + PolicyResult check(Request &request); + OptionalPolicyResult check(Request &request, + bool fromCacheOnly); + + virtual ~PolicyEnforcementPoint(); + + class PEPException + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, AlreadyInitialized) + }; + + /** + * This function take ownership of objects pass in call. + * Object will be deleted after call Deinitialize function. + */ + void initialize(IWebRuntime *wrt, + IResourceInformation *resource, + IOperationSystem *operation); + void terminate(); + + void updatePolicy(const std::string &policy); + + PolicyEvaluator *getPdp() const { return this->m_pdp; } + PolicyInformationPoint *getPip() const { return this->m_pip; } + + protected: + PolicyEnforcementPoint(); + friend class SecurityLogic; + private: // private data + IWebRuntime *m_wrt; + IResourceInformation *m_res; + IOperationSystem *m_sys; + PolicyEvaluator *m_pdp; + PolicyInformationPoint *m_pip; +}; + +#endif // POLICY_ENFORCEMENT_POINT_H diff --git a/modules/ace/include/dpl/ace/PolicyEvaluator.h b/modules/ace/include/dpl/ace/PolicyEvaluator.h new file mode 100644 index 0000000..418faef --- /dev/null +++ b/modules/ace/include/dpl/ace/PolicyEvaluator.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : PolicyEvaluator.h +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#ifndef _POLICY_EVALUATOR_H +#define _POLICY_EVALUATOR_H + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class PolicyEvaluator : DPL::Noncopyable +{ + protected: + + /** + * Internal method used to initiate policy evaluation. Called after attribute set has been fetched + * by PIP. + * @param root root of the policies tree to be evaluated + */ + virtual Effect evaluatePolicies(const TreeNode * root); + + enum updateErrors + { + POLICY_PARSING_SUCCESS = 0, + POLICY_FILE_ERROR = 1, + PARSER_CREATION_ERROR, + POLICY_PARSING_ERROR + }; + private: + AttributeSet m_attributeSet; + + TreeNode * m_root; + Combiner * m_combiner; + AsyncVerdictResultListener * m_verdictListener; + PolicyInformationPoint * m_pip; + // Required by unittests. + std::string m_currentPolicyFile; + + /** + * Method used to extract attributes from subtree defined by PolicySet + * @param root original TreeStructure root node + * @param newRoot copy of TreeStructure containing only policies that matches current request + * + */ + void extractAttributesFromSubtree(const TreeNode *root); + + /** + * Method used to extract attributes from Tree Structure + * @return pointer to set of attributes needed to evaluate current request + * @return if extraction has been successful + * TODO return reducte tree structure + * TODO change comments + */ + bool extractAttributesFromRules(const TreeNode *); + + /** + * Extracts attributes from target of a given policy that are required to be fetched by PIP + */ + void extractTargetAttributes(const Policy *policy); + bool extractAttributes(); + + OptionalPolicyResult getPolicyForRequestInternal(bool fromCacheOnly); + PolicyResult effectToPolicyResult(Effect effect); + public: + PolicyEvaluator(PolicyInformationPoint * pip) : + m_root(NULL), + m_combiner(new CombinerImpl()), + m_verdictListener(NULL), + m_pip(pip) + { + } + + bool extractAttributesTest() + { + m_attributeSet.clear(); + if (!extractAttributes()) { + LogInfo("Warnign attribute set cannot be extracted. Returning Deny"); + return true; + } + + return extractAttributes(); + } + + AttributeSet * getAttributeSet() + { + return &m_attributeSet; + } + + virtual bool initPDP(); + virtual ~PolicyEvaluator(); + virtual PolicyResult getPolicyForRequest(const Request &request); + virtual OptionalPolicyResult getPolicyForRequestFromCache( + const Request &request); + virtual OptionalPolicyResult getPolicyForRequest(const Request &request, + bool fromCacheOnly); + bool fillAttributeWithPolicy(); + + virtual int updatePolicy(const char *); + // Required by unittests. + // It's used to check environment before each unittest. + std::string getCurrentPolicy(); +}; + +#endif //_POLICYEVALUATOR_H diff --git a/modules/ace/include/dpl/ace/PolicyEvaluatorFactory.h b/modules/ace/include/dpl/ace/PolicyEvaluatorFactory.h new file mode 100644 index 0000000..19ad88e --- /dev/null +++ b/modules/ace/include/dpl/ace/PolicyEvaluatorFactory.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file AbstractObjectFactory.h + * @author Piotr Fatyga (p.fatyga@samsung.com) + * @version 0.1 + * @brief + */ + +#ifndef _ABSTRACTOBJECTFACTORY_H +#define _ABSTRACTOBJECTFACTORY_H + +#include + +class AbstractPolicyEvaluatorFactory +{ + public: + virtual PolicyEvaluator * createPolicyEvaluator(PolicyInformationPoint *pip) + const = 0; +}; + +class PolicyEvaluatorFactory : public AbstractPolicyEvaluatorFactory +{ + public: + PolicyEvaluator * createPolicyEvaluator(PolicyInformationPoint *pip) const + { + return new PolicyEvaluator(pip); + } +}; + +#endif /* _ABSTRACTOBJECTFACTORY_H */ + diff --git a/modules/ace/include/dpl/ace/PolicyInformationPoint.h b/modules/ace/include/dpl/ace/PolicyInformationPoint.h new file mode 100644 index 0000000..2b3de29 --- /dev/null +++ b/modules/ace/include/dpl/ace/PolicyInformationPoint.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : PolicyInformationPoint.h +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#ifndef _POLICY_INFORMATION_POINT_H +#define _POLICY_INFORMATION_POINT_H + +#include + +#include +#include + +#include + +#include + +typedef int PipResponse; + +class PolicyInformationPoint +{ + private: + + /** queries for interfaces*/ + std::list resourceAttributesQuery; + std::list environmentAttributesQuery; + std::list subjectAttributesQuery; + std::list functionParamAttributesQuery; + + /** create queries */ + void createQueries(AttributeSet* attributes); + + IWebRuntime* wrtInterface; + IResourceInformation* resourceInformation; + IOperationSystem* operationSystem; + + public: + static const int ERROR_SHIFT_RESOURCE = 3; + static const int ERROR_SHIFT_OS = 6; + static const int ERROR_SHIFT_FP = 9; + + /** Mask used to identify PIP error */ + enum ResponseTypeMask + { + SUCCESS = 0, + /* WebRuntime Error */ + WRT_UNKNOWN_SUBJECT = 1 << 0, + WRT_UNKNOWN_ATTRIBUTE = 1 << 1, + WRT_INTERNAL_ERROR = 1 << 2, + /* Resource Information Storage Error */ + RIS_UNKNOWN_RESOURCE = 1 << 3, + RIS_UNKNOWN_ATTRIBUTE = 1 << 4, + RIS_INTERNAL_ERROR = 1 << 5, + /*Operating system */ + OS_UNKNOWN_ATTRIBUTE = 1 << 6, + OS_INTERNAL_ERROR = 1 << 7 + }; + + //TODO add checking values of attributes + /** gather attributes values from adequate interfaces */ + virtual PipResponse getAttributesValues(const Request* request, + AttributeSet* attributes); + virtual ~PolicyInformationPoint(); + PolicyInformationPoint(IWebRuntime *wrt, + IResourceInformation *resource, + IOperationSystem *system) : wrtInterface(wrt), + resourceInformation(resource), + operationSystem(system) + { + } + virtual void update(IWebRuntime *wrt, + IResourceInformation *resource, + IOperationSystem *system) + { + wrtInterface = wrt; + resourceInformation = resource; + operationSystem = system; + } + IWebRuntime * getWebRuntime() + { + return wrtInterface; + } +}; + +#endif //_POLICY_INFORMATION_POINT_H diff --git a/modules/ace/include/dpl/ace/PolicyResult.h b/modules/ace/include/dpl/ace/PolicyResult.h new file mode 100644 index 0000000..8ccb41e --- /dev/null +++ b/modules/ace/include/dpl/ace/PolicyResult.h @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _SRC_ACCESS_CONTROL_COMMON_POLICY_RESULT_H_ +#define _SRC_ACCESS_CONTROL_COMMON_POLICY_RESULT_H_ + +#include +#include +#include + +#include + +typedef DPL::Optional OptionalPolicyEffect; + +class PolicyDecision +{ +public: + enum Value { NOT_APPLICABLE = -1 }; + + PolicyDecision(PolicyEffect effect) + : m_isPolicyEffect(true) + , m_effect(effect) + {} + + PolicyDecision(const PolicyDecision &decision) + : m_isPolicyEffect(decision.m_isPolicyEffect) + , m_effect(decision.m_effect) + {} + + PolicyDecision(Value) + : m_isPolicyEffect(false) + {} + + bool operator==(const PolicyDecision &decision) const { + return (m_isPolicyEffect + && decision.m_isPolicyEffect + && m_effect == decision.m_effect) + || (!m_isPolicyEffect && !decision.m_isPolicyEffect); + } + + bool operator==(Value) const { + return !m_isPolicyEffect; + } + + bool operator!=(const PolicyDecision &decision) const { + return !(*this == decision); + } + + bool operator!=(Value value) const { + return !(*this == value); + } + + OptionalPolicyEffect getEffect() const + { + if (!m_isPolicyEffect) { + return OptionalPolicyEffect(); + } + return OptionalPolicyEffect(m_effect); + } + + std::ostream & toStream(std::ostream& stream) { + if (m_isPolicyEffect) + stream << m_effect; + else + stream << "NOT-APPLICABLE"; + return stream; + } + +private: + bool m_isPolicyEffect; + PolicyEffect m_effect; +}; + +inline static bool operator==(PolicyEffect e, const PolicyDecision &d) { + return d.operator==(e); +} + +inline static bool operator!=(PolicyEffect e, const PolicyDecision &d) { + return !(e == d); +} + +inline static std::ostream & operator<<(std::ostream& stream, + PolicyDecision decision) +{ + return decision.toStream(stream); +} + +class PolicyResult { +public: + enum Value { UNDETERMINED = -2 }; + + // This constructor is required by dpl controller and by dpl optional + PolicyResult() + : m_isDecision(false) + , m_decision(PolicyDecision::Value::NOT_APPLICABLE) // don't care + {} + + PolicyResult(PolicyEffect effect) + : m_isDecision(true) + , m_decision(effect) + {} + + PolicyResult(const PolicyDecision &decision) + : m_isDecision(true) + , m_decision(decision) + {} + + PolicyResult(const PolicyResult &result) + : m_isDecision(result.m_isDecision) + , m_decision(result.m_decision) + {} + + PolicyResult(PolicyDecision::Value value) + : m_isDecision(true) + , m_decision(value) + {} + + PolicyResult(Value) + : m_isDecision(false) + , m_decision(PolicyDecision::Value::NOT_APPLICABLE) // don't care + {} + + bool operator==(const PolicyResult &result) const { + return (m_isDecision + && result.m_isDecision + && m_decision == result.m_decision) + || (!m_isDecision && !result.m_isDecision); + } + + bool operator==(Value) const { + return !m_isDecision; + } + + bool operator!=(const PolicyResult &result) const { + return !(*this == result); + } + + bool operator!=(Value value) const { + return !(*this == value); + } + + OptionalPolicyEffect getEffect() const + { + if (!m_isDecision) { + return OptionalPolicyEffect(); + } + return m_decision.getEffect(); + } + + static int serialize(const PolicyResult &policyResult) + { + if (!policyResult.m_isDecision) { + return BD_UNDETERMINED; + } else if (policyResult.m_decision == + PolicyDecision::Value::NOT_APPLICABLE) + { + return BD_NOT_APPLICABLE; + } else if (policyResult.m_decision == PolicyEffect::PROMPT_BLANKET) { + return BD_PROMPT_BLANKET; + } else if (policyResult.m_decision == PolicyEffect::PROMPT_SESSION) { + return BD_PROMPT_SESSION; + } else if (policyResult.m_decision == PolicyEffect::PROMPT_ONESHOT) { + return BD_PROMPT_ONESHOT; + } else if (policyResult.m_decision == PolicyEffect::PERMIT) { + return BD_PERMIT; + } else if (policyResult.m_decision == PolicyEffect::DENY) { + return BD_DENY; + } + Assert(false && "Unknown value of policyResult."); + } + + static PolicyResult deserialize(int dec){ + switch (dec) { + case BD_DENY: + return PolicyEffect::DENY; + case BD_PERMIT: + return PolicyEffect::PERMIT; + case BD_PROMPT_ONESHOT: + return PolicyEffect::PROMPT_ONESHOT; + case BD_PROMPT_SESSION: + return PolicyEffect::PROMPT_SESSION; + case BD_PROMPT_BLANKET: + return PolicyEffect::PROMPT_BLANKET; + case BD_NOT_APPLICABLE: + return PolicyDecision::Value::NOT_APPLICABLE; + case BD_UNDETERMINED: + return Value::UNDETERMINED; + } + Assert(false && "Broken database"); + } + + std::ostream & toStream(std::ostream& stream) { + if (m_isDecision) + stream << m_decision; + else + stream << "UNDETERMINED"; + return stream; + } + +private: + static const int BD_UNDETERMINED = 6; + static const int BD_NOT_APPLICABLE = 5; + static const int BD_PROMPT_BLANKET = 4; + static const int BD_PROMPT_SESSION = 3; + static const int BD_PROMPT_ONESHOT = 2; + static const int BD_PERMIT = 1; + static const int BD_DENY = 0; + + bool m_isDecision; + PolicyDecision m_decision; +}; + +inline static bool operator==(const PolicyDecision &d, const PolicyResult &r) { + return r == d; +} + +inline static bool operator!=(const PolicyDecision &d, const PolicyResult &r) { + return !(d == r); +} + +inline static bool operator==(const PolicyEffect &e, const PolicyResult &r) { + return e == r; +} + +inline static bool operator!=(const PolicyEffect &e, const PolicyResult &r) { + return !(e == r); +} + +inline static std::ostream & operator<<(std::ostream& stream, + PolicyResult result) +{ + return result.toStream(stream); +} + +typedef DPL::Optional OptionalPolicyResult; + +#endif // _SRC_ACCESS_CONTROL_COMMON_POLICY_RESULT_H_ diff --git a/modules/ace/include/dpl/ace/PolicySet.h b/modules/ace/include/dpl/ace/PolicySet.h new file mode 100644 index 0000000..6d6307d --- /dev/null +++ b/modules/ace/include/dpl/ace/PolicySet.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : PolicySet.h +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#if !defined(_POLICYSET_H) +#define _POLICYSET_H + +#include "Policy.h" +#include + +class PolicySet : public Policy +{ + public: + + //TODO Clean this class + //PolicySet(CombineAlgorithm algorithm, std::list * targetAttr,const std::string & subjectId) + // : Policy(algorithm,targetAttr,subjectId) + // {} + PolicySet() + { + } + ~PolicySet() + { + } + + PolicySet(std::istream& is) : Policy(is) + { + } +}; + +#endif //_POLICYSET_H diff --git a/modules/ace/include/dpl/ace/Preference.h b/modules/ace/include/dpl/ace/Preference.h new file mode 100644 index 0000000..97b34fc --- /dev/null +++ b/modules/ace/include/dpl/ace/Preference.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : Preference.h +// @ Date : 2009-05-2 +// @ Author : Samsung +// +// + +#ifndef _Preference_H_ +#define _Preference_H_ + +#include +#include + +#include + +typedef AceDB::PreferenceTypes Preference; +typedef AceDB::PreferenceTypesMap PreferenceMap; + +#endif //_Preference_H + diff --git a/modules/ace/include/dpl/ace/PromptDecision.h b/modules/ace/include/dpl/ace/PromptDecision.h new file mode 100644 index 0000000..bfe425b --- /dev/null +++ b/modules/ace/include/dpl/ace/PromptDecision.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _SRC_ACCESS_CONTROL_COMMON_PROMPT_DECISION_H_ +#define _SRC_ACCESS_CONTROL_COMMON_PROMPT_DECISION_H_ + +#include +#include + +enum class PromptDecision { + ALLOW_ALWAYS, + DENY_ALWAYS, + ALLOW_THIS_TIME, + DENY_THIS_TIME, + ALLOW_FOR_SESSION, + DENY_FOR_SESSION +}; + +typedef DPL::Optional OptionalPromptDecision; + +struct CachedPromptDecision { + PromptDecision decision; + DPL::OptionalString session; +}; + +typedef DPL::Optional OptionalCachedPromptDecision; + +#endif // _SRC_ACCESS_CONTROL_COMMON_PROMPT_DECISION_H_ diff --git a/modules/ace/include/dpl/ace/Request.h b/modules/ace/include/dpl/ace/Request.h new file mode 100644 index 0000000..c29def4 --- /dev/null +++ b/modules/ace/include/dpl/ace/Request.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : Request.h +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#ifndef _REQUEST_H_ +#define _REQUEST_H_ + +#include +#include +#include + +#include + +#include + +class Request : public AceDB::IRequest +{ + public: + typedef std::string DeviceCapability; + typedef std::set DeviceCapabilitySet; + + Request(WidgetHandle widgetHandle, + WidgetExecutionPhase phase, + IFunctionParam *functionParam = 0) + : m_widgetHandle(widgetHandle) + , m_phase(phase) + , m_functionParam(functionParam) + {} + + WidgetHandle getWidgetHandle() const + { + return m_widgetHandle; + } + + WidgetExecutionPhase getExecutionPhase() const + { + return m_phase; + } + + IFunctionParam *getFunctionParam() const + { + return m_functionParam; + } + + void addDeviceCapability(const std::string& device) + { + m_devcapSet.insert(device); + } + + DeviceCapabilitySet getDeviceCapabilitySet() const + { + return m_devcapSet; + } + + private: + WidgetHandle m_widgetHandle; + WidgetExecutionPhase m_phase; + //! \brief list of function param (only for intercept) + IFunctionParam *m_functionParam; + //! \brief Set of defice capabilities + DeviceCapabilitySet m_devcapSet; +}; + +typedef std::vector Requests; + +#endif //_REQUEST_H_ diff --git a/modules/ace/include/dpl/ace/Rule.h b/modules/ace/include/dpl/ace/Rule.h new file mode 100644 index 0000000..7877ced --- /dev/null +++ b/modules/ace/include/dpl/ace/Rule.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : Rule.h +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#if !defined(_RULE_H) +#define _RULE_H + +#include "Attribute.h" +#include "Effect.h" +#include "Condition.h" +#include + +class Rule : public AbstractTreeElement +{ + public: + + Effect evaluateRule(const AttributeSet * attrSet) const; + + // KW Rule(Effect effect, Condition condition) : effect(effect), condition(condition) { + // KW //TODO we should set it to deny or smth, not inapplicable + // KW } + + Rule() + { + //TODO we should set it to deny or smth, not inapplicable + this->effect = Inapplicable; + } + + // KW Effect getEffect() const { + // KW return this->effect; + // KW }; + + void setEffect(Effect effect) + { + //We should not allow to set "Inapplicable" effect. + //Rules cannot have effect that is inapplicable, evaluation of the rules may however + //render the effect inapplicable. + Assert(effect != Inapplicable); + this->effect = effect; + } + void setCondition(Condition condition) + { + this->condition = condition; + } + void getAttributes(AttributeSet * attrSet) + { + condition.getAttributes(attrSet); + } + + //deserialize + Rule(std::istream& is); + void serialize(std::ostream& os); + + //DEBUG methods + std::string printEffect(const Effect effect) const; + void printData(); + + private: + + Effect effect; + Condition condition; +}; + +#endif //_RULE_H diff --git a/modules/ace/include/dpl/ace/Serializer.h b/modules/ace/include/dpl/ace/Serializer.h new file mode 100644 index 0000000..1e1c4cb --- /dev/null +++ b/modules/ace/include/dpl/ace/Serializer.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if !defined(_SERIALIZER_H) +#define _SERIALIZER_H + +#include +#include +#include + +#include "TreeNode.h" +#include "Attribute.h" +#include "Subject.h" +#include "Policy.h" +#include "Condition.h" + +class Serializer +{ + public: + static Serializer* getInstance(); + + /**** Interaface ****************/ + + /** + * Serialized tree given by + * @param root + * */ + // KW void serializeTree(TreeNode* root); + + /** + * deserialize tree + * @return pointer to deserialized tree + * or NULL if error occur + * */ + // KW TreeNode* deserializeTree(); + + /* ------------------------------*/ + + void serializeInt(std::ostream& os, + const int value); + int deserializeInt(std::istream& is); + + void serializeString(std::ostream& os, + const std::string& text) const; + std::string deserializeString(std::istream& is); + + void serializeListAttributes(std::ostream& os, + const std::list& inputList); + std::list deserializeListAttributes(std::istream& is); + + void serializeType(std::ostream& os, + const Attribute::Type& typeId) const; + Attribute::Type deserializeType(std::istream& is); + + void serializeListStrings(std::ostream& os, + const std::list& inputList) const; + + std::list deserializeListStrings(std::istream&is); + + void serializeMatch(std::ostream& os, + const Attribute::Match& match) const; + Attribute::Match deserializeMatch(std::istream& is); + //TODO add smartptr + bool serializeModifier(std::ostream& os, + const Attribute::Modifier& modifier) const; + Attribute::Modifier deserializeModifier(std::istream& is); + + void serializeListTreeNode(std::ostream& os, + std::list& inputList); + std::list deserializeListTreeNode(std::istream&is, + TreeNode* parent); + + void serializeTypeAbstract(std::ostream& os, + const TreeNode::TypeID& typeId); + TreeNode::TypeID deserializeTypeAbstract(std::istream& is); + + void serializeAbstractElement(std::ostream& os, + AbstractTreeElement* element); + AbstractTreeElement* deserializeAbstractElement(std::istream& is, + TreeNode::TypeID&); + + void serializeListSubjects(std::ostream& os, + std::list& inputList); + std::list* deserializeListSubjects(std::istream& is); + + void serializeCombineAlgorithm(std::ostream& os, + const Policy::CombineAlgorithm & combAlg); + Policy::CombineAlgorithm deserializeCombineAlgorithm(std::istream& is); + + void serializeCombineType(std::ostream& os, + const Condition::CombineType& combType); + Condition::CombineType deserializeCombineType(std::istream& is); + + void serializeListConditions(std::ostream& os, + std::list& inputList); + std::list deserializeListConditions(std::istream& is, + Condition* parent); + + void serializeEffect(std::ostream& os, + Effect effect); + Effect deserializeEffect(std::istream& is); + + void serializeCondition(std::ostream& os, + Condition& cond); + Condition& deserializeCondition(std::istream& is); + + private: + Serializer() + { + } + static Serializer* pInstance; + + std::filebuf fileBuffer; +}; + +#endif + diff --git a/modules/ace/include/dpl/ace/SettingsLogic.h b/modules/ace/include/dpl/ace/SettingsLogic.h new file mode 100644 index 0000000..62096db --- /dev/null +++ b/modules/ace/include/dpl/ace/SettingsLogic.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file SettingsLogic.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 0.1 + * @brief Header file for class getting/setting user/global ACE settings + */ + +#ifndef WRT_SRC_ACCESS_CONTROL_LOGIC_SETTINGS_LOGIC_H_ +#define WRT_SRC_ACCESS_CONTROL_LOGIC_SETTINGS_LOGIC_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class SettingsLogic +{ + public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, DatabaseError) + }; + + // global settings + static AceDB::PreferenceTypes findGlobalUserSettings( + const std::string &resource, + WidgetHandle handler); + + static AceDB::PreferenceTypes findGlobalUserSettings( + const Request &request); + + // resource settings + static AceDB::PreferenceTypes getDevCapSetting( + const std::string &request); + static void getDevCapSettings(AceDB::PreferenceTypesMap *preferences); + static void setDevCapSetting(const std::string &resource, + AceDB::PreferenceTypes preference); + static void setAllDevCapSettings( + const std::list > &resourcesList); + static void removeDevCapSetting(const std::string &resource); + static void updateDevCapSetting(const std::string &resource, + AceDB::PreferenceTypes p); + + // user settings + static AceDB::PreferenceTypes getWidgetDevCapSetting( + const std::string &resource, + WidgetHandle handler); + static void getWidgetDevCapSettings(PermissionList *permissions); + static void setWidgetDevCapSetting(const std::string &resource, + WidgetHandle handler, + AceDB::PreferenceTypes preference); + static void setWidgetDevCapSettings(const PermissionList &tripleList); + static void removeWidgetDevCapSetting(const std::string &resource, + WidgetHandle handler); + + private: + SettingsLogic() + { + } + +}; + +#endif /* WRT_SRC_ACCESS_CONTROL_LOGIC_SETTINGS_LOGIC_H_ */ diff --git a/modules/ace/include/dpl/ace/Subject.h b/modules/ace/include/dpl/ace/Subject.h new file mode 100644 index 0000000..5821429 --- /dev/null +++ b/modules/ace/include/dpl/ace/Subject.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// File: Subject.h +// Author: notroot +// +// Created on June 2, 2009, 8:47 AM +// + +#ifndef _SUBJECT_H +#define _SUBJECT_H + +#include +#include +#include +#include +#include + +#include "Attribute.h" + +class Subject : DPL::Noncopyable +{ + std::string subjectId; + std::list targetAttributes; + + public: + Subject() + {} + + //deserialize + Subject(std::istream&); + + virtual bool serialize (std::ostream& os); + + const std::list& getTargetAttributes() const; + + void setSubjectId(const std::string & subjectId) + { + this->subjectId = subjectId; + } + + //TODO maybe we should remove that becuase this causes a memory leak right now!! [CR] maybe thats true, maybe whe can remove this fun + // KW void setTargetAttributes(std::list * targetAttributes){ this->targetAttributes = targetAttributes; } + + const std::string & getSubjectId() const + { + return this->subjectId; + } + + void addNewAttribute(Attribute & attr) + { + this->targetAttributes.push_back(attr); + } + + //TODO in 1.0 change to true/false/undetermined + bool matchSubject(const AttributeSet *attrSet, + bool &isUndetermined) const; + + ~Subject() + {} +}; + +#endif /* _SUBJECT_H */ + diff --git a/modules/ace/include/dpl/ace/TestTimer.h b/modules/ace/include/dpl/ace/TestTimer.h new file mode 100644 index 0000000..1f07e61 --- /dev/null +++ b/modules/ace/include/dpl/ace/TestTimer.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _TEST_TIMER_H +#define _TEST_TIMER_H + +#include + +class TestTimer +{ + time_t startt, endt; + + public: + void start() + { + time(&startt); + } + void stop() + { + time(&endt); + } + double getTime() + { + return difftime(endt, startt); + } +}; + +#endif //_TEST_TIMER_H + diff --git a/modules/ace/include/dpl/ace/TreeNode.h b/modules/ace/include/dpl/ace/TreeNode.h new file mode 100644 index 0000000..05bb0e6 --- /dev/null +++ b/modules/ace/include/dpl/ace/TreeNode.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : TreeNode.h +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#ifndef _TREE_NODE_H +#define _TREE_NODE_H + +#include +#include + +#include + +class TreeNode; + +typedef std::list ChildrenSet; +typedef std::list::iterator ChildrenIterator; +typedef std::list::const_iterator ChildrenConstIterator; + +class TreeNode +{ + public: + //TODO nazwac pozadnie TYPY - moze jakas konwencja ... ??!! + enum TypeID { Policy =0, PolicySet=1, Rule=2}; + + virtual bool serialize(std::ostream& os); + + TreeNode(std::istream& is, + TreeNode* parent); + + const ChildrenSet & getChildrenSet() const + { + return children; + } + + TreeNode * getParent() const + { + return this->parent; + } + + void setParent(TreeNode *parent) + { + this->parent = parent; + } + + TypeID getTypeID() const + { + return this->typeID; + } + + void addChild(TreeNode *child) + { + child->setParent(this); + children.push_back(child); + } + + /** + * Clone the node + */ + // KW TreeNode * clone() { return new TreeNode(NULL,this->getTypeID(),this->getElement()); } + + TreeNode(TreeNode * parent, + TypeID type, + AbstractTreeElement * element) : + parent(parent), + typeID(type), + element(element) + { + } + + AbstractTreeElement * getElement() const + { + return element; + } + + private: + virtual ~TreeNode(); + + public: + /* + * It is common that we create a copy of tree structure created out of xml file. However we don't want to + * copy abstract elements ( Policies and Rules ) because we need them only for reading. We want to modify the + * tree structure though. Therefore we copy TreeNode. When the copy of the original tree is being destroyed method + * releaseTheSubtree should be called on "root". It automatically traverse the tree and call TreeNode destructors for + * each TreeNode in the tree. It doesn't remove the abstract elements in the tree ( there is always at most one abstract + * element instance, when tree is copied it is a shallow copy. + * When we want to completely get rid of the the tree and abstract elements we have to call releaseResources on tree root. + * We may want to do this for instance when we want to serialize the tree to disc. releaseResource method traverses the tree + * and releses the resources, as well as the TreeNode so NO releaseTheSubtree is required any more + */ + void releaseResources(); + + /** + * Used to delete the copies of tree structure. The original tree structure should be removed with releaseResources method. + * ReleaseTheSubtree method doesn't delete the abstract elements, only TreeNodes. It traverses the whole tree, so it should be + * called on behalf of root of the tree + */ + // KW void releaseTheSubtree(); + + friend std::ostream & operator<<(std::ostream & out, + const TreeNode * node); + // KW void printSubtree(); + + private: + // KW TreeNode(const TreeNode& pattern){ (void)pattern; } + + std::list children; + TreeNode * parent; + //TODO standarize ID case + TypeID typeID; + AbstractTreeElement * element; + static int level; +}; + +#endif //_TREE_NODE_H diff --git a/modules/ace/include/dpl/ace/UserDecision.h b/modules/ace/include/dpl/ace/UserDecision.h new file mode 100644 index 0000000..4bc405d --- /dev/null +++ b/modules/ace/include/dpl/ace/UserDecision.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : UserDecision.h +// @ Date : 2009-05-22 +// @ Author : Samsung +// +// + +#ifndef _USERDECISION_H +#define _USERDECISION_H + +#include +#include + +typedef AceDB::ValidityTypes Validity; + +const char * toString(Validity validity); + +#endif //_USERDECISION_H + diff --git a/modules/ace/include/dpl/ace/Verdict.h b/modules/ace/include/dpl/ace/Verdict.h new file mode 100644 index 0000000..805d02a --- /dev/null +++ b/modules/ace/include/dpl/ace/Verdict.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : Verdict.h +// @ Date : 2009-05-2 +// @ Author : Samsung +// +// + +#ifndef _VERDICT_H +#define _VERDICT_H + +#include +#include +#include + +typedef AceDB::VerdictTypes Verdict; +//typedef AceDB::TimedVerdict TimedVerdict; + +const char * toString(Verdict verditct); + +#endif //_VERDICT_H + diff --git a/modules/ace/include/dpl/ace/WRT_INTERFACE.h b/modules/ace/include/dpl/ace/WRT_INTERFACE.h new file mode 100644 index 0000000..2f5b80b --- /dev/null +++ b/modules/ace/include/dpl/ace/WRT_INTERFACE.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _WRT_INERFACE_4_ACE_EXAMPLE_H_ +#define _WRT_INERFACE_4_ACE_EXAMPLE_H_ + +#include +#include +#include + +#include + +class Request; + +enum WidgetExecutionPhase +{ + WidgetExecutionPhase_Unknown = 0, + WidgetExecutionPhase_WidgetInstall = 1 << 0, + WidgetExecutionPhase_WidgetInstantiate = 1 << 1, + WidgetExecutionPhase_WebkitBind = 1 << 2, + WidgetExecutionPhase_Invoke = 1 << 3 +}; + +struct RequestContext +{ + const WidgetHandle Handle; + WidgetExecutionPhase Phase; + + RequestContext(WidgetHandle handle, + WidgetExecutionPhase phase) : + Handle(handle), + Phase(phase) + { + } +}; + +// Pair of pointer to attribute name and pointer to list of value for +// this attribute name +typedef std::pair< const std::string* const, std::list* > +ATTRIBUTE; + +/* + * Each function should return 0 as success and positive value as error + * + * Possible return value: + * 0 - succes + * 1 - subjectId/resourceId name unknown + * 2 - unknown attribute name + * 4 - interface error + **/ + +/************** Web Runtime ********************/ + +class IWebRuntime +{ + public: + + /** + * gather and set attributes values for specified subjectId + * and attribute name + * @param subjectId is a name of subject (widget or internet site URI ) + * @param attributes is a list of pairs( + * first: pointer to attribute name + * second: list of values for attribute (std::string) - + * its a list of string (BONDI requirement), but usually there will + * be only one string + * */ + virtual int getAttributesValues(const Request &request, + std::list *attributes) = 0; + + /*return current sessionId */ + virtual std::string getSessionId(const Request &request) = 0; + + virtual ~IWebRuntime() + { + } +}; + +/************** Resource Information ********************/ +class IResourceInformation +{ + public: + /** + * gather and set attributes values for specified resourceId + * and attribute name + * @param resourceId is a name of subject (widget or internet site URI ) + * @param attributes is a list of pairs( + * first: pointer to attribute name + * second: list of values for attribute (std::string) - + * its a list of string (BONDI requirement), but usually there will + * be only one string + * */ + virtual int getAttributesValues(const Request &request, + std::list *attributes) = 0; + + virtual ~IResourceInformation() + { + } +}; + +/************** Operation System ********************/ +class IOperationSystem +{ + public: + + /** + * gather and set attributes values for specified attribute name + * @param attributes is a list of pairs( + * first: pointer to attribute name + * second: list of values for attribute (std::string) - + * its a list of string (BONDI requirement), but usually + * there will be only one string + * */ + virtual int getAttributesValues(const Request &request, + std::list *attributes) = 0; + + virtual ~IOperationSystem() + { + } +}; + +class IFunctionParam +{ + public: + virtual int getAttributesValues(const Request &request, + std::list *attributes) = 0; + virtual ~IFunctionParam() + { + } +}; + +#endif //_WRT_INERFACE_4_ACE_EXAMPLE_H_ diff --git a/modules/ace/include/dpl/ace/WidgetUsageModel.h b/modules/ace/include/dpl/ace/WidgetUsageModel.h new file mode 100644 index 0000000..09d15f8 --- /dev/null +++ b/modules/ace/include/dpl/ace/WidgetUsageModel.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// @ Project : Access Control Engine +// @ File Name : UserDecision.h +// @ Date : 2009-05-22 +// @ Author : Samsung +// +// + +#ifndef _WIDGET_USAGE_H +#define _WIDGET_USAGE_H + +#include + +#include "Request.h" +#include "AsyncVerdictResultListener.h" + +enum UsageValidity +{ + USAGE_UNKNOWN, + USAGE_ONCE, + USAGE_SESSION, + USAGE_ALWAYS +}; + +enum UsageVerdict +{ + USAGE_VERDICT_PERMIT, + USAGE_VERDICT_DENY, + USAGE_VERDICT_INAPPLICABLE, + USAGE_VERDICT_UNDETERMINED, + USAGE_VERDICT_UNKNOWN, + USAGE_VERDICT_ERROR +}; +//Forward declaration +class PolicyEvaluator; + +class PolicyEvaluatorData +{ + private: + Request m_request; + UsageValidity m_validity; + UsageVerdict m_verdict; + AsyncVerdictResultListener *m_listener; + public: + + PolicyEvaluatorData(const Request& request, + AsyncVerdictResultListener *listener) : + m_request(request), + m_validity(USAGE_UNKNOWN), + m_verdict(USAGE_VERDICT_ERROR), + m_listener(listener) + { + } + + // KW UsageValidity getValidity() const { + // KW return m_validity; + // KW } + // KW + // KW UsageVerdict getVerdict() const { + // KW return m_verdict; + // KW } + // KW + // KW void setValidity(UsageValidity validity) { + // KW this->m_validity = validity; + // KW } + // KW + // KW void setVerdict(UsageVerdict verdict) { + // KW this->m_verdict = verdict; + // KW } + + const Request& getRequest() const + { + return m_request; + } + + AsyncVerdictResultListener* getListener() const + { + return m_listener; + } +}; + +#endif //_USERDECISION_H diff --git a/modules/ace/include/dpl/ace/acf_consts.h b/modules/ace/include/dpl/ace/acf_consts.h new file mode 100644 index 0000000..93ecfae --- /dev/null +++ b/modules/ace/include/dpl/ace/acf_consts.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * This file contain consts for Signing Template and Policy Manager + * This values will be used to specified and identified algorithms in xml policy documents. + * Its consistent with BONDI 1.0 released requirements + * + * NOTE: This values should be verified when ACF will be updated to the latest version of BONDI requirements + * This values comes from widget digital signature 1.0 - required version of this doc is very important + * + **/ + +#ifndef ACF_CONSTS_TYPES_H +#define ACF_CONSTS_TYPES_H + +//Digest Algorithms +extern const char* DIGEST_ALG_SHA256; + +//Canonicalization Algorithms +extern const char* CANONICAL_ALG_C14N; + +//Signature Algorithms +extern const char* SIGNATURE_ALG_RSA_with_SHA256; +extern const char* SIGNATURE_ALG_DSA_with_SHA1; +extern const char* SIGNATURE_ALG_ECDSA_with_SHA256; + +#endif + diff --git a/modules/ace/include/dpl/ace/parser.h b/modules/ace/include/dpl/ace/parser.h new file mode 100644 index 0000000..4fcd4d4 --- /dev/null +++ b/modules/ace/include/dpl/ace/parser.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// +// +// +// @ Project : Access Control Engine +// @ File Name : parser.h +// @ Date : 2009-05-06 +// @ Author : Samsung +// +// + +#ifndef _PARSER_H_ +#define _PARSER_H_ + +//#include "/usr/include/libxml2/libxml/parser.h" +#include +#include +#include +#include +#include + +#include "Policy.h" +#include "PolicySet.h" +#include "Request.h" +#include "Rule.h" +#include "Attribute.h" +#include "TreeNode.h" +#include "Subject.h" +#include "Condition.h" + +#define whitespaces " \n\t\r" + +enum CanonicalizationAlgorithm +{ + C14N, + C14NEXCLUSIVE +}; + +class Parser +{ + private: + + xmlTextReaderPtr reader; + + TreeNode * root; + TreeNode * currentRoot; + Subject * currentSubject; + Condition * currentCondition; + Attribute * currentAttribute; + std::string * currentText; + + bool processingSignature; + bool canonicalizeOnce; + + void processNode(xmlTextReaderPtr reader); + + //Node Handlers + void endNodeHandler(xmlTextReaderPtr reader); + void textNodeHandler(xmlTextReaderPtr reader); + void startNodeHandler(xmlTextReaderPtr reader); + + //Node names handlers + void handleAttr(xmlTextReaderPtr reader); + void handleRule(xmlTextReaderPtr reader); + void handleSubject(); + void handleCondition(xmlTextReaderPtr reader); + void handleSubjectMatch(xmlTextReaderPtr reader); + void handleMatch(xmlTextReaderPtr reader, + Attribute::Type); + void handlePolicy(xmlTextReaderPtr reader, + TreeNode::TypeID type); + + //helpers + Policy::CombineAlgorithm convertToCombineAlgorithm(xmlChar*); + Effect convertToEffect(xmlChar *effect); + Attribute::Match convertToMatchFunction(xmlChar * func); + void consumeCurrentText(); + void consumeCurrentAttribute(); + void consumeSubjectMatch(xmlChar * value = NULL); + void consumeCurrentSubject(); + void consumeCurrentCondition(); + void trim(std::string *); + // KW void canonicalize(const char *, const char *, CanonicalizationAlgorithm canonicalizationAlgorithm); + // KW int extractNodeToFile(xmlTextReaderPtr reader, const char * filename); + + static const char *TOKEN_PARAM; + public: + Parser(); + ~Parser(); + TreeNode * parse(const std::string& filename, const std::string& schema); +}; + +#endif //_PARSER_H diff --git a/modules/ace/orm/ace_db b/modules/ace/orm/ace_db new file mode 100644 index 0000000..0dc1c27 --- /dev/null +++ b/modules/ace/orm/ace_db @@ -0,0 +1,60 @@ +SQL( + PRAGMA foreign_keys = ON; + BEGIN TRANSACTION; +) + +CREATE_TABLE(AcePolicyResult) + COLUMN_NOT_NULL(decision, INTEGER, check(decision between 0 and 6)) + COLUMN_NOT_NULL(hash, TEXT,) + TABLE_CONSTRAINTS( + PRIMARY KEY(hash) + ) +CREATE_TABLE_END() + +CREATE_TABLE(AcePromptDecision) + COLUMN_NOT_NULL(user_param, TEXT,) + COLUMN_NOT_NULL(decision, INTEGER, check(decision between 0 and 5)) + COLUMN(session, TEXT,) + COLUMN_NOT_NULL(hash, TEXT,) + TABLE_CONSTRAINTS( + PRIMARY KEY(hash,user_param) + ) +CREATE_TABLE_END() + +CREATE_TABLE(AceAttribute) + COLUMN_NOT_NULL(attr_id, INTEGER, primary key autoincrement) + COLUMN_NOT_NULL(name, TEXT,) + COLUMN_NOT_NULL(type, INTEGER, check(type between 0 and 3)) + + TABLE_CONSTRAINTS(unique(name,type)) +CREATE_TABLE_END() + +CREATE_TABLE(AceSubject) + COLUMN_NOT_NULL(subject_id, INTEGER, primary key autoincrement) + COLUMN_NOT_NULL(id_uri, TEXT, unique) +CREATE_TABLE_END() + +CREATE_TABLE(AceDevCap) + COLUMN_NOT_NULL(resource_id, INTEGER, primary key autoincrement) + COLUMN_NOT_NULL(id_uri, TEXT, unique) + COLUMN_NOT_NULL(general_setting,INTEGER, check(general_setting between -1 and 4)) +CREATE_TABLE_END() + +CREATE_TABLE(AceWidgetDevCapSetting) + COLUMN_NOT_NULL(app_id, INTEGER, not null) + COLUMN_NOT_NULL(resource_id, INTEGER, references AceDevCap(resource_id)) + COLUMN_NOT_NULL(access_value, INTEGER, check(access_value between -1 and 4)) + + TABLE_CONSTRAINTS(unique(app_id,resource_id)) +CREATE_TABLE_END() + +CREATE_TABLE(AceStaticDevCapPermission) + COLUMN_NOT_NULL(app_id, INTEGER, not null) + COLUMN_NOT_NULL(dev_cap, TEXT,) + + TABLE_CONSTRAINTS(unique(app_id,dev_cap)) +CREATE_TABLE_END() + +SQL( + COMMIT; +) diff --git a/modules/ace/orm/ace_db_definitions b/modules/ace/orm/ace_db_definitions new file mode 100644 index 0000000..46836e9 --- /dev/null +++ b/modules/ace/orm/ace_db_definitions @@ -0,0 +1,6 @@ +DATABASE_START(ace) + +#include "ace_db" +#include "version_db" + +DATABASE_END() diff --git a/modules/ace/orm/ace_db_sql_generator.h b/modules/ace/orm/ace_db_sql_generator.h new file mode 100644 index 0000000..5af05ac --- /dev/null +++ b/modules/ace/orm/ace_db_sql_generator.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file wrt_db_sql_generator.h + * @author Bartosz Janiak (b.janiak@samsung.com) + * @version 1.0 + * @brief Macro definitions for generating the SQL input file from database definition. + */ + +//Do not include this file directly! It is used only for SQL code generation. + +#include + +#include "ace_db_definitions" diff --git a/modules/ace/orm/gen_db_md5.sh b/modules/ace/orm/gen_db_md5.sh new file mode 100755 index 0000000..38587b7 --- /dev/null +++ b/modules/ace/orm/gen_db_md5.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +CHECKSUM=`cat ${2} ${3} 2>/dev/null | md5sum 2>/dev/null | cut -d\ -f1 2>/dev/null` +echo "#define DB_CHECKSUM DB_VERSION_${CHECKSUM}" > ${1} +echo "#define DB_CHECKSUM_STR \"DB_VERSION_${CHECKSUM}\"" >> ${1} + diff --git a/modules/ace/orm/orm_generator_ace.h b/modules/ace/orm/orm_generator_ace.h new file mode 100644 index 0000000..640dd35 --- /dev/null +++ b/modules/ace/orm/orm_generator_ace.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ORM_GENERATOR_ACE_H +#define ORM_GENERATOR_ACE_H + +#define ORM_GENERATOR_DATABASE_NAME ace_db_definitions +#include +#undef ORM_GENERATOR_DATABASE_NAME + +#endif diff --git a/modules/ace/orm/version_db b/modules/ace/orm/version_db new file mode 100644 index 0000000..7e20d8d --- /dev/null +++ b/modules/ace/orm/version_db @@ -0,0 +1,5 @@ +SQL( + BEGIN TRANSACTION; + CREATE TABLE DB_CHECKSUM (version INT); + COMMIT; +) diff --git a/modules/core/DESCRIPTION b/modules/core/DESCRIPTION new file mode 100644 index 0000000..1369c40 --- /dev/null +++ b/modules/core/DESCRIPTION @@ -0,0 +1 @@ +Main library code diff --git a/modules/core/config.cmake b/modules/core/config.cmake new file mode 100644 index 0000000..10450d0 --- /dev/null +++ b/modules/core/config.cmake @@ -0,0 +1,159 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file config.cmake +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +# Set DPL core sources +SET(DPL_CORE_SOURCES + ${PROJECT_SOURCE_DIR}/modules/core/src/abstract_waitable_input_adapter.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/abstract_waitable_input_output_adapter.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/abstract_waitable_output_adapter.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/address.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/apply.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/assert.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/atomic.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/binary_queue.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/char_traits.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/colors.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/copy.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/errno_string.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/exception.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/fast_delegate.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/file_input.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/file_input_mapping.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/file_output.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/lexical_cast.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/mutex.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/named_base_pipe.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/named_input_pipe.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/named_output_pipe.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/noncopyable.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/once.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/read_write_mutex.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/recursive_mutex.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/serialization.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/single_instance.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/singleton.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/semaphore.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/string.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/task.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/task_list.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/thread.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/type_list.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/union_cast.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/zip_input.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/application.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/main.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/waitable_event.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/waitable_handle.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/waitable_handle_watch_support.cpp + ${PROJECT_SOURCE_DIR}/modules/core/src/generic_event.cpp + PARENT_SCOPE +) + + +# Set DPL core headers +SET(DPL_CORE_HEADERS + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/abstract_input.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/abstract_input_output.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/abstract_output.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/abstract_waitable_input_adapter.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/abstract_waitable_input.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/abstract_waitable_input_output_adapter.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/abstract_waitable_input_output.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/abstract_waitable_output_adapter.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/abstract_waitable_output.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/address.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/aligned.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/apply.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/assert.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/atomic.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/binary_queue.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/bool_operator.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/char_traits.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/colors.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/copy.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/enable_shared_from_this.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/errno_string.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/exception.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/fast_delegate.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/file_input.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/file_input_mapping.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/file_output.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/foreach.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/generic_event.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/lexical_cast.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/mutex.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/named_base_pipe.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/named_input_pipe.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/named_output_pipe.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/noreturn.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/noncopyable.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/once.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/optional.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/optional_typedefs.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/preprocessor.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/read_write_mutex.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/recursive_mutex.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/scoped_resource.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/scoped_array.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/scoped_close.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/scoped_fclose.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/scoped_free.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/scoped_ptr.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/scoped_gpointer.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/serialization.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/semaphore.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/shared_ptr.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/single_instance.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/singleton.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/singleton_impl.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/singleton_safe_impl.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/string.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/sstream.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/task.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/task_list.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/thread.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/type_list.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/union_cast.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/unused.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/workaround.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/zip_input.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/application.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/framework_appcore.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/framework_efl.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/framework_vconf.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/main.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/waitable_event.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/waitable_handle.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/waitable_handle_watch_support.h + PARENT_SCOPE +) + +SET(DPL_CORE_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/modules/core/include + PARENT_SCOPE +) + +SET(DPL_3RDPARTY_HEADERS + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/3rdparty/fastdelegate/FastDelegate.h + ${PROJECT_SOURCE_DIR}/modules/core/include/dpl/3rdparty/fastdelegate/FastDelegateBind.h + PARENT_SCOPE +) + diff --git a/modules/core/include/DESCRIPTION b/modules/core/include/DESCRIPTION new file mode 100644 index 0000000..6dfd446 --- /dev/null +++ b/modules/core/include/DESCRIPTION @@ -0,0 +1,2 @@ +!!!options!!! stop +Header files, including template implementations diff --git a/modules/core/include/dpl/3rdparty/fastdelegate/FastDelegate.h b/modules/core/include/dpl/3rdparty/fastdelegate/FastDelegate.h new file mode 100644 index 0000000..c69fccc --- /dev/null +++ b/modules/core/include/dpl/3rdparty/fastdelegate/FastDelegate.h @@ -0,0 +1,2108 @@ +// FastDelegate.h +// Efficient delegates in C++ that generate only two lines of asm code! +// Documentation is found at http://www.codeproject.com/cpp/FastDelegate.asp +// +// - Don Clugston, Mar 2004. +// Major contributions were made by Jody Hagins. +// History: +// 24-Apr-04 1.0 * Submitted to CodeProject. +// 28-Apr-04 1.1 * Prevent most unsafe uses of evil static function hack. +// * Improved syntax for horrible_cast (thanks Paul Bludov). +// * Tested on Metrowerks MWCC and Intel ICL (IA32) +// * Compiled, but not run, on Comeau C++ and Intel Itanium ICL. +// 27-Jun-04 1.2 * Now works on Borland C++ Builder 5.5 +// * Now works on /clr "managed C++" code on VC7, VC7.1 +// * Comeau C++ now compiles without warnings. +// * Prevent the virtual inheritance case from being used on +// VC6 and earlier, which generate incorrect code. +// * Improved warning and error messages. Non-standard hacks +// now have compile-time checks to make them safer. +// * implicit_cast used instead of static_cast in many cases. +// * If calling a const member function, a const class pointer can be used. +// * MakeDelegate() global helper function added to simplify pass-by-value. +// * Added fastdelegate.clear() +// 16-Jul-04 1.2.1* Workaround for gcc bug (const member function pointers in templates) +// 30-Oct-04 1.3 * Support for (non-void) return values. +// * No more workarounds in client code! +// MSVC and Intel now use a clever hack invented by John Dlugosz: +// - The FASTDELEGATEDECLARE workaround is no longer necessary. +// - No more warning messages for VC6 +// * Less use of macros. Error messages should be more comprehensible. +// * Added include guards +// * Added FastDelegate::empty() to test if invocation is safe (Thanks Neville Franks). +// * Now tested on VS 2005 Express Beta, PGI C++ +// 24-Dec-04 1.4 * Added DelegateMemento, to allow collections of disparate delegates. +// * <,>,<=,>= comparison operators to allow storage in ordered containers. +// * Substantial reduction of code size, especially the 'Closure' class. +// * Standardised all the compiler-specific workarounds. +// * MFP conversion now works for CodePlay (but not yet supported in the full code). +// * Now compiles without warnings on _any_ supported compiler, including BCC 5.5.1 +// * New syntax: FastDelegate< int (char *, double) >. +// 14-Feb-05 1.4.1* Now treats =0 as equivalent to .clear(), ==0 as equivalent to .empty(). (Thanks elfric). +// * Now tested on Intel ICL for AMD64, VS2005 Beta for AMD64 and Itanium. +// 30-Mar-05 1.5 * Safebool idiom: "if (dg)" is now equivalent to "if (!dg.empty())" +// * Fully supported by CodePlay VectorC +// * Bugfix for Metrowerks: empty() was buggy because a valid MFP can be 0 on MWCC! +// * More optimal assignment,== and != operators for static function pointers. + +#ifndef FASTDELEGATE_H +#define FASTDELEGATE_H +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include // to allow <,> comparisons + +//////////////////////////////////////////////////////////////////////////////// +// Configuration options +// +//////////////////////////////////////////////////////////////////////////////// + +// Uncomment the following #define for optimally-sized delegates. +// In this case, the generated asm code is almost identical to the code you'd get +// if the compiler had native support for delegates. +// It will not work on systems where sizeof(dataptr) < sizeof(codeptr). +// Thus, it will not work for DOS compilers using the medium model. +// It will also probably fail on some DSP systems. +#define FASTDELEGATE_USESTATICFUNCTIONHACK + +// Uncomment the next line to allow function declarator syntax. +// It is automatically enabled for those compilers where it is known to work. +//#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX + +//////////////////////////////////////////////////////////////////////////////// +// Compiler identification for workarounds +// +//////////////////////////////////////////////////////////////////////////////// + +// Compiler identification. It's not easy to identify Visual C++ because +// many vendors fraudulently define Microsoft's identifiers. +#if defined(_MSC_VER) && !defined(__MWERKS__) && !defined(__VECTOR_C) && !defined(__ICL) && !defined(__BORLANDC__) +#define FASTDLGT_ISMSVC + +#if (_MSC_VER <1300) // Many workarounds are required for VC6. +#define FASTDLGT_VC6 +#pragma warning(disable:4786) // disable this ridiculous warning +#endif + +#endif + +// Does the compiler uses Microsoft's member function pointer structure? +// If so, it needs special treatment. +// Metrowerks CodeWarrior, Intel, and CodePlay fraudulently define Microsoft's +// identifier, _MSC_VER. We need to filter Metrowerks out. +#if defined(_MSC_VER) && !defined(__MWERKS__) +#define FASTDLGT_MICROSOFT_MFP + +#if !defined(__VECTOR_C) +// CodePlay doesn't have the __single/multi/virtual_inheritance keywords +#define FASTDLGT_HASINHERITANCE_KEYWORDS +#endif +#endif + +// Does it allow function declarator syntax? The following compilers are known to work: +#if defined(FASTDLGT_ISMSVC) && (_MSC_VER >=1310) // VC 7.1 +#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX +#endif + +// Gcc(2.95+), and versions of Digital Mars, Intel and Comeau in common use. +#if defined (__DMC__) || defined(__GNUC__) || defined(__ICL) || defined(__COMO__) +#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX +#endif + +// It works on Metrowerks MWCC 3.2.2. From boost.Config it should work on earlier ones too. +#if defined (__MWERKS__) +#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX +#endif + +#ifdef __GNUC__ // Workaround GCC bug #8271 + // At present, GCC doesn't recognize constness of MFPs in templates +#define FASTDELEGATE_GCC_BUG_8271 +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// General tricks used in this code +// +// (a) Error messages are generated by typdefing an array of negative size to +// generate compile-time errors. +// (b) Warning messages on MSVC are generated by declaring unused variables, and +// enabling the "variable XXX is never used" warning. +// (c) Unions are used in a few compiler-specific cases to perform illegal casts. +// (d) For Microsoft and Intel, when adjusting the 'this' pointer, it's cast to +// (char *) first to ensure that the correct number of *bytes* are added. +// +//////////////////////////////////////////////////////////////////////////////// +// Helper templates +// +//////////////////////////////////////////////////////////////////////////////// + + +namespace fastdelegate { +namespace detail { // we'll hide the implementation details in a nested namespace. + +// implicit_cast< > +// I believe this was originally going to be in the C++ standard but +// was left out by accident. It's even milder than static_cast. +// I use it instead of static_cast<> to emphasize that I'm not doing +// anything nasty. +// Usage is identical to static_cast<> +template +inline OutputClass implicit_cast(InputClass input){ + return input; +} + +// horrible_cast< > +// This is truly evil. It completely subverts C++'s type system, allowing you +// to cast from any class to any other class. Technically, using a union +// to perform the cast is undefined behaviour (even in C). But we can see if +// it is OK by checking that the union is the same size as each of its members. +// horrible_cast<> should only be used for compiler-specific workarounds. +// Usage is identical to reinterpret_cast<>. + +// This union is declared outside the horrible_cast because BCC 5.5.1 +// can't inline a function with a nested class, and gives a warning. +template +union horrible_union{ + OutputClass out; + InputClass in; +}; + +template +inline OutputClass horrible_cast(const InputClass input){ + horrible_union u; + // Cause a compile-time error if in, out and u are not the same size. + // If the compile fails here, it means the compiler has peculiar + // unions which would prevent the cast from working. + typedef int ERROR_CantUseHorrible_cast[sizeof(InputClass)==sizeof(u) + && sizeof(InputClass)==sizeof(OutputClass) ? 1 : -1]; + u.in = input; + return u.out; +} + +//////////////////////////////////////////////////////////////////////////////// +// Workarounds +// +//////////////////////////////////////////////////////////////////////////////// + +// Backwards compatibility: This macro used to be necessary in the virtual inheritance +// case for Intel and Microsoft. Now it just forward-declares the class. +#define FASTDELEGATEDECLARE(CLASSNAME) class CLASSNAME; + +// Prevent use of the static function hack with the DOS medium model. +#ifdef __MEDIUM__ +#undef FASTDELEGATE_USESTATICFUNCTIONHACK +#endif + +// DefaultVoid - a workaround for 'void' templates in VC6. +// +// (1) VC6 and earlier do not allow 'void' as a default template argument. +// (2) They also doesn't allow you to return 'void' from a function. +// +// Workaround for (1): Declare a dummy type 'DefaultVoid' which we use +// when we'd like to use 'void'. We convert it into 'void' and back +// using the templates DefaultVoidToVoid<> and VoidToDefaultVoid<>. +// Workaround for (2): On VC6, the code for calling a void function is +// identical to the code for calling a non-void function in which the +// return value is never used, provided the return value is returned +// in the EAX register, rather than on the stack. +// This is true for most fundamental types such as int, enum, void *. +// Const void * is the safest option since it doesn't participate +// in any automatic conversions. But on a 16-bit compiler it might +// cause extra code to be generated, so we disable it for all compilers +// except for VC6 (and VC5). +#ifdef FASTDLGT_VC6 +// VC6 workaround +typedef const void * DefaultVoid; +#else +// On any other compiler, just use a normal void. +typedef void DefaultVoid; +#endif + +// Translate from 'DefaultVoid' to 'void'. +// Everything else is unchanged +template +struct DefaultVoidToVoid { typedef T type; }; + +template <> +struct DefaultVoidToVoid { typedef void type; }; + +// Translate from 'void' into 'DefaultVoid' +// Everything else is unchanged +template +struct VoidToDefaultVoid { typedef T type; }; + +template <> +struct VoidToDefaultVoid { typedef DefaultVoid type; }; + + + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 1: +// +// Conversion of member function pointer to a standard form +// +//////////////////////////////////////////////////////////////////////////////// + +// GenericClass is a fake class, ONLY used to provide a type. +// It is vitally important that it is never defined, so that the compiler doesn't +// think it can optimize the invocation. For example, Borland generates simpler +// code if it knows the class only uses single inheritance. + +// Compilers using Microsoft's structure need to be treated as a special case. +#ifdef FASTDLGT_MICROSOFT_MFP + +#ifdef FASTDLGT_HASINHERITANCE_KEYWORDS + // For Microsoft and Intel, we want to ensure that it's the most efficient type of MFP + // (4 bytes), even when the /vmg option is used. Declaring an empty class + // would give 16 byte pointers in this case.... + class __single_inheritance GenericClass; +#endif + // ...but for Codeplay, an empty class *always* gives 4 byte pointers. + // If compiled with the /clr option ("managed C++"), the JIT compiler thinks + // it needs to load GenericClass before it can call any of its functions, + // (compiles OK but crashes at runtime!), so we need to declare an + // empty class to make it happy. + // Codeplay and VC4 can't cope with the unknown_inheritance case either. + class GenericClass {}; +#else + class GenericClass; +#endif + +// The size of a single inheritance member function pointer. +const int SINGLE_MEMFUNCPTR_SIZE = sizeof(void (GenericClass::*)()); + +// SimplifyMemFunc< >::Convert() +// +// A template function that converts an arbitrary member function pointer into the +// simplest possible form of member function pointer, using a supplied 'this' pointer. +// According to the standard, this can be done legally with reinterpret_cast<>. +// For (non-standard) compilers which use member function pointers which vary in size +// depending on the class, we need to use knowledge of the internal structure of a +// member function pointer, as used by the compiler. Template specialization is used +// to distinguish between the sizes. Because some compilers don't support partial +// template specialisation, I use full specialisation of a wrapper struct. + +// general case -- don't know how to convert it. Force a compile failure +template +struct SimplifyMemFunc { + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + // Unsupported member function type -- force a compile failure. + // (it's illegal to have a array with negative size). + typedef char ERROR_Unsupported_member_function_pointer_on_this_compiler[N-100]; + return 0; + } +}; + +// For compilers where all member func ptrs are the same size, everything goes here. +// For non-standard compilers, only single_inheritance classes go here. +template <> +struct SimplifyMemFunc { + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { +#if defined __DMC__ + // Digital Mars doesn't allow you to cast between abitrary PMF's, + // even though the standard says you can. The 32-bit compiler lets you + // static_cast through an int, but the DOS compiler doesn't. + bound_func = horrible_cast(function_to_bind); +#else + bound_func = reinterpret_cast(function_to_bind); +#endif + return reinterpret_cast(pthis); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 1b: +// +// Workarounds for Microsoft and Intel +// +//////////////////////////////////////////////////////////////////////////////// + + +// Compilers with member function pointers which violate the standard (MSVC, Intel, Codeplay), +// need to be treated as a special case. +#ifdef FASTDLGT_MICROSOFT_MFP + +// We use unions to perform horrible_casts. I would like to use #pragma pack(push, 1) +// at the start of each function for extra safety, but VC6 seems to ICE +// intermittently if you do this inside a template. + +// __multiple_inheritance classes go here +// Nasty hack for Microsoft and Intel (IA32 and Itanium) +template<> +struct SimplifyMemFunc< SINGLE_MEMFUNCPTR_SIZE + sizeof(int) > { + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + // We need to use a horrible_cast to do this conversion. + // In MSVC, a multiple inheritance member pointer is internally defined as: + union { + XFuncType func; + struct { + GenericMemFuncType funcaddress; // points to the actual member function + int delta; // #BYTES to be added to the 'this' pointer + }s; + } u; + // Check that the horrible_cast will work + typedef int ERROR_CantUsehorrible_cast[sizeof(function_to_bind)==sizeof(u.s)? 1 : -1]; + u.func = function_to_bind; + bound_func = u.s.funcaddress; + return reinterpret_cast(reinterpret_cast(pthis) + u.s.delta); + } +}; + +// virtual inheritance is a real nuisance. It's inefficient and complicated. +// On MSVC and Intel, there isn't enough information in the pointer itself to +// enable conversion to a closure pointer. Earlier versions of this code didn't +// work for all cases, and generated a compile-time error instead. +// But a very clever hack invented by John M. Dlugosz solves this problem. +// My code is somewhat different to his: I have no asm code, and I make no +// assumptions about the calling convention that is used. + +// In VC++ and ICL, a virtual_inheritance member pointer +// is internally defined as: +struct MicrosoftVirtualMFP { + void (GenericClass::*codeptr)(); // points to the actual member function + int delta; // #bytes to be added to the 'this' pointer + int vtable_index; // or 0 if no virtual inheritance +}; +// The CRUCIAL feature of Microsoft/Intel MFPs which we exploit is that the +// m_codeptr member is *always* called, regardless of the values of the other +// members. (This is *not* true for other compilers, eg GCC, which obtain the +// function address from the vtable if a virtual function is being called). +// Dlugosz's trick is to make the codeptr point to a probe function which +// returns the 'this' pointer that was used. + +// Define a generic class that uses virtual inheritance. +// It has a trival member function that returns the value of the 'this' pointer. +struct GenericVirtualClass : virtual public GenericClass +{ + typedef GenericVirtualClass * (GenericVirtualClass::*ProbePtrType)(); + GenericVirtualClass * GetThis() { return this; } +}; + +// __virtual_inheritance classes go here +template <> +struct SimplifyMemFunc +{ + + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + union { + XFuncType func; + GenericClass* (X::*ProbeFunc)(); + MicrosoftVirtualMFP s; + } u; + u.func = function_to_bind; + bound_func = reinterpret_cast(u.s.codeptr); + union { + GenericVirtualClass::ProbePtrType virtfunc; + MicrosoftVirtualMFP s; + } u2; + // Check that the horrible_cast<>s will work + typedef int ERROR_CantUsehorrible_cast[sizeof(function_to_bind)==sizeof(u.s) + && sizeof(function_to_bind)==sizeof(u.ProbeFunc) + && sizeof(u2.virtfunc)==sizeof(u2.s) ? 1 : -1]; + // Unfortunately, taking the address of a MF prevents it from being inlined, so + // this next line can't be completely optimised away by the compiler. + u2.virtfunc = &GenericVirtualClass::GetThis; + u.s.codeptr = u2.s.codeptr; + return (pthis->*u.ProbeFunc)(); + } +}; + +#if (_MSC_VER <1300) + +// Nasty hack for Microsoft Visual C++ 6.0 +// unknown_inheritance classes go here +// There is a compiler bug in MSVC6 which generates incorrect code in this case!! +template <> +struct SimplifyMemFunc +{ + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + // There is an apalling but obscure compiler bug in MSVC6 and earlier: + // vtable_index and 'vtordisp' are always set to 0 in the + // unknown_inheritance case! + // This means that an incorrect function could be called!!! + // Compiling with the /vmg option leads to potentially incorrect code. + // This is probably the reason that the IDE has a user interface for specifying + // the /vmg option, but it is disabled - you can only specify /vmg on + // the command line. In VC1.5 and earlier, the compiler would ICE if it ever + // encountered this situation. + // It is OK to use the /vmg option if /vmm or /vms is specified. + + // Fortunately, the wrong function is only called in very obscure cases. + // It only occurs when a derived class overrides a virtual function declared + // in a virtual base class, and the member function + // points to the *Derived* version of that function. The problem can be + // completely averted in 100% of cases by using the *Base class* for the + // member fpointer. Ie, if you use the base class as an interface, you'll + // stay out of trouble. + // Occasionally, you might want to point directly to a derived class function + // that isn't an override of a base class. In this case, both vtable_index + // and 'vtordisp' are zero, but a virtual_inheritance pointer will be generated. + // We can generate correct code in this case. To prevent an incorrect call from + // ever being made, on MSVC6 we generate a warning, and call a function to + // make the program crash instantly. + typedef char ERROR_VC6CompilerBug[-100]; + return 0; + } +}; + + +#else + +// Nasty hack for Microsoft and Intel (IA32 and Itanium) +// unknown_inheritance classes go here +// This is probably the ugliest bit of code I've ever written. Look at the casts! +// There is a compiler bug in MSVC6 which prevents it from using this code. +template <> +struct SimplifyMemFunc +{ + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + // The member function pointer is 16 bytes long. We can't use a normal cast, but + // we can use a union to do the conversion. + union { + XFuncType func; + // In VC++ and ICL, an unknown_inheritance member pointer + // is internally defined as: + struct { + GenericMemFuncType m_funcaddress; // points to the actual member function + int delta; // #bytes to be added to the 'this' pointer + int vtordisp; // #bytes to add to 'this' to find the vtable + int vtable_index; // or 0 if no virtual inheritance + } s; + } u; + // Check that the horrible_cast will work + typedef int ERROR_CantUsehorrible_cast[sizeof(XFuncType)==sizeof(u.s)? 1 : -1]; + u.func = function_to_bind; + bound_func = u.s.funcaddress; + int virtual_delta = 0; + if (u.s.vtable_index) { // Virtual inheritance is used + // First, get to the vtable. + // It is 'vtordisp' bytes from the start of the class. + const int * vtable = *reinterpret_cast( + reinterpret_cast(pthis) + u.s.vtordisp ); + + // 'vtable_index' tells us where in the table we should be looking. + virtual_delta = u.s.vtordisp + *reinterpret_cast( + reinterpret_cast(vtable) + u.s.vtable_index); + } + // The int at 'virtual_delta' gives us the amount to add to 'this'. + // Finally we can add the three components together. Phew! + return reinterpret_cast( + reinterpret_cast(pthis) + u.s.delta + virtual_delta); + }; +}; +#endif // MSVC 7 and greater + +#endif // MS/Intel hacks + +} // namespace detail + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 2: +// +// Define the delegate storage, and cope with static functions +// +//////////////////////////////////////////////////////////////////////////////// + +// DelegateMemento -- an opaque structure which can hold an arbitary delegate. +// It knows nothing about the calling convention or number of arguments used by +// the function pointed to. +// It supplies comparison operators so that it can be stored in STL collections. +// It cannot be set to anything other than null, nor invoked directly: +// it must be converted to a specific delegate. + +// Implementation: +// There are two possible implementations: the Safe method and the Evil method. +// DelegateMemento - Safe version +// +// This implementation is standard-compliant, but a bit tricky. +// A static function pointer is stored inside the class. +// Here are the valid values: +// +-- Static pointer --+--pThis --+-- pMemFunc-+-- Meaning------+ +// | 0 | 0 | 0 | Empty | +// | !=0 |(dontcare)| Invoker | Static function| +// | 0 | !=0 | !=0* | Method call | +// +--------------------+----------+------------+----------------+ +// * For Metrowerks, this can be 0. (first virtual function in a +// single_inheritance class). +// When stored stored inside a specific delegate, the 'dontcare' entries are replaced +// with a reference to the delegate itself. This complicates the = and == operators +// for the delegate class. + +// DelegateMemento - Evil version +// +// For compilers where data pointers are at least as big as code pointers, it is +// possible to store the function pointer in the this pointer, using another +// horrible_cast. In this case the DelegateMemento implementation is simple: +// +--pThis --+-- pMemFunc-+-- Meaning---------------------+ +// | 0 | 0 | Empty | +// | !=0 | !=0* | Static function or method call| +// +----------+------------+-------------------------------+ +// * For Metrowerks, this can be 0. (first virtual function in a +// single_inheritance class). +// Note that the Sun C++ and MSVC documentation explicitly state that they +// support static_cast between void * and function pointers. + +class DelegateMemento { +protected: + // the data is protected, not private, because many + // compilers have problems with template friends. + typedef void (detail::GenericClass::*GenericMemFuncType)(); // arbitrary MFP. + detail::GenericClass *m_pthis; + GenericMemFuncType m_pFunction; + +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + typedef void (*GenericFuncPtr)(); // arbitrary code pointer + GenericFuncPtr m_pStaticFunction; +#endif + +public: +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + DelegateMemento() : m_pthis(0), m_pFunction(0), m_pStaticFunction(0) {}; + void clear() { + m_pthis=0; m_pFunction=0; m_pStaticFunction=0; + } +#else + DelegateMemento() : m_pthis(0), m_pFunction(0) {}; + void clear() { m_pthis=0; m_pFunction=0; } +#endif +public: +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + inline bool IsEqual (const DelegateMemento &x) const{ + // We have to cope with the static function pointers as a special case + if (m_pFunction!=x.m_pFunction) return false; + // the static function ptrs must either both be equal, or both be 0. + if (m_pStaticFunction!=x.m_pStaticFunction) return false; + if (m_pStaticFunction!=0) return m_pthis==x.m_pthis; + else return true; + } +#else // Evil Method + inline bool IsEqual (const DelegateMemento &x) const{ + return m_pthis==x.m_pthis && m_pFunction==x.m_pFunction; + } +#endif + // Provide a strict weak ordering for DelegateMementos. + inline bool IsLess(const DelegateMemento &right) const { + // deal with static function pointers first +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + if (m_pStaticFunction !=0 || right.m_pStaticFunction!=0) + return m_pStaticFunction < right.m_pStaticFunction; +#endif + if (m_pthis !=right.m_pthis) return m_pthis < right.m_pthis; + // There are no ordering operators for member function pointers, + // but we can fake one by comparing each byte. The resulting ordering is + // arbitrary (and compiler-dependent), but it permits storage in ordered STL containers. + return memcmp(&m_pFunction, &right.m_pFunction, sizeof(m_pFunction)) < 0; + + } + // BUGFIX (Mar 2005): + // We can't just compare m_pFunction because on Metrowerks, + // m_pFunction can be zero even if the delegate is not empty! + inline bool operator ! () const // Is it bound to anything? + { return m_pthis==0 && m_pFunction==0; } + inline bool empty() const // Is it bound to anything? + { return m_pthis==0 && m_pFunction==0; } +public: + DelegateMemento & operator = (const DelegateMemento &right) { + SetMementoFrom(right); + return *this; + } + inline bool operator <(const DelegateMemento &right) { + return IsLess(right); + } + inline bool operator >(const DelegateMemento &right) { + return right.IsLess(*this); + } + DelegateMemento (const DelegateMemento &right) : + m_pFunction(right.m_pFunction), m_pthis(right.m_pthis) +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + , m_pStaticFunction (right.m_pStaticFunction) +#endif + {} +protected: + void SetMementoFrom(const DelegateMemento &right) { + m_pFunction = right.m_pFunction; + m_pthis = right.m_pthis; +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + m_pStaticFunction = right.m_pStaticFunction; +#endif + } +}; + + +// ClosurePtr<> +// +// A private wrapper class that adds function signatures to DelegateMemento. +// It's the class that does most of the actual work. +// The signatures are specified by: +// GenericMemFunc: must be a type of GenericClass member function pointer. +// StaticFuncPtr: must be a type of function pointer with the same signature +// as GenericMemFunc. +// UnvoidStaticFuncPtr: is the same as StaticFuncPtr, except on VC6 +// where it never returns void (returns DefaultVoid instead). + +// An outer class, FastDelegateN<>, handles the invoking and creates the +// necessary typedefs. +// This class does everything else. + +namespace detail { + +template < class GenericMemFunc, class StaticFuncPtr, class UnvoidStaticFuncPtr> +class ClosurePtr : public DelegateMemento { +public: + // These functions are for setting the delegate to a member function. + + // Here's the clever bit: we convert an arbitrary member function into a + // standard form. XMemFunc should be a member function of class X, but I can't + // enforce that here. It needs to be enforced by the wrapper class. + template < class X, class XMemFunc > + inline void bindmemfunc(X *pthis, XMemFunc function_to_bind ) { + m_pthis = SimplifyMemFunc< sizeof(function_to_bind) > + ::Convert(pthis, function_to_bind, m_pFunction); +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + m_pStaticFunction = 0; +#endif + } + // For const member functions, we only need a const class pointer. + // Since we know that the member function is const, it's safe to + // remove the const qualifier from the 'this' pointer with a const_cast. + // VC6 has problems if we just overload 'bindmemfunc', so we give it a different name. + template < class X, class XMemFunc> + inline void bindconstmemfunc(const X *pthis, XMemFunc function_to_bind) { + m_pthis= SimplifyMemFunc< sizeof(function_to_bind) > + ::Convert(const_cast(pthis), function_to_bind, m_pFunction); +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + m_pStaticFunction = 0; +#endif + } +#ifdef FASTDELEGATE_GCC_BUG_8271 // At present, GCC doesn't recognize constness of MFPs in templates + template < class X, class XMemFunc> + inline void bindmemfunc(const X *pthis, XMemFunc function_to_bind) { + bindconstmemfunc(pthis, function_to_bind); +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + m_pStaticFunction = 0; +#endif + } +#endif + // These functions are required for invoking the stored function + inline GenericClass *GetClosureThis() const { return m_pthis; } + inline GenericMemFunc GetClosureMemPtr() const { return reinterpret_cast(m_pFunction); } + +// There are a few ways of dealing with static function pointers. +// There's a standard-compliant, but tricky method. +// There's also a straightforward hack, that won't work on DOS compilers using the +// medium memory model. It's so evil that I can't recommend it, but I've +// implemented it anyway because it produces very nice asm code. + +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + +// ClosurePtr<> - Safe version +// +// This implementation is standard-compliant, but a bit tricky. +// I store the function pointer inside the class, and the delegate then +// points to itself. Whenever the delegate is copied, these self-references +// must be transformed, and this complicates the = and == operators. +public: + // The next two functions are for operator ==, =, and the copy constructor. + // We may need to convert the m_pthis pointers, so that + // they remain as self-references. + template< class DerivedClass > + inline void CopyFrom (DerivedClass *pParent, const DelegateMemento &x) { + SetMementoFrom(x); + if (m_pStaticFunction!=0) { + // transform self references... + m_pthis=reinterpret_cast(pParent); + } + } + // For static functions, the 'static_function_invoker' class in the parent + // will be called. The parent then needs to call GetStaticFunction() to find out + // the actual function to invoke. + template < class DerivedClass, class ParentInvokerSig > + inline void bindstaticfunc(DerivedClass *pParent, ParentInvokerSig static_function_invoker, + StaticFuncPtr function_to_bind ) { + if (function_to_bind==0) { // cope with assignment to 0 + m_pFunction=0; + } else { + bindmemfunc(pParent, static_function_invoker); + } + m_pStaticFunction=reinterpret_cast(function_to_bind); + } + inline UnvoidStaticFuncPtr GetStaticFunction() const { + return reinterpret_cast(m_pStaticFunction); + } +#else + +// ClosurePtr<> - Evil version +// +// For compilers where data pointers are at least as big as code pointers, it is +// possible to store the function pointer in the this pointer, using another +// horrible_cast. Invocation isn't any faster, but it saves 4 bytes, and +// speeds up comparison and assignment. If C++ provided direct language support +// for delegates, they would produce asm code that was almost identical to this. +// Note that the Sun C++ and MSVC documentation explicitly state that they +// support static_cast between void * and function pointers. + + template< class DerivedClass > + inline void CopyFrom (DerivedClass *pParent, const DelegateMemento &right) { + SetMementoFrom(right); + } + // For static functions, the 'static_function_invoker' class in the parent + // will be called. The parent then needs to call GetStaticFunction() to find out + // the actual function to invoke. + // ******** EVIL, EVIL CODE! ******* + template < class DerivedClass, class ParentInvokerSig> + inline void bindstaticfunc(DerivedClass *pParent, ParentInvokerSig static_function_invoker, + StaticFuncPtr function_to_bind) { + if (function_to_bind==0) { // cope with assignment to 0 + m_pFunction=0; + } else { + // We'll be ignoring the 'this' pointer, but we need to make sure we pass + // a valid value to bindmemfunc(). + bindmemfunc(pParent, static_function_invoker); + } + + // WARNING! Evil hack. We store the function in the 'this' pointer! + // Ensure that there's a compilation failure if function pointers + // and data pointers have different sizes. + // If you get this error, you need to #undef FASTDELEGATE_USESTATICFUNCTIONHACK. + typedef int ERROR_CantUseEvilMethod[sizeof(GenericClass *)==sizeof(function_to_bind) ? 1 : -1]; + m_pthis = horrible_cast(function_to_bind); + // MSVC, SunC++ and DMC accept the following (non-standard) code: +// m_pthis = static_cast(static_cast(function_to_bind)); + // BCC32, Comeau and DMC accept this method. MSVC7.1 needs __int64 instead of long +// m_pthis = reinterpret_cast(reinterpret_cast(function_to_bind)); + } + // ******** EVIL, EVIL CODE! ******* + // This function will be called with an invalid 'this' pointer!! + // We're just returning the 'this' pointer, converted into + // a function pointer! + inline UnvoidStaticFuncPtr GetStaticFunction() const { + // Ensure that there's a compilation failure if function pointers + // and data pointers have different sizes. + // If you get this error, you need to #undef FASTDELEGATE_USESTATICFUNCTIONHACK. + typedef int ERROR_CantUseEvilMethod[sizeof(UnvoidStaticFuncPtr)==sizeof(this) ? 1 : -1]; + return horrible_cast(this); + } +#endif // !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + + // Does the closure contain this static function? + inline bool IsEqualToStaticFuncPtr(StaticFuncPtr funcptr){ + if (funcptr==0) return empty(); + // For the Evil method, if it doesn't actually contain a static function, this will return an arbitrary + // value that is not equal to any valid function pointer. + else return funcptr==reinterpret_cast(GetStaticFunction()); + } +}; + + +} // namespace detail + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 3: +// +// Wrapper classes to ensure type safety +// +//////////////////////////////////////////////////////////////////////////////// + + +// Once we have the member function conversion templates, it's easy to make the +// wrapper classes. So that they will work with as many compilers as possible, +// the classes are of the form +// FastDelegate3 +// They can cope with any combination of parameters. The max number of parameters +// allowed is 8, but it is trivial to increase this limit. +// Note that we need to treat const member functions seperately. +// All this class does is to enforce type safety, and invoke the delegate with +// the correct list of parameters. + +// Because of the weird rule about the class of derived member function pointers, +// you sometimes need to apply a downcast to the 'this' pointer. +// This is the reason for the use of "implicit_cast(pthis)" in the code below. +// If CDerivedClass is derived from CBaseClass, but doesn't override SimpleVirtualFunction, +// without this trick you'd need to write: +// MyDelegate(static_cast(&d), &CDerivedClass::SimpleVirtualFunction); +// but with the trick you can write +// MyDelegate(&d, &CDerivedClass::SimpleVirtualFunction); + +// RetType is the type the compiler uses in compiling the template. For VC6, +// it cannot be void. DesiredRetType is the real type which is returned from +// all of the functions. It can be void. + +// Implicit conversion to "bool" is achieved using the safe_bool idiom, +// using member data pointers (MDP). This allows "if (dg)..." syntax +// Because some compilers (eg codeplay) don't have a unique value for a zero +// MDP, an extra padding member is added to the SafeBool struct. +// Some compilers (eg VC6) won't implicitly convert from 0 to an MDP, so +// in that case the static function constructor is not made explicit; this +// allows "if (dg==0) ..." to compile. + +//N=0 +template +class FastDelegate0 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(); + typedef RetType (*UnvoidStaticFunctionPtr)(); + typedef RetType (detail::GenericClass::*GenericMemFn)(); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate0 type; + + // Construction and comparison functions + FastDelegate0() { clear(); } + FastDelegate0(const FastDelegate0 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate0 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate0 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate0 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate0 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate0 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate0(Y *pthis, DesiredRetType (X::* function_to_bind)() ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)()) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate0(const Y *pthis, DesiredRetType (X::* function_to_bind)() const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)() const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate0(DesiredRetType (*function_to_bind)() ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)() ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)()) { + m_Closure.bindstaticfunc(this, &FastDelegate0::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() () const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction() const { + return (*(m_Closure.GetStaticFunction()))(); } +}; + +//N=1 +template +class FastDelegate1 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate1 type; + + // Construction and comparison functions + FastDelegate1() { clear(); } + FastDelegate1(const FastDelegate1 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate1 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate1 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate1 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate1 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate1 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate1(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate1(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate1(DesiredRetType (*function_to_bind)(Param1 p1) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1)) { + m_Closure.bindstaticfunc(this, &FastDelegate1::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1) const { + return (*(m_Closure.GetStaticFunction()))(p1); } +}; + +//N=2 +template +class FastDelegate2 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate2 type; + + // Construction and comparison functions + FastDelegate2() { clear(); } + FastDelegate2(const FastDelegate2 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate2 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate2 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate2 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate2 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate2 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate2(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate2(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate2(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2)) { + m_Closure.bindstaticfunc(this, &FastDelegate2::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2); } +}; + +//N=3 +template +class FastDelegate3 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate3 type; + + // Construction and comparison functions + FastDelegate3() { clear(); } + FastDelegate3(const FastDelegate3 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate3 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate3 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate3 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate3 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate3 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate3(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate3(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate3(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3)) { + m_Closure.bindstaticfunc(this, &FastDelegate3::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3); } +}; + +//N=4 +template +class FastDelegate4 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate4 type; + + // Construction and comparison functions + FastDelegate4() { clear(); } + FastDelegate4(const FastDelegate4 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate4 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate4 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate4 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate4 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate4 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate4(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate4(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate4(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4)) { + m_Closure.bindstaticfunc(this, &FastDelegate4::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4); } +}; + +//N=5 +template +class FastDelegate5 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate5 type; + + // Construction and comparison functions + FastDelegate5() { clear(); } + FastDelegate5(const FastDelegate5 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate5 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate5 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate5 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate5 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate5 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate5(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate5(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate5(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5)) { + m_Closure.bindstaticfunc(this, &FastDelegate5::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4, p5); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4, p5); } +}; + +//N=6 +template +class FastDelegate6 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate6 type; + + // Construction and comparison functions + FastDelegate6() { clear(); } + FastDelegate6(const FastDelegate6 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate6 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate6 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate6 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate6 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate6 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate6(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate6(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate6(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6)) { + m_Closure.bindstaticfunc(this, &FastDelegate6::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4, p5, p6); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4, p5, p6); } +}; + +//N=7 +template +class FastDelegate7 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate7 type; + + // Construction and comparison functions + FastDelegate7() { clear(); } + FastDelegate7(const FastDelegate7 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate7 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate7 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate7 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate7 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate7 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate7(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate7(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate7(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7)) { + m_Closure.bindstaticfunc(this, &FastDelegate7::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4, p5, p6, p7); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4, p5, p6, p7); } +}; + +//N=8 +template +class FastDelegate8 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate8 type; + + // Construction and comparison functions + FastDelegate8() { clear(); } + FastDelegate8(const FastDelegate8 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate8 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate8 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate8 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate8 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate8 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate8(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate8(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate8(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8)) { + m_Closure.bindstaticfunc(this, &FastDelegate8::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4, p5, p6, p7, p8); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4, p5, p6, p7, p8); } +}; + + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 4: +// +// FastDelegate<> class (Original author: Jody Hagins) +// Allows boost::function style syntax like: +// FastDelegate< double (int, long) > +// instead of: +// FastDelegate2< int, long, double > +// +//////////////////////////////////////////////////////////////////////////////// + +#ifdef FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX + +// Declare FastDelegate as a class template. It will be specialized +// later for all number of arguments. +template +class FastDelegate; + +//N=0 +// Specialization to allow use of +// FastDelegate< R ( ) > +// instead of +// FastDelegate0 < R > +template +class FastDelegate< R ( ) > + // Inherit from FastDelegate0 so that it can be treated just like a FastDelegate0 + : public FastDelegate0 < R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate0 < R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=1 +// Specialization to allow use of +// FastDelegate< R ( Param1 ) > +// instead of +// FastDelegate1 < Param1, R > +template +class FastDelegate< R ( Param1 ) > + // Inherit from FastDelegate1 so that it can be treated just like a FastDelegate1 + : public FastDelegate1 < Param1, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate1 < Param1, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=2 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2 ) > +// instead of +// FastDelegate2 < Param1, Param2, R > +template +class FastDelegate< R ( Param1, Param2 ) > + // Inherit from FastDelegate2 so that it can be treated just like a FastDelegate2 + : public FastDelegate2 < Param1, Param2, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate2 < Param1, Param2, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=3 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3 ) > +// instead of +// FastDelegate3 < Param1, Param2, Param3, R > +template +class FastDelegate< R ( Param1, Param2, Param3 ) > + // Inherit from FastDelegate3 so that it can be treated just like a FastDelegate3 + : public FastDelegate3 < Param1, Param2, Param3, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate3 < Param1, Param2, Param3, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=4 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4 ) > +// instead of +// FastDelegate4 < Param1, Param2, Param3, Param4, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4 ) > + // Inherit from FastDelegate4 so that it can be treated just like a FastDelegate4 + : public FastDelegate4 < Param1, Param2, Param3, Param4, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate4 < Param1, Param2, Param3, Param4, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=5 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4, Param5 ) > +// instead of +// FastDelegate5 < Param1, Param2, Param3, Param4, Param5, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4, Param5 ) > + // Inherit from FastDelegate5 so that it can be treated just like a FastDelegate5 + : public FastDelegate5 < Param1, Param2, Param3, Param4, Param5, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate5 < Param1, Param2, Param3, Param4, Param5, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=6 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6 ) > +// instead of +// FastDelegate6 < Param1, Param2, Param3, Param4, Param5, Param6, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6 ) > + // Inherit from FastDelegate6 so that it can be treated just like a FastDelegate6 + : public FastDelegate6 < Param1, Param2, Param3, Param4, Param5, Param6, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate6 < Param1, Param2, Param3, Param4, Param5, Param6, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=7 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6, Param7 ) > +// instead of +// FastDelegate7 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6, Param7 ) > + // Inherit from FastDelegate7 so that it can be treated just like a FastDelegate7 + : public FastDelegate7 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate7 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=8 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8 ) > +// instead of +// FastDelegate8 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8 ) > + // Inherit from FastDelegate8 so that it can be treated just like a FastDelegate8 + : public FastDelegate8 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate8 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + + +#endif //FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 5: +// +// MakeDelegate() helper function +// +// MakeDelegate(&x, &X::func) returns a fastdelegate of the type +// necessary for calling x.func() with the correct number of arguments. +// This makes it possible to eliminate many typedefs from user code. +// +//////////////////////////////////////////////////////////////////////////////// + +// Also declare overloads of a MakeDelegate() global function to +// reduce the need for typedefs. +// We need seperate overloads for const and non-const member functions. +// Also, because of the weird rule about the class of derived member function pointers, +// implicit downcasts may need to be applied later to the 'this' pointer. +// That's why two classes (X and Y) appear in the definitions. Y must be implicitly +// castable to X. + +// Workaround for VC6. VC6 needs void return types converted into DefaultVoid. +// GCC 3.2 and later won't compile this unless it's preceded by 'typename', +// but VC6 doesn't allow 'typename' in this context. +// So, I have to use a macro. + +#ifdef FASTDLGT_VC6 +#define FASTDLGT_RETTYPE detail::VoidToDefaultVoid::type +#else +#define FASTDLGT_RETTYPE RetType +#endif + +//N=0 +template +FastDelegate0 MakeDelegate(Y* x, RetType (X::*func)()) { + return FastDelegate0(x, func); +} + +template +FastDelegate0 MakeDelegate(Y* x, RetType (X::*func)() const) { + return FastDelegate0(x, func); +} + +//N=1 +template +FastDelegate1 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1)) { + return FastDelegate1(x, func); +} + +template +FastDelegate1 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1) const) { + return FastDelegate1(x, func); +} + +//N=2 +template +FastDelegate2 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2)) { + return FastDelegate2(x, func); +} + +template +FastDelegate2 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2) const) { + return FastDelegate2(x, func); +} + +//N=3 +template +FastDelegate3 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3)) { + return FastDelegate3(x, func); +} + +template +FastDelegate3 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3) const) { + return FastDelegate3(x, func); +} + +//N=4 +template +FastDelegate4 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4)) { + return FastDelegate4(x, func); +} + +template +FastDelegate4 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) const) { + return FastDelegate4(x, func); +} + +//N=5 +template +FastDelegate5 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5)) { + return FastDelegate5(x, func); +} + +template +FastDelegate5 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const) { + return FastDelegate5(x, func); +} + +//N=6 +template +FastDelegate6 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6)) { + return FastDelegate6(x, func); +} + +template +FastDelegate6 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const) { + return FastDelegate6(x, func); +} + +//N=7 +template +FastDelegate7 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7)) { + return FastDelegate7(x, func); +} + +template +FastDelegate7 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const) { + return FastDelegate7(x, func); +} + +//N=8 +template +FastDelegate8 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8)) { + return FastDelegate8(x, func); +} + +template +FastDelegate8 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const) { + return FastDelegate8(x, func); +} + + + // clean up after ourselves... +#undef FASTDLGT_RETTYPE + +} // namespace fastdelegate + +#endif // !defined(FASTDELEGATE_H) + diff --git a/modules/core/include/dpl/3rdparty/fastdelegate/FastDelegateBind.h b/modules/core/include/dpl/3rdparty/fastdelegate/FastDelegateBind.h new file mode 100644 index 0000000..083a807 --- /dev/null +++ b/modules/core/include/dpl/3rdparty/fastdelegate/FastDelegateBind.h @@ -0,0 +1,243 @@ +// FastDelegateBind.h +// Helper file for FastDelegates. Provides bind() function, enabling +// FastDelegates to be rapidly compared to programs using boost::function and boost::bind. +// +// Documentation is found at http://www.codeproject.com/cpp/FastDelegate.asp +// +// Original author: Jody Hagins. +// Minor changes by Don Clugston. +// +// Warning: The arguments to 'bind' are ignored! No actual binding is performed. +// The behaviour is equivalent to boost::bind only when the basic placeholder +// arguments _1, _2, _3, etc are used in order. +// +// HISTORY: +// 1.4 Dec 2004. Initial release as part of FastDelegate 1.4. + + +#ifndef FASTDELEGATEBIND_H +#define FASTDELEGATEBIND_H +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +//////////////////////////////////////////////////////////////////////////////// +// FastDelegate bind() +// +// bind() helper function for boost compatibility. +// (Original author: Jody Hagins). +// +// Add another helper, so FastDelegate can be a dropin replacement +// for boost::bind (in a fair number of cases). +// Note the elipses, because boost::bind() takes place holders +// but FastDelegate does not care about them. Getting the place holder +// mechanism to work, and play well with boost is a bit tricky, so +// we do the "easy" thing... +// Assume we have the following code... +// using boost::bind; +// bind(&Foo:func, &foo, _1, _2); +// we should be able to replace the "using" with... +// using fastdelegate::bind; +// and everything should work fine... +//////////////////////////////////////////////////////////////////////////////// + +#ifdef FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX + +namespace fastdelegate { + +//N=0 +template +FastDelegate< RetType ( ) > +bind( + RetType (X::*func)( ), + Y * y, + ...) +{ + return FastDelegate< RetType ( ) >(y, func); +} + +template +FastDelegate< RetType ( ) > +bind( + RetType (X::*func)( ) const, + Y * y, + ...) +{ + return FastDelegate< RetType ( ) >(y, func); +} + +//N=1 +template +FastDelegate< RetType ( Param1 p1 ) > +bind( + RetType (X::*func)( Param1 p1 ), + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1 ) >(y, func); +} + +template +FastDelegate< RetType ( Param1 p1 ) > +bind( + RetType (X::*func)( Param1 p1 ) const, + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1 ) >(y, func); +} + +//N=2 +template +FastDelegate< RetType ( Param1 p1, Param2 p2 ) > +bind( + RetType (X::*func)( Param1 p1, Param2 p2 ), + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1, Param2 p2 ) >(y, func); +} + +template +FastDelegate< RetType ( Param1 p1, Param2 p2 ) > +bind( + RetType (X::*func)( Param1 p1, Param2 p2 ) const, + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1, Param2 p2 ) >(y, func); +} + +//N=3 +template +FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3 ) > +bind( + RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3 ), + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3 ) >(y, func); +} + +template +FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3 ) > +bind( + RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3 ) const, + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3 ) >(y, func); +} + +//N=4 +template +FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) > +bind( + RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ), + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) >(y, func); +} + +template +FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) > +bind( + RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) const, + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) >(y, func); +} + +//N=5 +template +FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) > +bind( + RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ), + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) >(y, func); +} + +template +FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) > +bind( + RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) const, + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) >(y, func); +} + +//N=6 +template +FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) > +bind( + RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ), + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) >(y, func); +} + +template +FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) > +bind( + RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) const, + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) >(y, func); +} + +//N=7 +template +FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) > +bind( + RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ), + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) >(y, func); +} + +template +FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) > +bind( + RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) const, + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) >(y, func); +} + +//N=8 +template +FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) > +bind( + RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ), + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) >(y, func); +} + +template +FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) > +bind( + RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) const, + Y * y, + ...) +{ + return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) >(y, func); +} + + +#endif //FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX + +} // namespace fastdelegate + +#endif // !defined(FASTDELEGATEBIND_H) + diff --git a/modules/core/include/dpl/abstract_input.h b/modules/core/include/dpl/abstract_input.h new file mode 100644 index 0000000..89ed8fc --- /dev/null +++ b/modules/core/include/dpl/abstract_input.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_input.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of abstract input + */ +#ifndef DPL_ABSTRACT_INPUT_H +#define DPL_ABSTRACT_INPUT_H + +#include +#include + +namespace DPL +{ +class BinaryQueue; +typedef std::auto_ptr BinaryQueueAutoPtr; + +class AbstractInput +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, ReadFailed) + }; + +public: + virtual ~AbstractInput() {} + + /** + * Read binary data from input + * If no data is available method returns NULL buffer. + * In case connection was successfuly close, method returns empty buffer + * + * @param[in] size Maximum number of bytes to read from input + * @return Buffer containing read bytes + * @throw ReadFailed + */ + virtual BinaryQueueAutoPtr Read(size_t size) = 0; +}; +} // namespace DPL + +#endif // DPL_ABSTRACT_INPUT_H diff --git a/modules/core/include/dpl/abstract_input_output.h b/modules/core/include/dpl/abstract_input_output.h new file mode 100644 index 0000000..f775afc --- /dev/null +++ b/modules/core/include/dpl/abstract_input_output.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_output.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of abstract output + */ +#ifndef DPL_ABSTRACT_INPUT_OUTPUT_H +#define DPL_ABSTRACT_INPUT_OUTPUT_H + +#include +#include + +namespace DPL +{ +class AbstractInputOutput + : public AbstractInput, + public AbstractOutput +{ +public: + virtual ~AbstractInputOutput() {} +}; + +} // namespace DPL + +#endif // DPL_ABSTRACT_INPUT_OUTPUT_H diff --git a/modules/core/include/dpl/abstract_output.h b/modules/core/include/dpl/abstract_output.h new file mode 100644 index 0000000..160907c --- /dev/null +++ b/modules/core/include/dpl/abstract_output.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_output.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of abstract output + */ +#ifndef DPL_ABSTRACT_OUTPUT_H +#define DPL_ABSTRACT_OUTPUT_H + +#include +#include + +namespace DPL +{ +class BinaryQueue; +typedef std::auto_ptr BinaryQueueAutoPtr; + +class AbstractOutput +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, WriteFailed) + }; + +public: + virtual ~AbstractOutput() {} + + /** + * Write binary data to output + * If output is blocked, Write returns zero, if instance is a type of + * WaitableAbstractOutput one can wait for writability then + * + * @param[in] buffer Input buffer with data to be written + * @param[in] bufferSize Maximum number of bytes to write from buffer + * @return Number of bytes success successfuly written or zero if output is blocked + * @throw WriteFailed + */ + virtual size_t Write(const BinaryQueue &buffer, size_t bufferSize) = 0; +}; +} // namespace DPL + +#endif // DPL_ABSTRACT_OUTPUT_H diff --git a/modules/core/include/dpl/abstract_waitable_input.h b/modules/core/include/dpl/abstract_waitable_input.h new file mode 100644 index 0000000..6b39ea7 --- /dev/null +++ b/modules/core/include/dpl/abstract_waitable_input.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_waitable_input.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of abstract waitable input + */ +#ifndef DPL_ABSTRACT_WAITABLE_INPUT_H +#define DPL_ABSTRACT_WAITABLE_INPUT_H + +#include +#include + +namespace DPL +{ + +class AbstractWaitableInput + : public AbstractInput +{ +public: + virtual ~AbstractWaitableInput() {} + + virtual WaitableHandle WaitableReadHandle() const = 0; +}; + +} // namespace DPL + +#endif // DPL_ABSTRACT_WAITABLE_INPUT_H diff --git a/modules/core/include/dpl/abstract_waitable_input_adapter.h b/modules/core/include/dpl/abstract_waitable_input_adapter.h new file mode 100644 index 0000000..a9d396a --- /dev/null +++ b/modules/core/include/dpl/abstract_waitable_input_adapter.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_waitable_input.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of abstract waitable input + */ +#ifndef DPL_ABSTRACT_WAITABLE_INPUT_ADAPTER_H +#define DPL_ABSTRACT_WAITABLE_INPUT_ADAPTER_H + +#include +#include +#include + +namespace DPL +{ + +class AbstractWaitableInputAdapter + : public AbstractWaitableInput +{ +private: + AbstractInput *m_input; + WaitableEvent m_waitableEvent; + +public: + explicit AbstractWaitableInputAdapter(AbstractInput *input); + + virtual BinaryQueueAutoPtr Read(size_t size); + + virtual WaitableHandle WaitableReadHandle() const; +}; + +} // namespace DPL + +#endif // DPL_ABSTRACT_WAITABLE_INPUT_ADAPTER_H diff --git a/modules/core/include/dpl/abstract_waitable_input_output.h b/modules/core/include/dpl/abstract_waitable_input_output.h new file mode 100644 index 0000000..3d3e6f2 --- /dev/null +++ b/modules/core/include/dpl/abstract_waitable_input_output.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_waitable_input_output.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of abstract waitable input output + */ +#ifndef DPL_ABSTRACT_WAITABLE_INPUT_OUTPUT_H +#define DPL_ABSTRACT_WAITABLE_INPUT_OUTPUT_H + +#include +#include + +namespace DPL +{ + +class AbstractWaitableInputOutput + : public AbstractWaitableInput, + public AbstractWaitableOutput +{ +public: + virtual ~AbstractWaitableInputOutput() {} +}; + +} // namespace DPL + +#endif // DPL_ABSTRACT_WAITABLE_INPUT_OUTPUT_H diff --git a/modules/core/include/dpl/abstract_waitable_input_output_adapter.h b/modules/core/include/dpl/abstract_waitable_input_output_adapter.h new file mode 100644 index 0000000..6ac2228 --- /dev/null +++ b/modules/core/include/dpl/abstract_waitable_input_output_adapter.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_waitable_input_output.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of abstract waitable input output + */ +#ifndef DPL_ABSTRACT_WAITABLE_INPUT_OUTPUT_ADAPTER_H +#define DPL_ABSTRACT_WAITABLE_INPUT_OUTPUT_ADAPTER_H + +#include +#include +#include + +namespace DPL +{ + +class AbstractWaitableInputOutputAdapter + : public AbstractWaitableInputAdapter, + public AbstractWaitableOutputAdapter +{ +public: + explicit AbstractWaitableInputOutputAdapter(AbstractInputOutput *inputOutput); +}; + +} // namespace DPL + +#endif // DPL_ABSTRACT_WAITABLE_INPUT_OUTPUT_ADAPTER_H diff --git a/modules/core/include/dpl/abstract_waitable_output.h b/modules/core/include/dpl/abstract_waitable_output.h new file mode 100644 index 0000000..6cc8d23 --- /dev/null +++ b/modules/core/include/dpl/abstract_waitable_output.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_waitable_output.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of abstract waitable output + */ +#ifndef DPL_ABSTRACT_WAITABLE_OUTPUT_H +#define DPL_ABSTRACT_WAITABLE_OUTPUT_H + +#include +#include + +namespace DPL +{ + +class AbstractWaitableOutput + : public AbstractOutput +{ +public: + virtual ~AbstractWaitableOutput() {} + + virtual WaitableHandle WaitableWriteHandle() const = 0; +}; + +} // namespace DPL + +#endif // DPL_ABSTRACT_WAITABLE_OUTPUT_H diff --git a/modules/core/include/dpl/abstract_waitable_output_adapter.h b/modules/core/include/dpl/abstract_waitable_output_adapter.h new file mode 100644 index 0000000..64fc3b3 --- /dev/null +++ b/modules/core/include/dpl/abstract_waitable_output_adapter.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_waitable_output.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of abstract waitable output + */ +#ifndef DPL_ABSTRACT_WAITABLE_OUTPUT_ADAPTER_H +#define DPL_ABSTRACT_WAITABLE_OUTPUT_ADAPTER_H + +#include +#include +#include + +namespace DPL +{ + +class AbstractWaitableOutputAdapter + : public AbstractWaitableOutput +{ +private: + AbstractOutput *m_output; + WaitableEvent m_waitableEvent; + +public: + explicit AbstractWaitableOutputAdapter(AbstractOutput *output); + + virtual size_t Write(const BinaryQueue &buffer, size_t bufferSize); + + virtual WaitableHandle WaitableWriteHandle() const; +}; + +} // namespace DPL + +#endif // DPL_ABSTRACT_WAITABLE_OUTPUT_ADAPTER_H diff --git a/modules/core/include/dpl/address.h b/modules/core/include/dpl/address.h new file mode 100644 index 0000000..7b1d8e8 --- /dev/null +++ b/modules/core/include/dpl/address.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file address.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of address + */ +#ifndef DPL_ADDRESS_H +#define DPL_ADDRESS_H + +#include +#include + +namespace DPL +{ +class Address +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, InvalidAddress) + }; + +private: + std::string m_address; + unsigned short m_port; + +public: + Address(); + Address(const std::string &address); + Address(const std::string &address, unsigned short port); + + virtual ~Address(); + + std::string GetAddress() const; + unsigned short GetPort() const; + + std::string ToString() const; + + bool operator<(const Address &addr) const; +}; + +} // namespace DPL + +#endif // DPL_ADDRESS_H diff --git a/modules/core/include/dpl/aligned.h b/modules/core/include/dpl/aligned.h new file mode 100644 index 0000000..902deb6 --- /dev/null +++ b/modules/core/include/dpl/aligned.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file aligned.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of aligned attribute from gcc + */ +#ifndef DPL_ALIGNED_H +#define DPL_ALIGNED_H + +#define DPL_ALIGNED(n) __attribute__((aligned(n))) + +#endif // DPL_ALIGNED_H diff --git a/modules/core/include/dpl/application.h b/modules/core/include/dpl/application.h new file mode 100644 index 0000000..f2c309e --- /dev/null +++ b/modules/core/include/dpl/application.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file application.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of MVC application support + */ +#ifndef DPL_APPLICATION_H +#define DPL_APPLICATION_H + +#include +#include +#include +#include +#include + +namespace DPL +{ +class Application +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, TooManyInstances) + DECLARE_EXCEPTION_TYPE(Base, FrameworkError) + }; + +private: + static int app_create(void *data); + static int app_terminate(void *data); + static int app_pause(void *data); + static int app_resume(void *data); + static int app_reset(bundle *b, void *data); + +protected: + int m_argc; + char **m_argv; + std::string m_applicationName; + + bool m_mainWindowVisible; + + virtual void OnCreate(); + virtual void OnStart(); + virtual void OnStop(); + virtual void OnResume(); + virtual void OnPause(); + virtual void OnRelaunch(); + virtual void OnReset(bundle *b); + virtual void OnTerminate(); + virtual void OnLowMemory(); + virtual void OnLowBattery(); + virtual void OnLanguageChanged(); + +public: + Application(int argc, char **argv, const std::string &applicationName, bool showMainWindow = true); + virtual ~Application(); + + /** + * @brief Execute application and start message processing + */ + virtual int Exec(); + + /* + * @brief Sends quit message to application message loop + */ + virtual void Quit(); +}; + +class ApplicationExt : public Application +{ +public: + ApplicationExt(int argc, char **argv, const std::string &applicationName, bool showMainWindow = true); + virtual ~ApplicationExt(); + + /** + * @brief Execute application and start message processing + */ + virtual int Exec(); + + /* + * @brief Sends quit message to application message loop + */ + virtual void Quit(); +private: + static DPL::Atomic m_useCount; +}; + +} // namespace DPL + +#endif // DPL_APPLICATION_H diff --git a/modules/core/include/dpl/apply.h b/modules/core/include/dpl/apply.h new file mode 100644 index 0000000..f441072 --- /dev/null +++ b/modules/core/include/dpl/apply.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file apply.h + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief Implementation file for Apply functionality. + */ +#ifndef DPL_APPLY_H_ +#define DPL_APPLY_H_ + +#ifndef __GXX_EXPERIMENTAL_CXX0X__ // C++11 compatibility check +# include +#else + +#include +#include + +namespace DPL +{ +enum class ExtraArgsInsertPolicy +{ + /** + * Extra arguments will be add at the end of the argument list + * passed to operation call. + */ + Append, + + /** + * Extra arguments will be add at the begining of the argument list + * passed to operation call. + */ + Prepend +}; + +template +struct _ApplyDispatchByPolicy; + +template +Result Apply(Operation op, + const std::tuple& t, + ArgsE && ... extra) +{ + return _ApplyDispatchByPolicy:: + template Apply(op, t, std::forward(extra) ...); +} + +template +struct _ApplyTuple +{ + template + static Result Apply(Operation op, + const std::tuple& t1, + const std::tuple& t2, + Args && ... args) + { + return _ApplyTuple:: + template Apply(op, + t1, + t2, + std::get(t1), + std::forward(args) ...); + } +}; + +template +struct _ApplyTuple<0, M> +{ + template + static Result Apply(Operation op, + const std::tuple& t1, + const std::tuple& t2, + Args && ... args) + { + return _ApplyTuple<0, M - 1>:: + template Apply(op, + t1, + t2, + std::get(t2), + std::forward(args) ...); + } +}; + +template<> +struct _ApplyTuple<0, 0> +{ + template + static Result Apply(Operation op, + const std::tuple&, + const std::tuple&, + Args && ... args) + { + return op(std::forward(args) ...); + } +}; + +template +struct _ApplyArgs +{ + template + static Result Apply(Operation op, + const std::tuple& t, + Args && ... args) + { + return _ApplyArgs:: + template Apply(op, + t, + std::get(t), + std::forward(args) ...); + } +}; + +template<> +struct _ApplyArgs<0> +{ + template + static Result Apply(Operation op, + const std::tuple&, + Args && ... args) + { + return op(std::forward(args) ...); + } +}; + +template<> +struct _ApplyDispatchByPolicy +{ + template + static Result Apply(Operation op, + const std::tuple& t, + ArgsE && ... extra) + { + return _ApplyArgs:: + template Apply(op, + t, + std::forward(extra) ...); + } +}; + +template<> +struct _ApplyDispatchByPolicy +{ + template + static Result Apply(Operation op, + const std::tuple& t, + ArgsE && ... extra) + { + return _ApplyTuple:: + template Apply(op, + t, + std::make_tuple(std::forward(extra) ...)); + } +}; +} // namespace DPL + +#endif // __GXX_EXPERIMENTAL_CXX0X__ + +#endif // DPL_APPLY_H_ diff --git a/modules/core/include/dpl/assert.h b/modules/core/include/dpl/assert.h new file mode 100644 index 0000000..f0b62f7 --- /dev/null +++ b/modules/core/include/dpl/assert.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file assert.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of assert + */ +#ifndef DPL_ASSERT_H +#define DPL_ASSERT_H + +#include + +namespace DPL +{ +// Assertion handler procedure +// Do not call directly +// Always use Assert macro +DPL_NORETURN void AssertProc(const char *condition, const char *file, int line, const char *function); +} // namespace DPL + +#define Assert(Condition) do { if (!(Condition)) DPL::AssertProc(#Condition, __FILE__, __LINE__, __FUNCTION__); } while (0) + +#endif // DPL_ASSERT_H diff --git a/modules/core/include/dpl/atomic.h b/modules/core/include/dpl/atomic.h new file mode 100644 index 0000000..33ea8a9 --- /dev/null +++ b/modules/core/include/dpl/atomic.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file atomic.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of atomic + */ +#ifndef DPL_ATOMIC_H +#define DPL_ATOMIC_H + +#pragma GCC system_header +#include + +namespace DPL +{ +class Atomic +{ +public: + typedef gint ValueType; + +private: + volatile ValueType m_value; + +public: + Atomic(ValueType value = static_cast(0)); + + ValueType ExchangeAndAdd(ValueType value); + bool CompareAndExchange(ValueType oldValue, ValueType newValue); + bool operator--(); + void operator++(); + + operator ValueType() const; +}; +} // namespace DPL + +#endif // DPL_ATOMIC_H diff --git a/modules/core/include/dpl/binary_queue.h b/modules/core/include/dpl/binary_queue.h new file mode 100644 index 0000000..ae85ceb --- /dev/null +++ b/modules/core/include/dpl/binary_queue.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file binary_queue.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of binary queue + */ +#ifndef DPL_BINARY_QUEUE_H +#define DPL_BINARY_QUEUE_H + +#include +#include +#include +#include +#include + +namespace DPL +{ +/** + * Binary stream implemented as constant size bucket list + * + * @todo Add optimized implementation for FlattenConsume + */ +class BinaryQueue + : public AbstractInputOutput +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, OutOfData) + }; + + typedef void (*BufferDeleter)(const void *buffer, size_t bufferSize, void *userParam); + static void BufferDeleterFree(const void *buffer, size_t bufferSize, void *userParam); + + class BucketVisitor + { + public: + /** + * Destructor + */ + virtual ~BucketVisitor(); + + /** + * Visit bucket + * + * @return none + * @param[in] buffer Constant pointer to bucket data buffer + * @param[in] bufferSize Number of bytes in bucket + */ + virtual void OnVisitBucket(const void *buffer, size_t bufferSize) = 0; + }; + +private: + struct Bucket + : private Noncopyable + { + const void *buffer; + const void *ptr; + size_t size; + size_t left; + + BufferDeleter deleter; + void *param; + + Bucket(const void *buffer, size_t bufferSize, BufferDeleter deleter, void *userParam); + virtual ~Bucket(); + }; + + typedef std::list BucketList; + BucketList m_buckets; + size_t m_size; + + static void DeleteBucket(Bucket *bucket); + + class BucketVisitorCall + { + private: + BucketVisitor *m_visitor; + + public: + BucketVisitorCall(BucketVisitor *visitor); + virtual ~BucketVisitorCall(); + + void operator()(Bucket *bucket) const; + }; + +public: + /** + * Construct empty binary queue + */ + BinaryQueue(); + + /** + * Construct binary queue via bare copy of other binary queue + * + * @param[in] other Other binary queue to copy from + * @warning One cannot assume that bucket structure is preserved during copy + */ + BinaryQueue(const BinaryQueue &other); + + /** + * Destructor + */ + virtual ~BinaryQueue(); + + /** + * Construct binary queue via bare copy of other binary queue + * + * @param[in] other Other binary queue to copy from + * @warning One cannot assume that bucket structure is preserved during copy + */ + const BinaryQueue &operator=(const BinaryQueue &other); + + /** + * Append copy of @a bufferSize bytes from memory pointed by @a buffer + * to the end of binary queue. Uses default deleter based on free. + * + * @return none + * @param[in] buffer Pointer to buffer to copy data from + * @param[in] bufferSize Number of bytes to copy + * @exception std::bad_alloc Cannot allocate memory to hold additional data + * @see BinaryQueue::BufferDeleterFree + */ + void AppendCopy(const void *buffer, size_t bufferSize); + + /** + * Append @a bufferSize bytes from memory pointed by @a buffer + * to the end of binary queue. Uses custom provided deleter. + * Responsibility for deleting provided buffer is transfered to BinaryQueue. + * + * @return none + * @param[in] buffer Pointer to data buffer + * @param[in] bufferSize Number of bytes available in buffer + * @param[in] deleter Pointer to deleter procedure used to free provided buffer + * @param[in] userParam User parameter passed to deleter routine + * @exception std::bad_alloc Cannot allocate memory to hold additional data + */ + void AppendUnmanaged(const void *buffer, size_t bufferSize, BufferDeleter deleter = &BinaryQueue::BufferDeleterFree, void *userParam = NULL); + + /** + * Append copy of other binary queue to the end of this binary queue + * + * @return none + * @param[in] other Constant reference to other binary queue to copy data from + * @exception std::bad_alloc Cannot allocate memory to hold additional data + * @warning One cannot assume that bucket structure is preserved during copy + */ + void AppendCopyFrom(const BinaryQueue &other); + + /** + * Move bytes from other binary queue to the end of this binary queue. + * This also removes all bytes from other binary queue. + * This method is designed to be as fast as possible (only pointer swaps) + * and is suggested over making copies of binary queues. + * Bucket structure is preserved after operation. + * + * @return none + * @param[in] other Reference to other binary queue to move data from + * @exception std::bad_alloc Cannot allocate memory to hold additional data + */ + void AppendMoveFrom(BinaryQueue &other); + + /** + * Append copy of binary queue to the end of other binary queue + * + * @return none + * @param[in] other Constant reference to other binary queue to copy data to + * @exception std::bad_alloc Cannot allocate memory to hold additional data + * @warning One cannot assume that bucket structure is preserved during copy + */ + void AppendCopyTo(BinaryQueue &other) const; + + /** + * Move bytes from binary queue to the end of other binary queue. + * This also removes all bytes from binary queue. + * This method is designed to be as fast as possible (only pointer swaps) + * and is suggested over making copies of binary queues. + * Bucket structure is preserved after operation. + * + * @return none + * @param[in] other Reference to other binary queue to move data to + * @exception std::bad_alloc Cannot allocate memory to hold additional data + */ + void AppendMoveTo(BinaryQueue &other); + + /** + * Retrieve total size of all data contained in binary queue + * + * @return Number of bytes in binary queue + */ + size_t Size() const; + + /** + * Remove all data from binary queue + * + * @return none + */ + void Clear(); + + /** + * Check if binary queue is empty + * + * @return true if binary queue is empty, false otherwise + */ + bool Empty() const; + + /** + * Remove @a size bytes from beginning of binary queue + * + * @return none + * @param[in] size Number of bytes to remove + * @exception BinaryQueue::Exception::OutOfData Number of bytes is larger + * than available bytes in binary queue + */ + void Consume(size_t size); + + /** + * Retrieve @a bufferSize bytes from beginning of binary queue and copy them + * to user supplied buffer + * + * @return none + * @param[in] buffer Pointer to user buffer to receive bytes + * @param[in] bufferSize Size of user buffer pointed by @a buffer + * @exception BinaryQueue::Exception::OutOfData Number of bytes to flatten + * is larger than available bytes in binary queue + */ + void Flatten(void *buffer, size_t bufferSize) const; + + /** + * Retrieve @a bufferSize bytes from beginning of binary queue, copy them + * to user supplied buffer, and remove from binary queue + * + * @return none + * @param[in] buffer Pointer to user buffer to receive bytes + * @param[in] bufferSize Size of user buffer pointed by @a buffer + * @exception BinaryQueue::Exception::OutOfData Number of bytes to flatten + * is larger than available bytes in binary queue + */ + void FlattenConsume(void *buffer, size_t bufferSize); + + /** + * Visit each buffer with data using visitor object + * + * @return none + * @param[in] visitor Pointer to bucket visitor + * @see BinaryQueue::BucketVisitor + */ + void VisitBuckets(BucketVisitor *visitor) const; + + /** + * IAbstractInput interface + */ + virtual BinaryQueueAutoPtr Read(size_t size); + + /** + * IAbstractOutput interface + */ + virtual size_t Write(const BinaryQueue &buffer, size_t bufferSize); +}; + +/** + * Binary queue auto pointer + */ +typedef std::auto_ptr BinaryQueueAutoPtr; +} // namespace DPL + +#endif // DPL_BINARY_QUEUE_H diff --git a/modules/core/include/dpl/bool_operator.h b/modules/core/include/dpl/bool_operator.h new file mode 100644 index 0000000..ccb17f8 --- /dev/null +++ b/modules/core/include/dpl/bool_operator.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file bool_operator.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of bool operator + */ +#ifndef DPL_BOOL_OPERATOR_H +#define DPL_BOOL_OPERATOR_H + +#define DPL_IMPLEMENT_BOOL_OPERATOR(Type, ThisType, CheckPtr, ClassPtr) \ + typedef Type *ThisType::*UnknownBoolType; \ + \ + operator UnknownBoolType() const \ + { \ + return (CheckPtr == NULL) ? NULL : &ThisType::ClassPtr; \ + } \ + \ + bool operator !() const \ + { \ + return CheckPtr == NULL; \ + } + +#endif // DPL_BOOL_OPERATOR_H diff --git a/modules/core/include/dpl/char_traits.h b/modules/core/include/dpl/char_traits.h new file mode 100644 index 0000000..1b50c51 --- /dev/null +++ b/modules/core/include/dpl/char_traits.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file char_traits.h + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @version 1.0 + * @brief Char traits are used to create basic_string extended with additional features + * Current char traits could be extended in feature to boost performance + */ +#ifndef DPL_CHAR_TRAITS +#define DPL_CHAR_TRAITS + +#include +#include +#include +#include +#include + +namespace DPL +{ + +typedef std::char_traits CharTraits; + +} // namespace DPL + +#endif // DPL_CHAR_TRAITS diff --git a/modules/core/include/dpl/colors.h b/modules/core/include/dpl/colors.h new file mode 100644 index 0000000..efc104d --- /dev/null +++ b/modules/core/include/dpl/colors.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file colors.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief Some constants with definition of colors for Console + * and html output + */ + +#ifndef DPL_COLORS_H +#define DPL_COLORS_H + +namespace DPL +{ + +namespace Colors +{ + +namespace Text +{ + +extern const char* BOLD_GREEN_BEGIN; +extern const char* BOLD_GREEN_END; +extern const char* PURPLE_BEGIN; +extern const char* PURPLE_END; +extern const char* RED_BEGIN; +extern const char* RED_END; +extern const char* GREEN_BEGIN; +extern const char* GREEN_END; +extern const char* CYAN_BEGIN; +extern const char* CYAN_END; +extern const char* BOLD_RED_BEGIN; +extern const char* BOLD_RED_END; +extern const char* BOLD_YELLOW_BEGIN; +extern const char* BOLD_YELLOW_END; +extern const char* BOLD_GOLD_BEGIN; +extern const char* BOLD_GOLD_END; +extern const char* BOLD_WHITE_BEGIN; +extern const char* BOLD_WHITE_END; + +} //namespace Text + +namespace Html +{ + +extern const char* BOLD_GREEN_BEGIN; +extern const char* BOLD_GREEN_END; +extern const char* PURPLE_BEGIN; +extern const char* PURPLE_END; +extern const char* RED_BEGIN; +extern const char* RED_END; +extern const char* GREEN_BEGIN; +extern const char* GREEN_END; +extern const char* CYAN_BEGIN; +extern const char* CYAN_END; +extern const char* BOLD_RED_BEGIN; +extern const char* BOLD_RED_END; +extern const char* BOLD_YELLOW_BEGIN; +extern const char* BOLD_YELLOW_END; +extern const char* BOLD_GOLD_BEGIN; +extern const char* BOLD_GOLD_END; +extern const char* BOLD_WHITE_BEGIN; +extern const char* BOLD_WHITE_END; + +} //namespace Html + +} //namespace Colors + +} //namespace DPL + +#endif /* DPL_COLORS_H */ diff --git a/modules/core/include/dpl/copy.h b/modules/core/include/dpl/copy.h new file mode 100644 index 0000000..d91fbbc --- /dev/null +++ b/modules/core/include/dpl/copy.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file copy.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of copy + */ +#ifndef DPL_COPY_H +#define DPL_COPY_H + +#include +#include +#include + +namespace DPL +{ +/** + * Copy failed exception + */ +DECLARE_EXCEPTION_TYPE(Exception, CopyFailed) + +/** + * Copy all bytes abstract waitable input to abstract waitable output + * + * @param[in] input Abstract waitable input to copy from + * @param[in] output Abstract waitable output to copy to + * @throw CopyFailed An error occurred while copying. Look into exception trace for details. + */ +void Copy(AbstractWaitableInput *input, AbstractWaitableOutput *output); + +/** + * Copy exactly totalBytes bytes abstract waitable input to abstract waitable output + * + * @param[in] input Abstract waitable input to copy from + * @param[in] output Abstract waitable output to copy to + * @throw CopyFailed An error occurred while copying. Look into exception trace for details. + */ +void Copy(AbstractWaitableInput *input, AbstractWaitableOutput *output, size_t totalBytes); +} // namespace DPL + +#endif // DPL_COPY_H diff --git a/modules/core/include/dpl/enable_shared_from_this.h b/modules/core/include/dpl/enable_shared_from_this.h new file mode 100644 index 0000000..ed84130 --- /dev/null +++ b/modules/core/include/dpl/enable_shared_from_this.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file enable_shared_from_this.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of shared pointer RAII + */ +#ifndef DPL_ENABLE_SHARED_FROM_THIS_H +#define DPL_ENABLE_SHARED_FROM_THIS_H + +#include +#include +#include + +namespace DPL +{ +template +class EnableSharedFromThis : private Noncopyable +{ + +private: + // A weak pointer to shared counter + SharedCounter *m_counter; + Class *m_ptr; + +public: + DPL::SharedPtr SharedFromThis() + { + Assert(m_counter != NULL && "Pointer is not shared!"); + return SharedPtr(m_counter, m_ptr); + } + + DPL::SharedPtr SharedFromThis() const + { + Assert(m_counter != NULL && "Pointer is not shared!"); + return SharedPtr(m_counter, m_ptr); + } + + // For internal SharedPtr usage only. Do not call directly. + void _Internal_AcceptSharedPtr(SharedCounter *counter, Class *ptr) + { + m_counter = counter; + m_ptr = ptr; + } + + EnableSharedFromThis() + : m_counter(NULL), + m_ptr(NULL) + { + } + + virtual ~EnableSharedFromThis() + { + } +}; +} // namespace DPL + +#endif // DPL_ENABLE_SHARED_FROM_THIS_H diff --git a/modules/core/include/dpl/errno_string.h b/modules/core/include/dpl/errno_string.h new file mode 100644 index 0000000..d3da348 --- /dev/null +++ b/modules/core/include/dpl/errno_string.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file errno_string.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of errno string + */ +#ifndef DPL_ERRNO_STRING_H +#define DPL_ERRNO_STRING_H + +#include +#include +#include + +namespace DPL +{ +DECLARE_EXCEPTION_TYPE(DPL::Exception, InvalidErrnoValue) + +std::string GetErrnoString(int error = errno); +} // namespace DPL + +#endif // DPL_ERRNO_STRING_H diff --git a/modules/core/include/dpl/exception.h b/modules/core/include/dpl/exception.h new file mode 100644 index 0000000..a2e3dcf --- /dev/null +++ b/modules/core/include/dpl/exception.h @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file exception.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Header file for base exception + */ +#ifndef DPL_EXCEPTION_H +#define DPL_EXCEPTION_H + +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +void LogUnhandledException(const std::string &str); +void LogUnhandledException(const std::string &str, const char *filename, int line, const char *function); +} + +namespace DPL +{ +class Exception +{ +private: + static unsigned int m_exceptionCount; + static Exception* m_lastException; + static void (*m_terminateHandler)(); + + static void AddRef(Exception* exception) + { + if (!m_exceptionCount) + m_terminateHandler = std::set_terminate(&TerminateHandler); + + ++m_exceptionCount; + m_lastException = exception; + } + + static void UnRef(Exception* e) + { + if (m_lastException == e) + m_lastException = NULL; + + --m_exceptionCount; + + if (!m_exceptionCount) + { + std::set_terminate(m_terminateHandler); + m_terminateHandler = NULL; + } + } + + static void TerminateHandler() + { + if (m_lastException != NULL) + { + DisplayKnownException(*m_lastException); + abort(); + } + else + { + DisplayUnknownException(); + abort(); + } + } + + Exception *m_reason; + std::string m_path; + std::string m_function; + int m_line; + +protected: + std::string m_message; + std::string m_className; + +public: + static std::string KnownExceptionToString(const Exception &e) + { + std::ostringstream message; + message << "\033[1;5;31m\n=== Unhandled DPL exception occurred ===\033[m\n\n"; + message << "\033[1;33mException trace:\033[m\n\n"; + message << e.DumpToString(); + message << "\033[1;31m\n=== Will now abort ===\033[m\n"; + + return message.str(); + } + + static std::string UnknownExceptionToString() + { + std::ostringstream message; + message << "\033[1;5;31m\n=== Unhandled non-DPL exception occurred ===\033[m\n\n"; + message << "\033[1;31m\n=== Will now abort ===\033[m\n"; + + return message.str(); + } + + static void DisplayKnownException(const Exception& e) + { + LogUnhandledException(KnownExceptionToString(e).c_str()); + } + + static void DisplayUnknownException() + { + LogUnhandledException(UnknownExceptionToString().c_str()); + } + + Exception(const Exception &other) + { + // Deep copy + if (other.m_reason != NULL) + m_reason = new Exception(*other.m_reason); + else + m_reason = NULL; + + m_message = other.m_message; + m_path = other.m_path; + m_function = other.m_function; + m_line = other.m_line; + + m_className = other.m_className; + + AddRef(this); + } + + const Exception &operator =(const Exception &other) + { + if (this == &other) + return *this; + + // Deep copy + if (other.m_reason != NULL) + m_reason = new Exception(*other.m_reason); + else + m_reason = NULL; + + m_message = other.m_message; + m_path = other.m_path; + m_function = other.m_function; + m_line = other.m_line; + + m_className = other.m_className; + + AddRef(this); + + return *this; + } + + Exception(const char *path, const char *function, int line, const std::string &message) + : m_reason(NULL), + m_path(path), + m_function(function), + m_line(line), + m_message(message) + { + AddRef(this); + } + + Exception(const char *path, const char *function, int line, const Exception &reason, const std::string &message) + : m_reason(new Exception(reason)), + m_path(path), + m_function(function), + m_line(line), + m_message(message) + { + AddRef(this); + } + + virtual ~Exception() throw() + { + if (m_reason != NULL) + { + delete m_reason; + m_reason = NULL; + } + + UnRef(this); + } + + void Dump() const + { + // Show reason first + if (m_reason != NULL) + m_reason->Dump(); + + // Afterward, dump exception + const char *file = strchr(m_path.c_str(), '/'); + + if (file == NULL) + file = m_path.c_str(); + else + ++file; + + printf("\033[0;36m[%s:%i]\033[m %s() \033[4;35m%s\033[m: %s\033[m\n", + file, m_line, + m_function.c_str(), + m_className.c_str(), + m_message.empty() ? "" : m_message.c_str()); + } + + std::string DumpToString() const + { + std::string ret; + if (m_reason != NULL) + ret = m_reason->DumpToString(); + + const char *file = strchr(m_path.c_str(), '/'); + + if (file == NULL) + file = m_path.c_str(); + else + ++file; + + char buf[1024]; + snprintf(buf, sizeof(buf), "\033[0;36m[%s:%i]\033[m %s() \033[4;35m%s\033[m: %s\033[m\n", + file, m_line, + m_function.c_str(), + m_className.c_str(), + m_message.empty() ? "" : m_message.c_str()); + + buf[sizeof(buf)-1] = '\n'; + ret += buf; + + return ret; + } + + Exception *GetReason() const + { + return m_reason; + } + + std::string GetPath() const + { + return m_path; + } + + std::string GetFunction() const + { + return m_function; + } + + int GetLine() const + { + return m_line; + } + + std::string GetMessage() const + { + return m_message; + } + + std::string GetClassName() const + { + return m_className; + } +}; +} // namespace DPL + +#define Try try + +#define Throw(ClassName) \ + throw ClassName(__FILE__, __FUNCTION__, __LINE__) + +#define ThrowMsg(ClassName, Message) \ + do \ + { \ + std::ostringstream dplLoggingStream; \ + dplLoggingStream << Message; \ + throw ClassName(__FILE__, __FUNCTION__, __LINE__, dplLoggingStream.str()); \ + }while(0) + +#define ReThrow(ClassName) \ + throw ClassName(__FILE__, __FUNCTION__, __LINE__, _rethrown_exception) + +#define ReThrowMsg(ClassName, Message) \ + throw ClassName(__FILE__, __FUNCTION__, __LINE__, _rethrown_exception, Message) + +#define Catch(ClassName) \ + catch (const ClassName &_rethrown_exception) + +#define DECLARE_EXCEPTION_TYPE(BaseClass, Class) \ + class Class \ + : public BaseClass \ + { \ + public: \ + Class(const char *path, const char *function, int line, const std::string &message = std::string()) \ + : BaseClass(path, function, line, message) \ + { \ + BaseClass::m_className = #Class; \ + } \ + \ + Class(const char *path, const char *function, int line, const DPL::Exception &reason, const std::string &message = std::string()) \ + : BaseClass(path, function, line, reason, message) \ + { \ + BaseClass::m_className = #Class; \ + } \ + }; + +#define UNHANDLED_EXCEPTION_HANDLER_BEGIN try + +#define UNHANDLED_EXCEPTION_HANDLER_END \ + catch (const DPL::Exception &exception) \ + { \ + std::ostringstream msg; \ + msg << DPL::Exception::KnownExceptionToString(exception); \ + DPL::LogUnhandledException(msg.str(), __FILE__, __LINE__, __FUNCTION__); \ + abort(); \ + } \ + catch (std::exception& e) \ + { \ + std::ostringstream msg; \ + msg << e.what(); \ + msg << "\n"; \ + msg << DPL::Exception::UnknownExceptionToString(); \ + DPL::LogUnhandledException(msg.str(), __FILE__, __LINE__, __FUNCTION__); \ + abort(); \ + } \ + catch (...) \ + { \ + std::ostringstream msg; \ + msg << DPL::Exception::UnknownExceptionToString(); \ + DPL::LogUnhandledException(msg.str(), __FILE__, __LINE__, __FUNCTION__); \ + abort(); \ + } + +namespace DPL +{ +namespace CommonException +{ +/** + * Internal exception definitions + * + * These should normally not happen. + * Usually, exception trace with internal error includes + * important messages. + */ +DECLARE_EXCEPTION_TYPE(Exception, InternalError) ///< Unexpected error from underlying libraries or kernel +} +} + +#endif // DPL_EXCEPTION_H diff --git a/modules/core/include/dpl/fast_delegate.h b/modules/core/include/dpl/fast_delegate.h new file mode 100644 index 0000000..501c801 --- /dev/null +++ b/modules/core/include/dpl/fast_delegate.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file fast_delegate.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of fast delegate + */ +#ifndef DPL_FAST_DELEGATE_H +#define DPL_FAST_DELEGATE_H + +#pragma GCC system_header + +#define FASTDELEGATE_USESTATICFUNCTIONHACK +#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX + +#include +#include + +namespace DPL +{ +using namespace fastdelegate; +} // namespace DPL + +#endif // DPL_FAST_DELEGATE_H diff --git a/modules/core/include/dpl/file_input.h b/modules/core/include/dpl/file_input.h new file mode 100644 index 0000000..d1e4641 --- /dev/null +++ b/modules/core/include/dpl/file_input.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file file_input.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of file input + */ +#ifndef DPL_FILE_INPUT_H +#define DPL_FILE_INPUT_H + +#include +#include +#include + +namespace DPL +{ +class FileInput + : private Noncopyable, + public AbstractWaitableInput +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, OpenFailed) + DECLARE_EXCEPTION_TYPE(Base, CloseFailed) + }; + +protected: + int m_fd; + +public: + FileInput(); + FileInput(const std::string &fileName); + virtual ~FileInput(); + + void Open(const std::string &fileName); + void Close(); + + // AbstractInput + virtual BinaryQueueAutoPtr Read(size_t size); + + // AbstractWaitableInput + virtual WaitableHandle WaitableReadHandle() const; +}; +} // namespace DPL + +#endif // DPL_FILE_INPUT_H diff --git a/modules/core/include/dpl/file_input_mapping.h b/modules/core/include/dpl/file_input_mapping.h new file mode 100644 index 0000000..fd2d206 --- /dev/null +++ b/modules/core/include/dpl/file_input_mapping.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file file_input_mapping.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of file input mapping + */ +#ifndef DPL_FILE_INPUT_MAPPING_H +#define DPL_FILE_INPUT_MAPPING_H + +#include +#include + +namespace DPL +{ +class FileInputMapping + : private Noncopyable +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, OpenFailed) + }; + +private: + int m_handle; + off64_t m_size; + unsigned char *m_address; + +public: + /** + * Constructor + */ + explicit FileInputMapping(const std::string &fileName); + + /** + * Destructor + */ + ~FileInputMapping(); + + /** + * Get file mapping total size + * + * @return 64-bit size + */ + off64_t GetSize() const; + + /** + * Get file mapping base address + * + * @return Base address of file mapping + */ + const unsigned char *GetAddress() const; +}; +} // namespace DPL + +#endif // DPL_FILE_INPUT_MAPPING_H diff --git a/modules/core/include/dpl/file_output.h b/modules/core/include/dpl/file_output.h new file mode 100644 index 0000000..48532dc --- /dev/null +++ b/modules/core/include/dpl/file_output.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file file_output.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of file output + */ +#ifndef DPL_FILE_OUTPUT_H +#define DPL_FILE_OUTPUT_H + +#include +#include +#include + +namespace DPL +{ +class FileOutput + : private Noncopyable, + public AbstractWaitableOutput +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, OpenFailed) + DECLARE_EXCEPTION_TYPE(Base, CloseFailed) + }; + +protected: + int m_fd; + +public: + FileOutput(); + FileOutput(const std::string &fileName); + virtual ~FileOutput(); + + void Open(const std::string &fileName); + void Close(); + + // AbstractOutput + virtual size_t Write(const BinaryQueue &buffer, size_t bufferSize); + + // AbstracWaitableOutput + virtual WaitableHandle WaitableWriteHandle() const; +}; +} // namespace DPL + +#endif // DPL_FILE_OUTPUT_H diff --git a/modules/core/include/dpl/foreach.h b/modules/core/include/dpl/foreach.h new file mode 100644 index 0000000..0923609 --- /dev/null +++ b/modules/core/include/dpl/foreach.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file foreach.h + * @author Bartosz Janiak (b.janiak@samsung.com) + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of foreach macro for stl containers + */ +#ifndef DPL_FOREACH_H +#define DPL_FOREACH_H + +#include + +namespace DPL +{ +namespace Private +{ + +/* + * Used to detect type of valid reference to value object. + */ +template +T& ValueReference(T& t) +{ + return(t); +} + +template +const T& ValueReference(const T& t) +{ + return(t); +} + + +} //Private +} //DPL + + +#define DPL_FOREACH_IMPL(temporaryName, iterator, container) \ + __typeof__ (DPL::Private::ValueReference((container))) & \ + temporaryName = (container); \ + for (__typeof__ (temporaryName.begin()) iterator = \ + temporaryName.begin(); \ + (iterator) != temporaryName.end(); ++iterator) + +#define FOREACH(iterator, container) \ + DPL_FOREACH_IMPL( \ + DPL_MACRO_CONCAT(foreachContainerReference, __COUNTER__), \ + iterator, \ + container) + +#endif // DPL_FOREACH_H diff --git a/modules/core/include/dpl/framework_appcore.h b/modules/core/include/dpl/framework_appcore.h new file mode 100644 index 0000000..034134a --- /dev/null +++ b/modules/core/include/dpl/framework_appcore.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file framework_appcore.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the forward header file for APPCORE framework + */ +#pragma GCC system_header +#include diff --git a/modules/core/include/dpl/framework_efl.h b/modules/core/include/dpl/framework_efl.h new file mode 100644 index 0000000..6246587 --- /dev/null +++ b/modules/core/include/dpl/framework_efl.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file framework_efl.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the forward header file for EFL framework + */ +#pragma GCC system_header +#include +#include +#include diff --git a/modules/core/include/dpl/framework_vconf.h b/modules/core/include/dpl/framework_vconf.h new file mode 100644 index 0000000..8de9b31 --- /dev/null +++ b/modules/core/include/dpl/framework_vconf.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file framework_vconf.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the forward header file for VCONF + */ +#pragma GCC system_header +#include +#include diff --git a/modules/core/include/dpl/generic_event.h b/modules/core/include/dpl/generic_event.h new file mode 100644 index 0000000..371717a --- /dev/null +++ b/modules/core/include/dpl/generic_event.h @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_event.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of MVC generic event + */ +#ifndef DPL_GENERIC_EVENT_H +#define DPL_GENERIC_EVENT_H + +namespace DPL +{ + +class EventSender +{ +public: + explicit EventSender(void *sender) + : m_sender(sender) + { + } + + void* GetSender() const + { + return m_sender; + } + +private: + void *m_sender; +}; + +class GenericEvent +{ +protected: + void *m_sender; + +public: + explicit GenericEvent(const EventSender &sender) + : m_sender(sender.GetSender()) + { + } + + virtual ~GenericEvent() + { + } + + void *GetSender() const + { + return m_sender; + } +}; + +class GenericEvent0 + : public GenericEvent +{ +public: + explicit GenericEvent0(const EventSender &sender) + : GenericEvent(sender) + { + } + + virtual ~GenericEvent0() + { + } + +}; + +template +class GenericEvent1 + : public GenericEvent0 +{ +public: + typedef Arg0Type Arg0; + +protected: + Arg0 m_arg0; + +public: + explicit GenericEvent1(const EventSender &sender) + : GenericEvent0(sender) + { + } + + GenericEvent1(Arg0 arg0, const EventSender &sender) + : GenericEvent0(sender), + m_arg0(arg0) + { + } + + virtual ~GenericEvent1() + { + } + + Arg0 GetArg0() const + { + return m_arg0; + } +}; + +template +class GenericEvent2 + : public GenericEvent1 +{ +public: + typedef Arg0Type Arg0; + typedef Arg1Type Arg1; + +protected: + Arg1 m_arg1; + +public: + explicit GenericEvent2(const EventSender &sender) + : GenericEvent1(sender) + { + } + + GenericEvent2(Arg0 arg0, Arg1 arg1, const EventSender &sender) + : GenericEvent1(arg0, sender), + m_arg1(arg1) + { + } + + virtual ~GenericEvent2() + { + } + + Arg1 GetArg1() const + { + return m_arg1; + } +}; + +template +class GenericEvent3 + : public GenericEvent2 +{ +public: + typedef Arg0Type Arg0; + typedef Arg1Type Arg1; + typedef Arg2Type Arg2; + +protected: + Arg2 m_arg2; + +public: + explicit GenericEvent3(const EventSender &sender) + : GenericEvent2(sender) + { + } + + GenericEvent3(Arg0 arg0, Arg1 arg1, Arg2 arg2, const EventSender &sender) + : GenericEvent2(arg0, arg1, sender), + m_arg2(arg2) + { + } + + virtual ~GenericEvent3() + { + } + + Arg2 GetArg2() const + { + return m_arg2; + } +}; + +template +class GenericEvent4 + : public GenericEvent3 +{ +public: + typedef Arg0Type Arg0; + typedef Arg1Type Arg1; + typedef Arg2Type Arg2; + typedef Arg3Type Arg3; + +protected: + Arg3 m_arg3; + +public: + explicit GenericEvent4(const EventSender &sender) + : GenericEvent3(sender) + { + } + + GenericEvent4(Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3, const EventSender &sender) + : GenericEvent3(arg0, arg1, arg2, sender), + m_arg3(arg3) + { + } + + virtual ~GenericEvent4() + { + } + + Arg3 GetArg3() const + { + return m_arg3; + } +}; + +template +class GenericEvent5 + : public GenericEvent4 +{ +public: + typedef Arg0Type Arg0; + typedef Arg1Type Arg1; + typedef Arg2Type Arg2; + typedef Arg3Type Arg3; + typedef Arg4Type Arg4; + +protected: + Arg4 m_arg4; + +public: + explicit GenericEvent5(const EventSender &sender) + : GenericEvent4(sender) + { + } + + GenericEvent5(Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, const EventSender &sender) + : GenericEvent4(arg0, arg1, arg2, arg3, sender), + m_arg4(arg4) + { + } + + virtual ~GenericEvent5() + { + } + + Arg4 GetArg4() const + { + return m_arg4; + } +}; + +template +class GenericEvent6 + : public GenericEvent5 +{ +public: + typedef Arg0Type Arg0; + typedef Arg1Type Arg1; + typedef Arg2Type Arg2; + typedef Arg3Type Arg3; + typedef Arg4Type Arg4; + typedef Arg5Type Arg5; + +protected: + Arg5 m_arg5; + +public: + explicit GenericEvent6(const EventSender &sender) + : GenericEvent5(sender) + { + } + + GenericEvent6(Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, const EventSender &sender) + : GenericEvent5(arg0, arg1, arg2, arg3, arg4, sender), + m_arg5(arg5) + { + } + + virtual ~GenericEvent6() + { + } + + Arg5 GetArg5() const + { + return m_arg5; + } +}; + +template +class GenericEvent7 + : public GenericEvent6 +{ +public: + typedef Arg0Type Arg0; + typedef Arg1Type Arg1; + typedef Arg2Type Arg2; + typedef Arg3Type Arg3; + typedef Arg4Type Arg4; + typedef Arg5Type Arg5; + typedef Arg6Type Arg6; + +protected: + Arg6 m_arg6; + +public: + explicit GenericEvent7(const EventSender &sender) + : GenericEvent6(sender) + { + } + + GenericEvent7(Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6, const EventSender &sender) + : GenericEvent6(arg0, arg1, arg2, arg3, arg4, arg5, sender), + m_arg6(arg6) + { + } + + virtual ~GenericEvent7() + { + } + + Arg6 GetArg6() const + { + return m_arg6; + } +}; + +template +class GenericEvent8 + : public GenericEvent7 +{ +public: + typedef Arg0Type Arg0; + typedef Arg1Type Arg1; + typedef Arg2Type Arg2; + typedef Arg3Type Arg3; + typedef Arg4Type Arg4; + typedef Arg5Type Arg5; + typedef Arg6Type Arg6; + typedef Arg7Type Arg7; + +protected: + Arg7 m_arg7; + +public: + explicit GenericEvent8(const EventSender &sender) + : GenericEvent7(sender) + { + } + + GenericEvent8(Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6, Arg7 arg7, const EventSender &sender) + : GenericEvent7(arg0, arg1, arg2, arg3, arg4, arg5, arg6, sender), + m_arg7(arg7) + { + } + + virtual ~GenericEvent8() + { + } + + Arg7 GetArg7() const + { + return m_arg7; + } +}; + +} // namespace DPL + +#define DECLARE_GENERIC_EVENT_0(ClassName) \ + class ClassName \ + : public DPL::GenericEvent0 \ + { \ + public: \ + explicit ClassName(const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent0(sender) \ + { \ + } \ + }; + +#define DECLARE_GENERIC_EVENT_1(ClassName, Arg0Type) \ + class ClassName \ + : public DPL::GenericEvent1 \ + { \ + public: \ + explicit ClassName(const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent1(sender) \ + { \ + } \ + \ + explicit ClassName(Arg0Type arg0, \ + const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent1(arg0, sender) \ + { \ + } \ + }; + +#define DECLARE_GENERIC_EVENT_2(ClassName, Arg0Type, Arg1Type) \ + class ClassName \ + : public DPL::GenericEvent2 \ + { \ + public: \ + explicit ClassName(const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent2(sender) \ + { \ + } \ + \ + ClassName(Arg0Type arg0, Arg1Type arg1, \ + const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent2(arg0, arg1, sender) \ + { \ + } \ + }; + +#define DECLARE_GENERIC_EVENT_3(ClassName, Arg0Type, Arg1Type, Arg2Type) \ + class ClassName \ + : public DPL::GenericEvent3 \ + { \ + public: \ + explicit ClassName(const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent3(sender) \ + { \ + } \ + \ + ClassName(Arg0Type arg0, Arg1Type arg1, Arg2Type arg2, \ + const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent3(arg0, arg1, arg2, sender) \ + { \ + } \ + }; + +#define DECLARE_GENERIC_EVENT_4(ClassName, Arg0Type, Arg1Type, Arg2Type, Arg3Type) \ + class ClassName \ + : public DPL::GenericEvent4 \ + { \ + public: \ + explicit ClassName(const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent4(sender) \ + { \ + } \ + \ + ClassName(Arg0Type arg0, Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, \ + const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent4(arg0, arg1, arg2, arg3, sender) \ + { \ + } \ + }; + +#define DECLARE_GENERIC_EVENT_5(ClassName, Arg0Type, Arg1Type, Arg2Type, Arg3Type, Arg4Type) \ + class ClassName \ + : public DPL::GenericEvent5 \ + { \ + public: \ + explicit ClassName(const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent5(sender) \ + { \ + } \ + \ + ClassName(Arg0Type arg0, Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4, \ + const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent5(arg0, arg1, arg2, arg3, arg4, sender) \ + { \ + } \ + }; + +#define DECLARE_GENERIC_EVENT_6(ClassName, Arg0Type, Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type) \ + class ClassName \ + : public DPL::GenericEvent6 \ + { \ + public: \ + explicit ClassName(const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent6(sender) \ + { \ + } \ + \ + ClassName(Arg0Type arg0, Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4, Arg5Type arg5, \ + const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent6(arg0, arg1, arg2, arg3, arg4, arg5, sender) \ + { \ + } \ + }; + +#define DECLARE_GENERIC_EVENT_7(ClassName, Arg0Type, Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type, Arg6Type) \ + class ClassName \ + : public DPL::GenericEvent7 \ + { \ + public: \ + explicit ClassName(const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent7(sender) \ + { \ + } \ + \ + ClassName(Arg0Type arg0, Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4, Arg5Type arg5, Arg6Type arg6, \ + const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent7(arg0, arg1, arg2, arg3, arg4, arg5, arg6, sender) \ + { \ + } \ + }; + +#define DECLARE_GENERIC_EVENT_8(ClassName, Arg0Type, Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type, Arg6Type, Arg7Type) \ + class ClassName \ + : public DPL::GenericEvent8 \ + { \ + public: \ + explicit ClassName(const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent8(sender) \ + { \ + } \ + \ + ClassName(Arg0Type arg0, Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4, Arg5Type arg5, Arg6Type arg6, Arg7Type arg7, \ + const DPL::EventSender &sender = DPL::EventSender(NULL)) \ + : DPL::GenericEvent8(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, sender) \ + { \ + } \ + }; + +#endif // DPL_GENERIC_EVENT_H diff --git a/modules/core/include/dpl/lexical_cast.h b/modules/core/include/dpl/lexical_cast.h new file mode 100644 index 0000000..57eb5e0 --- /dev/null +++ b/modules/core/include/dpl/lexical_cast.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file lexical_cast.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Header file for lexical cast + */ +#ifndef DPL_LEXICAL_CAST_H +#define DPL_LEXICAL_CAST_H + +#include + +namespace DPL +{ +template +TargetType lexical_cast(const SourceType &data) +{ + TargetType result; + + std::ostringstream out; + out << data; + + std::istringstream in(out.str()); + in >> result; + + return result; +} +} // namespace DPL + +#endif // DPL_LEXICAL_CAST_H diff --git a/modules/core/include/dpl/main.h b/modules/core/include/dpl/main.h new file mode 100644 index 0000000..62bd7e0 --- /dev/null +++ b/modules/core/include/dpl/main.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file main.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of main for EFL + */ +#ifndef DPL_MAIN_H +#define DPL_MAIN_H + +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +class Main + : public WaitableHandleWatchSupport +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, CreateFailed) + }; + +protected: + Ecore_Fd_Handler *m_invokerHandler; + + static Eina_Bool StaticDispatchInvoker(void *data, Ecore_Fd_Handler *fd_handler); + static Eina_Bool StaticDispatchReadWatcher(void *data, Ecore_Fd_Handler *fd_handler); + static Eina_Bool StaticDispatchWriteWatcher(void *data, Ecore_Fd_Handler *fd_handler); + + typedef std::list EcoreFdHandlerList; + + EcoreFdHandlerList m_readWatchersList; + EcoreFdHandlerList m_writeWatchersList; + + void DispatchInvoker(); + void DispatchReadWatcher(WaitableHandle waitableHandle); + void DispatchWriteWatcher(WaitableHandle waitableHandle); + + void ReloadWatchList(); + + // WaitableHandleWatchSupport + virtual Thread *GetInvokerThread(); + virtual void HandleDirectInvoker(); + +#ifdef DPL_ENABLE_GLIB_LOOP_INTEGRATION_WORKAROUND + // GLIB loop intergration workaround + typedef int (*EcoreSelectType)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); + EcoreSelectType m_oldEcoreSelect; + + static int EcoreSelectInterceptor(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); +#endif // DPL_ENABLE_GLIB_LOOP_INTEGRATION_WORKAROUND + +public: + explicit Main(); + virtual ~Main(); +}; + +/** + * Main singleton + */ +typedef Singleton
MainSingleton; +} // namespace DPL + +#endif // DPL_MAIN_H diff --git a/modules/core/include/dpl/mutex.h b/modules/core/include/dpl/mutex.h new file mode 100644 index 0000000..fbc0c5b --- /dev/null +++ b/modules/core/include/dpl/mutex.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file mutex.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of mutex + */ +#ifndef DPL_MUTEX_H +#define DPL_MUTEX_H + +#include +#include +#include + +namespace DPL +{ +class Mutex + : private Noncopyable +{ +public: + class ScopedLock + : private Noncopyable + { + private: + Mutex *m_mutex; + + public: + explicit ScopedLock(Mutex *mutex); + ~ScopedLock(); + }; + + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, CreateFailed) + DECLARE_EXCEPTION_TYPE(Base, LockFailed) + DECLARE_EXCEPTION_TYPE(Base, UnlockFailed) + }; + +private: + mutable pthread_mutex_t m_mutex; + + void Lock() const; + void Unlock() const; + +public: + Mutex(); + ~Mutex(); +}; + +} // namespace DPL + +#endif // DPL_MUTEX_H diff --git a/modules/core/include/dpl/named_base_pipe.h b/modules/core/include/dpl/named_base_pipe.h new file mode 100644 index 0000000..ab7630b --- /dev/null +++ b/modules/core/include/dpl/named_base_pipe.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file named_base_pipe.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of named base pipe + */ +#ifndef DPL_NAMED_BASE_PIPE_H +#define DPL_NAMED_BASE_PIPE_H + +#include + +namespace DPL +{ +class NamedBasePipe +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, AlreadyExist) + DECLARE_EXCEPTION_TYPE(Base, CreateFailed) + DECLARE_EXCEPTION_TYPE(Base, DestroyFailed) + }; + +public: + virtual ~NamedBasePipe(); + + static void Create(const std::string &fileName); + static void Destroy(const std::string &fileName); +}; +} // namespace DPL + +#endif // DPL_NAMED_BASE_PIPE_H diff --git a/modules/core/include/dpl/named_input_pipe.h b/modules/core/include/dpl/named_input_pipe.h new file mode 100644 index 0000000..247890d --- /dev/null +++ b/modules/core/include/dpl/named_input_pipe.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file named_input_pipe.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of named input pipe + */ +#ifndef DPL_NAMED_PIPE_H +#define DPL_NAMED_PIPE_H + +#include +#include +#include +#include + +namespace DPL +{ +class NamedInputPipe + : private Noncopyable, + public NamedBasePipe, + public AbstractWaitableInput +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, OpenFailed) + DECLARE_EXCEPTION_TYPE(Base, CloseFailed) + }; + +protected: + int m_fifo; + +public: + NamedInputPipe(); + virtual ~NamedInputPipe(); + + void Open(const std::string &fileName); + void Close(); + + // AbstractInput + virtual BinaryQueueAutoPtr Read(size_t size); + + // AbstractWaitableInput + virtual WaitableHandle WaitableReadHandle() const; +}; +} // namespace DPL + +#endif // DPL_NAMED_PIPE_H diff --git a/modules/core/include/dpl/named_output_pipe.h b/modules/core/include/dpl/named_output_pipe.h new file mode 100644 index 0000000..9855a81 --- /dev/null +++ b/modules/core/include/dpl/named_output_pipe.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file named_output_pipe.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of named output pipe + */ +#ifndef DPL_NAMED_OUTPUT_PIPE_H +#define DPL_NAMED_OUTPUT_PIPE_H + +#include +#include +#include +#include + +namespace DPL +{ +class NamedOutputPipe + : private Noncopyable, + public NamedBasePipe, + public AbstractWaitableOutput +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, OpenFailed) + DECLARE_EXCEPTION_TYPE(Base, CloseFailed) + }; + +protected: + int m_fifo; + +public: + NamedOutputPipe(); + virtual ~NamedOutputPipe(); + + void Open(const std::string &fileName); + void Close(); + + // AbstractOutput + virtual size_t Write(const BinaryQueue &buffer, size_t bufferSize); + + // AbstracWaitableOutput + virtual WaitableHandle WaitableWriteHandle() const; +}; +} // namespace DPL + +#endif // DPL_NAMED_OUTPUT_PIPE_H diff --git a/modules/core/include/dpl/noncopyable.h b/modules/core/include/dpl/noncopyable.h new file mode 100644 index 0000000..2cc95d4 --- /dev/null +++ b/modules/core/include/dpl/noncopyable.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file noncopyable + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of noncopyable + */ +#ifndef DPL_NONCOPYABLE_H +#define DPL_NONCOPYABLE_H + +namespace DPL +{ +class Noncopyable +{ +private: + Noncopyable(const Noncopyable &); + const Noncopyable &operator=(const Noncopyable &); +public: + Noncopyable(); + virtual ~Noncopyable(); +}; + +} // namespace DPL + +#endif // DPL_NONCOPYABLE_H diff --git a/modules/core/include/dpl/noreturn.h b/modules/core/include/dpl/noreturn.h new file mode 100644 index 0000000..93845b7 --- /dev/null +++ b/modules/core/include/dpl/noreturn.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file noreturn.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of noreturn + */ +#ifndef DPL_NORETURN_H +#define DPL_NORETURN_H + +#define DPL_NORETURN __attribute__((__noreturn__)) + +#endif // DPL_NORETURN_H diff --git a/modules/core/include/dpl/once.h b/modules/core/include/dpl/once.h new file mode 100644 index 0000000..2b9039e --- /dev/null +++ b/modules/core/include/dpl/once.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file once.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of once + */ +#ifndef DPL_ONCE_H +#define DPL_ONCE_H + +#include +#include +#include +#include + +namespace DPL +{ +class Once + : private Noncopyable +{ +public: + typedef FastDelegate Delegate; + + void Call(Delegate delegate); + +private: + Atomic m_atomic; + Mutex m_mutex; +}; +} // namespace DPL + +#endif // DPL_ONCE_H diff --git a/modules/core/include/dpl/optional.h b/modules/core/include/dpl/optional.h new file mode 100644 index 0000000..0a0736c --- /dev/null +++ b/modules/core/include/dpl/optional.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file optional_value.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + */ + +#ifndef DPL_OPTIONAL_H +#define DPL_OPTIONAL_H + +#include + +namespace DPL +{ + +template +class Optional +{ + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, NullReference) + }; + +public: + Optional() : + m_null(true), + m_value() + { + } + + Optional(const Type& t) : + m_null(false), + m_value(t) + { + } + + bool IsNull() const + { + return m_null; + } + + Type& operator*() + { + if (m_null) Throw(typename Exception::NullReference); + return m_value; + } + + const Type& operator*() const + { + if (m_null) Throw(typename Exception::NullReference); + return m_value; + } + + const Type* operator->() const + { + if (m_null) Throw(typename Exception::NullReference); + return &m_value; + } + + Type* operator->() + { + if (m_null) Throw(typename Exception::NullReference); + return &m_value; + } + + bool operator!() const + { + return m_null; + } + + Optional& operator=(const Type& other) + { + m_null = false; + m_value = other; + return *this; + } + + bool operator==(const Optional& aSecond) const + { + return LogicalOperator(*this, aSecond, std::equal_to(), std::equal_to()); + } + + bool operator==(const Type& aSecond) const + { + return Optional(aSecond) == *this; + } + + bool operator!=(const Optional& aSecond) const + { + return !(*this == aSecond); + } + + bool operator<(const Optional& aSecond) const + { + return LogicalOperator(*this, aSecond, std::less(), std::less()); + } + + bool operator>(const Optional& aSecond) const + { + return LogicalOperator(*this, aSecond, std::greater(), std::greater()); + } + + bool operator<=(const Optional& aSecond) const + { + return *this == aSecond || *this < aSecond; + } + + bool operator>=(const Optional& aSecond) const + { + return *this == aSecond || *this > aSecond; + } + + static Optional Null; + +private: + bool m_null; + Type m_value; + + template + static bool LogicalOperator(const Optional& aFirst, const Optional& aSecond, + taComparator aComparator, taNullComparator aNullComparator) + { + if (aFirst.m_null == aSecond.m_null) + { + if (aFirst.m_null) + { + return taEquality; + } + else + { + return aComparator(aFirst.m_value, aSecond.m_value); + } + } + else + { + return aNullComparator(aFirst.m_null, aSecond.m_null); + } + } +}; + +template +Optional Optional::Null = Optional(); + +} //namespace DPL + +template +std::ostream& operator<<(std::ostream& aStream, const DPL::Optional& aOptional) +{ + if (aOptional.IsNull()) + { + return aStream << "null optional"; + } + else + { + return aStream << *aOptional; + } +} + +#endif // DPL_OPTIONAL_VALUE_H diff --git a/modules/core/include/dpl/optional_typedefs.h b/modules/core/include/dpl/optional_typedefs.h new file mode 100644 index 0000000..153e4df --- /dev/null +++ b/modules/core/include/dpl/optional_typedefs.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef DPL_OPTIONAL_TYPEDEFS_H +#define DPL_OPTIONAL_TYPEDEFS_H + +#include +#include + +namespace DPL +{ + +typedef Optional OptionalString; +typedef Optional OptionalInt; +typedef Optional OptionalBool; +typedef Optional OptionalFloat; + +} //namespace DPL + +#endif /* DPL_OPTIONAL_TYPEDEFS_H */ + diff --git a/modules/core/include/dpl/preprocessor.h b/modules/core/include/dpl/preprocessor.h new file mode 100644 index 0000000..83a41e3 --- /dev/null +++ b/modules/core/include/dpl/preprocessor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file preprocessor.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief This file contains some usefull macros. + */ + +#ifndef DPL_PREPROCESSOR_H +#define DPL_PREPROCESSOR_H + +#define DPL_MACRO_CONCAT_IMPL(x, y) x##y +#define DPL_MACRO_CONCAT(x, y) DPL_MACRO_CONCAT_IMPL(x, y) + +#endif//DPL_PREPROCESSOR_H diff --git a/modules/core/include/dpl/read_write_mutex.h b/modules/core/include/dpl/read_write_mutex.h new file mode 100644 index 0000000..1e224de --- /dev/null +++ b/modules/core/include/dpl/read_write_mutex.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file read_write_mutex.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of read write mutex + */ +#ifndef DPL_READ_WRITE_MUTEX_H +#define DPL_READ_WRITE_MUTEX_H + +#include +#include +#include + +namespace DPL +{ +class ReadWriteMutex + : private Noncopyable +{ +public: + class ScopedReadLock + : private Noncopyable + { + private: + ReadWriteMutex *m_mutex; + + public: + ScopedReadLock(ReadWriteMutex *mutex); + virtual ~ScopedReadLock(); + }; + + class ScopedWriteLock + : private Noncopyable + { + private: + ReadWriteMutex *m_mutex; + + public: + ScopedWriteLock(ReadWriteMutex *mutex); + virtual ~ScopedWriteLock(); + }; + + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, CreateFailed) + DECLARE_EXCEPTION_TYPE(Base, DestroyFailed) + DECLARE_EXCEPTION_TYPE(Base, ReadLockFailed) + DECLARE_EXCEPTION_TYPE(Base, WriteLockFailed) + DECLARE_EXCEPTION_TYPE(Base, UnlockFailed) + }; + +private: + mutable pthread_rwlock_t m_rwlock; + + void ReadLock() const; + void WriteLock() const; + void Unlock() const; + +public: + explicit ReadWriteMutex(); + virtual ~ReadWriteMutex(); +}; + +} // namespace DPL + +#endif // DPL_READ_WRITE_MUTEX_H diff --git a/modules/core/include/dpl/recursive_mutex.h b/modules/core/include/dpl/recursive_mutex.h new file mode 100644 index 0000000..ec1e23c --- /dev/null +++ b/modules/core/include/dpl/recursive_mutex.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file recursive_mutex.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of recursive mutex + */ +#ifndef DPL_RECURSIVE_MUTEX_H +#define DPL_RECURSIVE_MUTEX_H + +#include +#include +#include + +namespace DPL +{ +class RecursiveMutex + : private Noncopyable +{ +public: + class ScopedLock + : private Noncopyable + { + private: + RecursiveMutex *m_mutex; + + public: + ScopedLock(RecursiveMutex *mutex); + virtual ~ScopedLock(); + }; + + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, CreateFailed) + DECLARE_EXCEPTION_TYPE(Base, DestroyFailed) + DECLARE_EXCEPTION_TYPE(Base, LockFailed) + DECLARE_EXCEPTION_TYPE(Base, UnlockFailed) + }; + +private: + mutable pthread_mutex_t m_mutex; + + void Lock() const; + void Unlock() const; + +public: + explicit RecursiveMutex(); + virtual ~RecursiveMutex(); +}; + +} // namespace DPL + +#endif // DPL_RECURSIVE_MUTEX_H diff --git a/modules/core/include/dpl/scoped_array.h b/modules/core/include/dpl/scoped_array.h new file mode 100644 index 0000000..fa0c540 --- /dev/null +++ b/modules/core/include/dpl/scoped_array.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @file scoped_ptr.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of scoped array RAII + */ +#ifndef DPL_SCOPED_ARRAY_H +#define DPL_SCOPED_ARRAY_H + +#include + +#include +#include + +namespace DPL +{ +template +struct ScopedArrayPolicy +{ + typedef Class* Type; + static Type NullValue() { return NULL; } + static void Destroy(Type ptr) { delete [] ptr; } +}; + +template +class ScopedArray : public ScopedResource > +{ + typedef ScopedArrayPolicy Policy; + typedef ScopedResource BaseType; + + public: + explicit ScopedArray(Class *ptr = Policy::NullValue()) : BaseType(ptr) { } + + Class &operator [](std::ptrdiff_t k) const + { + Assert(this->m_value != Policy::NullValue() && + "Dereference of scoped NULL array!"); + Assert(k >= 0 && "Negative array index"); + + return this->m_value[k]; + } +}; +} // namespace DPL + +#endif // DPL_SCOPED_PTR_H diff --git a/modules/core/include/dpl/scoped_close.h b/modules/core/include/dpl/scoped_close.h new file mode 100644 index 0000000..e51b7b4 --- /dev/null +++ b/modules/core/include/dpl/scoped_close.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @file scoped_close.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of scoped close RAII + */ +#ifndef DPL_SCOPED_CLOSE_H +#define DPL_SCOPED_CLOSE_H + +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +struct ScopedClosePolicy +{ + typedef int Type; + static Type NullValue() { return -1; } + static void Destroy(Type handle) + { + if (handle != -1) + { + if (TEMP_FAILURE_RETRY(::fsync(handle)) == -1) + { + std::string errString = GetErrnoString(); + LogPedantic("Failed to fsync scoped close error: " + << errString); + } + + if (::close(handle) == -1) + { + std::string errString = GetErrnoString(); + LogPedantic("Failed to scoped close error: " + << errString); + } + } + } +}; + +class ScopedClose : public ScopedResource +{ + typedef ScopedClosePolicy Policy; + typedef ScopedResource BaseType; + typedef ScopedClosePolicy::Type Type; + + public: + explicit ScopedClose(Type handle = Policy::NullValue()) : + BaseType(handle) + { } +}; +} // namespace DPL + +#endif // DPL_SCOPED_CLOSE_H diff --git a/modules/core/include/dpl/scoped_fclose.h b/modules/core/include/dpl/scoped_fclose.h new file mode 100644 index 0000000..9758b8d --- /dev/null +++ b/modules/core/include/dpl/scoped_fclose.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @file scoped_fclose.h + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of scoped fclose RAII + */ +#ifndef DPL_SCOPED_FCLOSE_H +#define DPL_SCOPED_FCLOSE_H + + +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +struct ScopedFClosePolicy +{ + typedef FILE* Type; + static Type NullValue() { return NULL; } + static void Destroy(Type file) + { + if (file != NULL) + { + // Try to flush first + if (TEMP_FAILURE_RETRY(fflush(file)) != 0) + { + std::string errString = GetErrnoString(); + LogPedantic("Failed to fflush scoped fclose error: " + << errString); + } + + // fclose cannot be retried, try to close once + if (fclose(file) != 0) + { + std::string errString = GetErrnoString(); + LogPedantic("Failed scoped fclose error: " << errString); + } + } + } +}; + +class ScopedFClose : public ScopedResource +{ + typedef ScopedFClosePolicy Policy; + typedef ScopedResource BaseType; + + public: + explicit ScopedFClose(FILE* argFileStream = Policy::NullValue()) : + BaseType(argFileStream) + {} +}; +} // namespace DPL + +#endif // DPL_SCOPED_FCLOSE_H diff --git a/modules/core/include/dpl/scoped_free.h b/modules/core/include/dpl/scoped_free.h new file mode 100644 index 0000000..7b3bd5a --- /dev/null +++ b/modules/core/include/dpl/scoped_free.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @file scoped_free.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of scoped free RAII + */ + +#ifndef DPL_SCOPED_FREE_H +#define DPL_SCOPED_FREE_H + +#include +#include + +#include + +namespace DPL +{ +template +struct ScopedFreePolicy +{ + typedef Class* Type; + static Type NullValue() { return NULL; } + static void Destroy(Type ptr) { free(ptr); } +}; + +template +class ScopedFree : public ScopedResource > +{ + typedef ScopedFreePolicy Policy; + typedef ScopedResource BaseType; + + public: + explicit ScopedFree(Memory *ptr = Policy::NullValue()) : BaseType(ptr) { } +}; +} // namespace DPL + +#endif // DPL_SCOPED_FREE_H diff --git a/modules/core/include/dpl/scoped_gpointer.h b/modules/core/include/dpl/scoped_gpointer.h new file mode 100644 index 0000000..2f78bec --- /dev/null +++ b/modules/core/include/dpl/scoped_gpointer.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @file scoped_gpointer.h + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of scoped_gpointer + */ + +#ifndef DPL_SCOPED_GPOINTER_H +#define DPL_SCOPED_GPOINTER_H + +#include +#include +#include +#include + +namespace DPL +{ + +struct ScopedGPointerPolicy +{ + typedef gpointer Type; + static Type NullValue() + { + return NULL; + } + static void Destroy(Type pointer) + { + if (pointer != NULL) { + g_object_unref(pointer); + } + } +}; + +template +class ScopedGPointer : public DPL::ScopedResource +{ + typedef ScopedGPointerPolicy Policy; + typedef DPL::ScopedResource BaseType; + + public: + explicit ScopedGPointer(typename Policy::Type pointer = + Policy::NullValue()) : + BaseType(pointer) + { + } + + Class *operator->() const throw() + { + Assert(this->m_value != Policy::NullValue() && + "Dereference of scoped NULL pointer!"); + return static_cast(this->m_value); + } + + Class & operator *() const throw() + { + Assert(this->m_value != Policy::NullValue() && + "Dereference of scoped NULL pointer!"); + return *static_cast(this->m_value); + } +}; + +} // namespace DPL + +#endif // DPL_SCOPED_GPOINTER_H diff --git a/modules/core/include/dpl/scoped_ptr.h b/modules/core/include/dpl/scoped_ptr.h new file mode 100644 index 0000000..3788dc1 --- /dev/null +++ b/modules/core/include/dpl/scoped_ptr.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file scoped_ptr.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of scoped pointer RAII + */ +#ifndef DPL_SCOPED_PTR_H +#define DPL_SCOPED_PTR_H + +#include + +#include +#include + +namespace DPL +{ +template +struct ScopedPtrPolicy +{ + typedef Class* Type; + static Type NullValue() { return NULL; } + static void Destroy(Type ptr) { delete ptr; } +}; + +template > +class ScopedPtr : public ScopedResource +{ + typedef ClassPolicy Policy; + typedef ScopedResource BaseType; + + public: + explicit ScopedPtr(Class *ptr = Policy::NullValue()) : BaseType(ptr) { } + + Class *operator->() const throw() + { + Assert(this->m_value != Policy::NullValue() && + "Dereference of scoped NULL pointer!"); + return this->m_value; + } + + Class &operator *() const throw() + { + Assert(this->m_value != Policy::NullValue() && + "Dereference of scoped NULL pointer!"); + return *this->m_value; + } +}; +} // namespace DPL + +#endif // DPL_SCOPED_PTR_H diff --git a/modules/core/include/dpl/scoped_resource.h b/modules/core/include/dpl/scoped_resource.h new file mode 100644 index 0000000..a902d72 --- /dev/null +++ b/modules/core/include/dpl/scoped_resource.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file scoped_resource.h + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of scoped resource pattern + */ +#ifndef DPL_SCOPED_RESOURCE_H +#define DPL_SCOPED_RESOURCE_H + +#include + +namespace DPL +{ +template +class ScopedResource + : private Noncopyable +{ + public: + typedef typename ClassPolicy::Type ValueType; + typedef ScopedResource ThisType; + + protected: + ValueType m_value; + + public: + explicit ScopedResource(ValueType value) : m_value(value) { } + + ~ScopedResource() + { + ClassPolicy::Destroy(m_value); + } + + ValueType Get() const { return m_value; } + + void Reset(ValueType value = ClassPolicy::NullValue()) + { + ClassPolicy::Destroy(m_value); + m_value = value; + } + + ValueType Release() + { + ValueType value = m_value; + m_value = ClassPolicy::NullValue(); + return value; + } + typedef ValueType ThisType::*UnknownBoolType; + + operator UnknownBoolType() const + { + return m_value == ClassPolicy::NullValue() ? + 0 : //0 is valid here because it converts to false + &ThisType::m_value; //it converts to true + } + + bool operator !() const + { + return m_value == ClassPolicy::NullValue(); + } + +}; +} // namespace DPL + +#endif // DPL_SCOPED_RESOURCE_H diff --git a/modules/core/include/dpl/semaphore.h b/modules/core/include/dpl/semaphore.h new file mode 100644 index 0000000..420d9a5 --- /dev/null +++ b/modules/core/include/dpl/semaphore.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file semaphore.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of semaphore + */ +#ifndef DPL_SEMAPHORE_H +#define DPL_SEMAPHORE_H + +#include +#include +#include +#include + +namespace DPL +{ +class Semaphore + : private Noncopyable +{ +public: + class ScopedLock + : private Noncopyable + { + private: + Semaphore *m_semaphore; + + public: + explicit ScopedLock(Semaphore *semaphore); + ~ScopedLock(); + }; + + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, CreateFailed) + DECLARE_EXCEPTION_TYPE(Base, LockFailed) + DECLARE_EXCEPTION_TYPE(Base, UnlockFailed) + DECLARE_EXCEPTION_TYPE(Base, RemoveFailed) + }; + +private: + enum Type + { + Type_Unnamed, + Type_Named + }; + + Type m_type; + + mutable union + { + struct + { + sem_t handle; + } unnamed; + + struct + { + sem_t *handle; + char *name; + bool unlinkOnDestroy; + } named; + } m_semaphore; + + sem_t *InternalGet() const; + void InternalDestroy(); + + void Lock() const; + void Unlock() const; + +public: + /** + * Remove a named semaphore + * + * @param fileName Name of the semaphore + */ + static void Remove(const std::string &fileName); + + /** + * Open an unnamed semaphore + * + * @param maxLockCount Maximum number of threads allowed to enter semaphore + */ + explicit Semaphore(size_t maxLockCount); + + /** + * Open a named semaphore + * + * @param fileName Semaphore filename + * @param allowCreate Should non-existing semaphore be created + * @param maxLockCount Maximum number of threads allowed to enter semaphore + * @param permissions Semaphore file permissions + * @param unlinkOnDestroy Should semaphore file be deleted on destruction + */ + explicit Semaphore(const std::string &fileName, + bool allowCreate = true, + bool exclusiveCreate = false, + size_t maxLockCount = 1, + int permissions = 0600, + bool unlinkOnDestroy = false); + + /** + * Destroy a semaphore + */ + ~Semaphore(); +}; +} // namespace DPL + +#endif // DPL_SEMAPHORE_H diff --git a/modules/core/include/dpl/serialization.h b/modules/core/include/dpl/serialization.h new file mode 100644 index 0000000..8369665 --- /dev/null +++ b/modules/core/include/dpl/serialization.h @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file serialization.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 1.0 + * @brief Interfaces and templates used for data serialization. + */ +#ifndef SERIALIZATION_H +#define SERIALIZATION_H + +#include +#include +#include +#include + +namespace DPL { + +// Abstract data stream buffer +class IStream { + public: + virtual void Read(size_t num, void * bytes) = 0; + virtual void Write(size_t num, const void * bytes) = 0; + virtual ~IStream(){}; +}; + +// Serializable interface +class ISerializable { + public: +/* ISerializable(){}; + ISerializable(IStream&){}; */ + virtual void Serialize(IStream &) const = 0; + virtual ~ISerializable(){}; +}; + +struct Serialization { +// serialization +// normal functions + +// ISerializable objects +static void Serialize(IStream& stream, const ISerializable& object){ + object.Serialize(stream); +} +static void Serialize(IStream& stream, const ISerializable* const object){ + object->Serialize(stream); +} + +// unsigned int +static void Serialize(IStream& stream, const unsigned value){ + stream.Write(sizeof(value),&value); +} +static void Serialize(IStream& stream, const unsigned* const value){ + stream.Write(sizeof(*value),value); +} + +// int +static void Serialize(IStream& stream, const int value){ + stream.Write(sizeof(value),&value); +} +static void Serialize(IStream& stream, const int* const value){ + stream.Write(sizeof(*value),value); +} + +// bool +static void Serialize(IStream& stream, const bool value){ + stream.Write(sizeof(value),&value); +} +static void Serialize(IStream& stream, const bool* const value){ + stream.Write(sizeof(*value),value); +} + +// std::string +static void Serialize(IStream& stream, const std::string& str){ + int length = str.size(); + stream.Write(sizeof(length),&length); + stream.Write(length,str.c_str()); +} +static void Serialize(IStream& stream, const std::string* const str){ + int length = str->size(); + stream.Write(sizeof(length),&length); + stream.Write(length,str->c_str()); +} + +// STL templates + +// std::list +template +static void Serialize(IStream& stream, const std::list& list){ + int length = list.size(); + stream.Write(sizeof(length),&length); + for(typename std::list::const_iterator list_iter = list.begin(); + list_iter != list.end(); list_iter++) + { + Serialize(stream, *list_iter); + } +} +template +static void Serialize(IStream& stream, const std::list* const list){ + Serialize(stream,*list); +} + +// std::vector +template +static void Serialize(IStream& stream, const std::vector& vec){ + int length = vec.size(); + stream.Write(sizeof(length),&length); + for(typename std::vector::const_iterator vec_iter = vec.begin(); + vec_iter != vec.end(); vec_iter ++) + { + Serialize(stream, *vec_iter); + } +} +template +static void Serialize(IStream& stream, const std::vector* const vec){ + Serialize(stream,*vec); +} + +// std::pair +template +static void Serialize(IStream& stream, const std::pair& p){ + Serialize(stream, p.first); + Serialize(stream, p.second); +} +template +static void Serialize(IStream& stream, const std::pair* const p){ + Serialize(stream,*p); +} + +// std::map +template +static void Serialize(IStream& stream, const std::map& map){ + int length = map.size(); + stream.Write(sizeof(length),&length); + typename std::map::const_iterator it; + for (it = map.begin(); it != map.end(); ++it) { + Serialize(stream,(*it).first); + Serialize(stream,(*it).second); + } +} +template +static void Serialize(IStream& stream, const std::map* const map){ + Serialize(stream,*map); +} +}; // struct Serialization + +struct Deserialization { +// deserialization +// normal functions + +// ISerializable objects +// T instead of ISerializable is needed to call proper constructor +template +static void Deserialize(IStream& stream, T& object){ + object = T(stream); +} +template +static void Deserialize(IStream& stream, T*& object){ + object = new T(stream); +} + +// unsigned int +static void Deserialize(IStream& stream, unsigned& value){ + stream.Read(sizeof(value),&value); +} +static void Deserialize(IStream& stream, unsigned*& value){ + value = new unsigned; + stream.Read(sizeof(*value),value); +} + +// int +static void Deserialize(IStream& stream, int& value){ + stream.Read(sizeof(value),&value); +} +static void Deserialize(IStream& stream, int*& value){ + value = new int; + stream.Read(sizeof(*value),value); +} + +// bool +static void Deserialize(IStream& stream, bool& value){ + stream.Read(sizeof(value),&value); +} +static void Deserialize(IStream& stream, bool*& value){ + value = new bool; + stream.Read(sizeof(*value),value); +} + +// std::string +static void Deserialize(IStream& stream, std::string& str){ + int length; + stream.Read(sizeof(length),&length); + char * buf = new char[length+1]; + stream.Read(length,buf); + buf[length] = 0; + str = std::string(buf); + delete [] buf; +} +static void Deserialize(IStream& stream, std::string*& str){ + int length; + stream.Read(sizeof(length),&length); + char * buf = new char[length+1]; + stream.Read(length,buf); + buf[length] = 0; + str = new std::string(buf); + delete [] buf; +} + +// STL templates + +// std::list +template +static void Deserialize(IStream& stream, std::list& list){ + int length; + stream.Read(sizeof(length),&length); + for (int i = 0; i < length; ++i) { + T obj; + Deserialize(stream, obj); + list.push_back(obj); + } +} +template +static void Deserialize(IStream& stream, std::list*& list){ + list = new std::list; + Deserialize(stream,*list); +} + +// std::vector +template +static void Deserialize(IStream& stream, std::vector& vec){ + int length; + stream.Read(sizeof(length),&length); + for (int i = 0; i < length; ++i) { + T obj; + Deserialize(stream, obj); + vec.push_back(obj); + } +} +template +static void Deserialize(IStream& stream, std::vector*& vec){ + vec = new std::vector; + Deserialize(stream,*vec); +} + +// std::pair +template +static void Deserialize(IStream& stream, std::pair& p){ + Deserialize(stream, p.first); + Deserialize(stream, p.second); +} +template +static void Deserialize(IStream& stream, std::pair*& p){ + p = new std::pair; + Deserialize(stream,*p); +} + +// std::map +template +static void Deserialize(IStream& stream, std::map& map){ + int length; + stream.Read(sizeof(length),&length); + for (int i = 0; i < length; ++i) { + K key; + T obj; + Deserialize(stream,key); + Deserialize(stream,obj); + map[key] = obj; + } +} +template +static void Deserialize(IStream& stream, std::map*& map){ + map = new std::map; + Deserialize(stream,*map); +} +}; // struct Deserialization + +} // namespace DPL + +#endif // SERIALIZATION_H diff --git a/modules/core/include/dpl/shared_ptr.h b/modules/core/include/dpl/shared_ptr.h new file mode 100644 index 0000000..c964fd9 --- /dev/null +++ b/modules/core/include/dpl/shared_ptr.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file shared_ptr.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of shared pointer RAII + */ +#ifndef DPL_SHARED_PTR_H +#define DPL_SHARED_PTR_H + +#include +#include +#include +#include +#include + +namespace DPL +{ +struct StaticPointerCastTag {}; +struct ConstPointerCastTag {}; +struct DynamicPointerCastTag {}; + +struct SharedCounter +{ + SharedCounter() + : ref(1) + { + } + + Atomic ref; +}; + +template +class EnableSharedFromThis; + +template +inline void _Internal_AcceptSharedPtr(SharedCounter *counter, Other *other, EnableSharedFromThis *otherBase) +{ + otherBase->_Internal_AcceptSharedPtr(counter, other); +} + +struct AnyPointer +{ + template + AnyPointer(Other *) + { + } +}; + +inline void _Internal_AcceptSharedPtr(SharedCounter *, AnyPointer, AnyPointer) +{ +} + +template +class SharedPtr +{ +public: + typedef Class ValueType; + typedef SharedPtr ThisType; + +private: + SharedCounter *m_counter; + Class *m_ptr; + + void AttachCounter(const SharedCounter *counter) + { + // Attention: R-Value const cast + m_counter = const_cast(counter); + + if (m_counter != NULL) + ++m_counter->ref; + } + + void DetachCounter() + { + if (m_counter) + { + if (!--m_counter->ref) + { + delete m_ptr; + delete m_counter; + } + + m_counter = NULL; + m_ptr = NULL; + } + } + +public: + SharedPtr() + : m_counter(NULL), + m_ptr(NULL) + { + } + + explicit SharedPtr(Class *ptr) + : m_counter(NULL), + m_ptr(ptr) + { + if (m_ptr != NULL) + { + m_counter = new SharedCounter(); + _Internal_AcceptSharedPtr(m_counter, m_ptr, m_ptr); + } + } + + SharedPtr(const SharedPtr &other) + : m_counter(NULL), + m_ptr(other.m_ptr) + { + AttachCounter(other.m_counter); + } + + SharedPtr(SharedCounter *counter, Class *ptr) + : m_counter(NULL), + m_ptr(ptr) + { + AttachCounter(counter); + } + + template + friend class SharedPtr; + + template + SharedPtr(const SharedPtr &other, const StaticPointerCastTag &) + : m_counter(NULL), + m_ptr(NULL) + { + m_ptr = static_cast(other.m_ptr); + AttachCounter(other.m_counter); + } + + template + SharedPtr(const SharedPtr &other, const ConstPointerCastTag &) + : m_counter(NULL), + m_ptr(NULL) + { + m_ptr = const_cast(other.m_ptr); + AttachCounter(other.m_counter); + } + + template + SharedPtr(const SharedPtr &other, const DynamicPointerCastTag &) + : m_counter(NULL), + m_ptr(NULL) + { + Class *ptr = dynamic_cast(other.Get()); + + if (ptr == NULL) + return; + + m_ptr = ptr; + AttachCounter(other.m_counter); + } + + virtual ~SharedPtr() + { + DetachCounter(); + } + + Class *Get() const + { + return m_counter == NULL ? NULL : m_ptr; + } + + Class *operator->() const + { + Assert(m_counter != NULL && "Dereference of shared NULL pointer!"); + return m_ptr; + } + + Class &operator *() const + { + Assert(m_counter != NULL && "Dereference of shared NULL pointer!"); + return *m_ptr; + } + + void Reset(Class *ptr = NULL) + { + DetachCounter(); + + if (ptr != NULL) + { + m_ptr = ptr; + m_counter = new SharedCounter(); + _Internal_AcceptSharedPtr(m_counter, m_ptr, m_ptr); + } + } + + SharedPtr &operator=(const SharedPtr &other) + { + if (this != &other) + { + DetachCounter(); + m_ptr = other.m_ptr; + AttachCounter(other.m_counter); + } + + return *this; + } + + Atomic::ValueType GetUseCount() const + { + if (m_counter == NULL) + return Atomic::ValueType(0); + + return m_counter->ref; + } + + DPL_IMPLEMENT_BOOL_OPERATOR(ValueType, ThisType, m_counter, m_ptr) +}; + +template +SharedPtr StaticPointerCast(const SharedPtr &ptr) +{ + return SharedPtr(ptr, StaticPointerCastTag()); +} + +template +SharedPtr ConstPointerCast(const SharedPtr &ptr) +{ + return SharedPtr(ptr, ConstPointerCastTag()); +} + +template +SharedPtr DynamicPointerCast(const SharedPtr &ptr) +{ + return SharedPtr(ptr, DynamicPointerCastTag()); +} + +template +inline bool operator ==(const SharedPtr &first, const SharedPtr &second) +{ + return first.Get() == second.Get(); +} + +template +inline bool operator !=(const SharedPtr &first, const SharedPtr &second) +{ + return first.Get() != second.Get(); +} + +template +inline bool operator <(const SharedPtr &first, const SharedPtr &second) +{ + return first.Get() < second.Get(); +} +template +inline bool operator >(const SharedPtr &first, const SharedPtr &second) +{ + return first.Get() > second.Get(); +} + +template +inline bool operator <=(const SharedPtr &first, const SharedPtr &second) +{ + return first.Get() <= second.Get(); +} + +template +inline bool operator >=(const SharedPtr &first, const SharedPtr &second) +{ + return first.Get() >= second.Get(); +} + +} // namespace DPL + +#endif // DPL_SHARED_PTR_H diff --git a/modules/core/include/dpl/single_instance.h b/modules/core/include/dpl/single_instance.h new file mode 100644 index 0000000..a480b2b --- /dev/null +++ b/modules/core/include/dpl/single_instance.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file single_instance.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of single instance + */ +#ifndef DPL_SINGLE_INSTANCE_H +#define DPL_SINGLE_INSTANCE_H + +#include +#include +#include + +namespace DPL +{ +class SingleInstance + : private Noncopyable +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, LockError) + DECLARE_EXCEPTION_TYPE(Base, ReleaseError) + }; + +private: + bool m_locked; + int m_fdLock; + +public: + SingleInstance(); + virtual ~SingleInstance(); + + bool TryLock(const std::string &lockName); + void Release(); +}; +} // namespace DPL + +#endif // DPL_SINGLE_INSTANCE_H diff --git a/modules/core/include/dpl/singleton.h b/modules/core/include/dpl/singleton.h new file mode 100644 index 0000000..24d2e64 --- /dev/null +++ b/modules/core/include/dpl/singleton.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file singleton.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of singleton + */ +#ifndef DPL_SINGLETON_H +#define DPL_SINGLETON_H + +#include +#include +#include + +namespace DPL +{ +template +class Singleton + : private Class +{ + // + // Note: + // + // To remove posibility of instantiating directly Class, + // make Class' default constructor protected + // + +private: + Singleton() + { + } + + typedef Optional OptionalThreadPtr; + OptionalThreadPtr m_guard; + + static Singleton &InternalInstance(); + +public: + virtual ~Singleton() + { + } + + static Class &Instance(); + + // Thread guarding + static void SetThreadGuard(Thread *thread); + static void ResetThreadGuard(); +}; + +//This is needed for backward compatibility +#ifndef SEPARATED_SINGLETON_IMPLEMENTATION +template +Singleton& Singleton::InternalInstance() +{ + static Singleton instance; + return instance; +} + +template +Class &Singleton::Instance() +{ + Singleton& instance = Singleton::InternalInstance(); + + if (!!instance.m_guard) + { + Assert(Thread::GetCurrentThread() == *instance.m_guard && + "Singleton thread guard failed. A forbidden call from foreign thread was detected!"); + } + + return instance; +} + +// Thread guarding +template +void Singleton::SetThreadGuard(Thread *thread) +{ + Singleton& instance = Singleton::InternalInstance(); + instance.m_guard = OptionalThreadPtr(thread); +} + +template +void Singleton::ResetThreadGuard() +{ + Singleton& instance = Singleton::InternalInstance(); + instance.m_guard = OptionalThreadPtr::Null; +} + +#endif + +} // namespace DPL + +#endif // DPL_SINGLETON_H diff --git a/modules/core/include/dpl/singleton_impl.h b/modules/core/include/dpl/singleton_impl.h new file mode 100644 index 0000000..365ec15 --- /dev/null +++ b/modules/core/include/dpl/singleton_impl.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file singleton_impl.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of singleton + */ +#ifndef DPL_SINGLETON_IMPL_H +#define DPL_SINGLETON_IMPL_H + +/* + * WARNING! + * + * If some singleton's implementation uses another singletons implementation, + * those templates make the second singleton a dubleton. Be warned. Try to use + * singleton_safe_impl.h if possible. + */ + +namespace DPL +{ + +template +Singleton& Singleton::InternalInstance() +{ + static Singleton instance; + return instance; +} + +template +Class &Singleton::Instance() +{ + Singleton& instance = Singleton::InternalInstance(); + + if (!!instance.m_guard) + { + Assert(Thread::GetCurrentThread() == *instance.m_guard && + "Singleton thread guard failed. A forbidden call from foreign thread was detected!"); + } + + return instance; +} + +// Thread guarding +template +void Singleton::SetThreadGuard(Thread *thread) +{ + Singleton& instance = Singleton::InternalInstance(); + instance.m_guard = OptionalThreadPtr(thread); +} + +template +void Singleton::ResetThreadGuard() +{ + Singleton& instance = Singleton::InternalInstance(); + instance.m_guard = OptionalThreadPtr::Null; +} + +} // namespace DPL + +#define IMPLEMENT_SINGLETON(Type) \ +template DPL::Singleton& DPL::Singleton::InternalInstance(); \ +template Type& DPL::Singleton::Instance(); \ +template void DPL::Singleton::SetThreadGuard(DPL::Thread *thread); \ +template void DPL::Singleton::ResetThreadGuard(); + +#endif // DPL_SINGLETON_IMPL_H diff --git a/modules/core/include/dpl/singleton_safe_impl.h b/modules/core/include/dpl/singleton_safe_impl.h new file mode 100644 index 0000000..8fbe009 --- /dev/null +++ b/modules/core/include/dpl/singleton_safe_impl.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file singleton_safe_impl.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of singleton + */ +#ifndef DPL_SINGLETON_SAFE_IMPL_H +#define DPL_SINGLETON_SAFE_IMPL_H + +#define IMPLEMENT_SAFE_SINGLETON(Class) \ +namespace DPL { \ +template<> \ +Singleton& Singleton::InternalInstance() \ +{ \ + static Singleton instance; \ + return instance; \ +} \ + \ +template<> \ +Class &Singleton::Instance() \ +{ \ + Singleton& instance = Singleton::InternalInstance(); \ + if (!!instance.m_guard) \ + { \ + Assert(Thread::GetCurrentThread() == *instance.m_guard && \ + "Singleton thread guard failed. A forbidden call from foreign thread was detected!");\ + } \ + return instance; \ +} \ + \ +template<> \ +void Singleton::SetThreadGuard(Thread *thread) \ +{ \ + Singleton& instance = Singleton::InternalInstance(); \ + instance.m_guard = OptionalThreadPtr(thread); \ +} \ + \ +template<> \ +void Singleton::ResetThreadGuard() \ +{ \ + Singleton& instance = Singleton::InternalInstance(); \ + instance.m_guard = OptionalThreadPtr::Null; \ +} \ +template Singleton& Singleton::InternalInstance(); \ +template Class& Singleton::Instance(); \ +template void Singleton::SetThreadGuard(Thread *thread); \ +template void Singleton::ResetThreadGuard(); \ +} // namespace DPL + + +#endif // DPL_SINGLETON_SAFE_IMPL_H diff --git a/modules/core/include/dpl/sstream.h b/modules/core/include/dpl/sstream.h new file mode 100644 index 0000000..32e21d3 --- /dev/null +++ b/modules/core/include/dpl/sstream.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file sstream.h + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @version 1.0 + * @brief String stream typedefs + */ +#ifndef DPL_CORE_INCLUDE_SSTREAM_H_ +#define DPL_CORE_INCLUDE_SSTREAM_H_ + +#include +#include + +namespace DPL +{ + +// @brief DPL IStringStream +typedef std::basic_istringstream IStringStream; + +// @brief DPL OStringStream +typedef std::basic_ostringstream OStringStream; + +} //namespace DPL + + +#endif // DPL_CORE_INCLUDE_SSTREAM_H_ diff --git a/modules/core/include/dpl/string.h b/modules/core/include/dpl/string.h new file mode 100644 index 0000000..7c35cdb --- /dev/null +++ b/modules/core/include/dpl/string.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file string.h + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @version 1.0 + */ +#ifndef DPL_STRING +#define DPL_STRING + +#include +#include +#include +#include + +namespace DPL +{ +// @brief DPL string +typedef std::basic_string String; + +// @brief String exception class +class StringException +{ +public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + + // @brief Invalid init for UTF8 to UTF32 converter + DECLARE_EXCEPTION_TYPE(Base, IconvInitErrorUTF8ToUTF32) + + // @brief Invalid taStdContainerinit for UTF32 to UTF32 converter + DECLARE_EXCEPTION_TYPE(Base, IconvInitErrorUTF32ToUTF8) + + // @brief Invalid conversion for UTF8 to UTF32 converter + DECLARE_EXCEPTION_TYPE(Base, IconvConvertErrorUTF8ToUTF32) + + // @brief Invalid conversion for UTF8 to UTF32 converter + DECLARE_EXCEPTION_TYPE(Base, IconvConvertErrorUTF32ToUTF8) + + // @brief Invalid ASCII character detected in FromASCII + DECLARE_EXCEPTION_TYPE(Base, InvalidASCIICharacter) + + // @brief Invalid ASCII character detected in FromASCII + DECLARE_EXCEPTION_TYPE(Base, ICUInvalidCharacterFound) +}; + +//!\brief convert ASCII string to DPL::String +String FromASCIIString(const std::string& aString); + +//!\brief convert UTF32 string to DPL::String +String FromUTF32String(const std::wstring& aString); + +//@brief Returns String object created from UTF8 string +//@param[in] aString input UTF-8 string +String FromUTF8String(const std::string& aString); + +//@brief Returns String content as std::string +std::string ToUTF8String(const String& aString); + +//@brief Compare two unicode strings +int StringCompare(const String &left, + const String &right, + bool caseInsensitive = false); + +//@brief Splits the string into substrings. +//@param[in] str Input string +//@param[in] delimiters array or string containing a sequence of substring delimiters. Can be also a single delimiter character. +//@param[in] it InserterIterator that is used to save the generated substrings. +template +void Tokenize(const StringType& str, const Delimiters& delimiters, InserterIterator it, bool ignoreEmpty = false) +{ + typename StringType::size_type nextSearchStart = 0; + typename StringType::size_type pos; + typename StringType::size_type length; + + while ( true ) + { + pos = str.find_first_of(delimiters, nextSearchStart); + length = ((pos == StringType::npos) ? str.length() : pos) - nextSearchStart; + + if ( !ignoreEmpty || length > 0 ) + { + *it = str.substr(nextSearchStart, length); + it++; + } + + if ( pos == StringType::npos ) + return; + + nextSearchStart = pos + 1; + } +} + +} //namespace DPL + +std::ostream& operator<<(std::ostream& aStream, const DPL::String& aString); + +#endif // DPL_STRING diff --git a/modules/core/include/dpl/task.h b/modules/core/include/dpl/task.h new file mode 100644 index 0000000..7e43253 --- /dev/null +++ b/modules/core/include/dpl/task.h @@ -0,0 +1,535 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Radoslaw Wicik (r.wicik@samsung.com) + * @version 1.0 + * @brief Header file for abstaract task definition + */ +#ifndef DPL_TASK_H +#define DPL_TASK_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +class TaskList; + +class Task + : private Noncopyable +{ +public: + virtual ~Task() {} + + virtual bool NextStep() = 0; + virtual bool Abort() = 0; + virtual size_t GetStepCount() const = 0; +}; + +template +class TaskDecl + : public Task +{ +protected: + typedef void (Impl::*Step)(); + +private: + typedef std::list StepList; + + StepList m_steps; + StepList m_abortSteps; + typename StepList::iterator m_currentStep; + typename StepList::iterator m_nextStep; + bool m_switched; + + Impl *m_impl; + bool m_running; + +protected: + void AddStep(Step step) + { + Assert(!m_running && "AddStep is not allowed after calling NextStep!"); + Assert(m_steps.end() == std::find(m_steps.begin(), m_steps.end(), step) && "The same step started twice is not supported"); + m_steps.push_back(step); + m_nextStep = m_steps.begin(); + } + + void AddAbortStep(Step step) + { + Assert(!m_running && "AddAbortStep is not allowed after calling NextStep!"); + Assert(m_abortSteps.end() == std::find(m_abortSteps.begin(), m_abortSteps.end(), step) && "The same step started twice is not supported"); + m_abortSteps.push_front(step); + } + + void SwitchToStep(Step step) + { + /// @TODO There can be problem here if user sets the same method two times in task list. + typename StepList::iterator i = std::find(m_steps.begin(), m_steps.end(), step); + Assert(i != m_steps.end()); + m_nextStep = i; + m_switched = true; + } + + Step GetCurrentStep() const + { + if(m_currentStep == m_steps.end()) + return NULL; + + return *m_currentStep; + } + +public: + TaskDecl(Impl *impl) + : m_switched(false), + m_impl(impl), + m_running(false) + { + Assert(this == m_impl); + m_currentStep = m_steps.end(); + m_nextStep = m_steps.end(); + } + + bool NextStep() + { + m_running = true; + + Assert(m_nextStep != m_steps.end() && "Step list is empty or all steps done"); + + m_switched = false; + + Step call = *m_nextStep; + (*m_impl.*call)(); + + m_currentStep = m_nextStep; + + if (m_switched) + return true; + else + return ++m_nextStep != m_steps.end(); + } + + bool Abort() + { + m_running = true; + + m_steps.clear(); + + if (m_abortSteps.empty()) + return false; + + FOREACH (it, m_abortSteps) + m_steps.push_back(*it); + + m_nextStep = m_steps.begin(); + + m_abortSteps.clear(); + + return true; + } + + size_t GetStepCount() const + { + return static_cast(m_steps.size()); + } +}; + +template +class MultiTaskDecl + : public Task +{ +protected: + typedef void (ImplementationType::*Step)(); + typedef std::list StepList; + typedef std::stack StepStack; + +private: + static std::string StepToString(Step step) + { + std::ostringstream pseudoAddressStream; + pseudoAddressStream << std::hex << union_cast(step); + + std::string pseudoAddress = pseudoAddressStream.str(); + + std::transform(pseudoAddress.begin(), pseudoAddress.end(), + pseudoAddress.begin(), ::toupper); + + return std::string("0x") + pseudoAddress; + } + + struct ConditionalStep + { + Step step; + + // Depencency lists + StepList unsatisfiedDependencies; + StepList satisfiedDependencies; + + ConditionalStep() + : step(NULL) + { + } + + ConditionalStep(Step stepArg, + StepList dependenciesArg) + : step(stepArg), + unsatisfiedDependencies(dependenciesArg) + { + } + }; + + typedef std::list ConditionalStepList; + + // Synchronization + Semaphore m_activeStepsSemaphore; + mutable Mutex m_dependencyListMutex; + + // Those steps that have their dependency lists satified and are ready + // to be executed + // Current step is defined to be the front of satisfied list + ConditionalStepList m_satisfiedSteps; + + // Those steps that are going to be executed but their dependency + // lists have not been satified + ConditionalStepList m_unsatisfiedSteps; + + // Those steps that have their dependency lists satified and are currently + // being executed + ConditionalStepList m_executingSteps; + + // Those steps that have been executed with their dependency lists + // satisfied + ConditionalStepList m_historicSteps; + + ///< Growing list of submitted abort steps + StepStack m_abortSteps; + + // Deriving implementation + ImplementationType *m_implementation; + + // Max parallel steps + size_t m_maxParallelCount; + + ///< Valid in dependency list mutex only + void SatisfyDependencies(const ConditionalStep &conditionalStep) + { + LogPedantic("Satisfying steps with dependecy to step: " + << StepToString(conditionalStep.step)); + + // Can satisfy conditional steps if and only if there are no more + // satisfied, unsatisfied or running same steps + // There is at least one historic step - this one + if (IsContainingStep(conditionalStep.step, m_unsatisfiedSteps) || + IsContainingStep(conditionalStep.step, m_satisfiedSteps) || + IsContainingStep(conditionalStep.step, m_executingSteps)) + { + LogPedantic("Step " << StepToString(conditionalStep.step) + << " cannot satify other steps yet"); + + return; + } + + LogPedantic("Step " << StepToString(conditionalStep.step) + << " can satify other steps"); + + // Do satisfy + typename ConditionalStepList::iterator unsatisfiedStepIterator = + m_unsatisfiedSteps.begin();; + + while (unsatisfiedStepIterator != m_unsatisfiedSteps.end()) + { + typename StepList::iterator iterator = + std::find( + unsatisfiedStepIterator->unsatisfiedDependencies.begin(), + unsatisfiedStepIterator->unsatisfiedDependencies.end(), + conditionalStep.step); + + // Does this conditional step need to be satisfied ? + if (iterator == + unsatisfiedStepIterator->unsatisfiedDependencies.end()) + { + continue; + } + + LogPedantic("Satisfying step " + << StepToString(unsatisfiedStepIterator->step) + << " dependency to step " + << StepToString(conditionalStep.step)); + + // Satisfy dependency + unsatisfiedStepIterator->unsatisfiedDependencies.erase( + iterator); + + unsatisfiedStepIterator->satisfiedDependencies.push_back( + conditionalStep.step); + + // If step is fully satisfied, transfer it to the satisfied + // steps list + if (unsatisfiedStepIterator->unsatisfiedDependencies.empty()) + { + LogPedantic("Step " + << StepToString(unsatisfiedStepIterator->step) + << " is fully satisfied"); + + // Move step + m_satisfiedSteps.push_back(*unsatisfiedStepIterator); + + unsatisfiedStepIterator = + m_unsatisfiedSteps.erase(unsatisfiedStepIterator); + + continue; + } + + // Check next unsatisfied step + ++unsatisfiedStepIterator; + } + } + + ///< Valid in dependency list mutex only + bool IsContainingStep(Step step, const ConditionalStepList &dependencies) const + { + FOREACH (iterator, dependencies) + if (iterator->step == step) + return true; + + return false; + } + + ///< Valid in dependency list mutex only + bool IsStepDependecyListSatisfied( + const StepList &dependencies) const + { + // All dependant step must be historic + FOREACH (iterator, dependencies) + if (!IsContainingStep(*iterator, m_historicSteps)) + return false; + + // Also, none dependant step can exist in + // unsatisfied/satisfied/inProgress lists + FOREACH (iterator, dependencies) + { + if (IsContainingStep(*iterator, m_unsatisfiedSteps) || + IsContainingStep(*iterator, m_satisfiedSteps) || + IsContainingStep(*iterator, m_executingSteps)) + { + return false; + } + } + + return true; + } + + bool AbortInternal() + { + // Clear all steps and construct + // a single-dependency list of abort steps + m_unsatisfiedSteps.clear(); + m_satisfiedSteps.clear(); + m_executingSteps.clear(); + m_historicSteps.clear(); + + if (m_abortSteps.empty()) + return false; + + // Register last abort step as satisfied + m_satisfiedSteps.push_back( + ConditionalStep( + m_abortSteps.top(), + StepList())); + + Step lastStep = m_abortSteps.top(); + m_abortSteps.pop(); + + // Create abort step list + while (!m_abortSteps.empty()) + { + // Add next unsatisfied step + StepList dependencies; + dependencies.push_back(lastStep); + + m_unsatisfiedSteps.push_back( + ConditionalStep( + m_abortSteps.top(), + dependencies)); + + // Remove top abort step + lastStep = m_abortSteps.top(); + m_abortSteps.pop(); + } + + return true; + } + +protected: + void AddStep(Step step, + const StepList &dependencies) + { + Mutex::ScopedLock lock(&m_dependencyListMutex); + + LogPedantic("Adding step: " << StepToString(step)); + + FOREACH (iterator, dependencies) + LogPedantic(" Dependency: " << StepToString(*iterator)); + + // Add step to proper list + if (IsStepDependecyListSatisfied(dependencies)) + { + m_satisfiedSteps.push_back(ConditionalStep(step, dependencies)); + + LogPedantic("Step " << StepToString(step) << " is satisfied"); + } + else + { + m_unsatisfiedSteps.push_back(ConditionalStep(step, dependencies)); + + LogPedantic("Step " << StepToString(step) << " is unsatisfied"); + } + + LogPedantic("Satisfied step count: " << m_satisfiedSteps.size()); + LogPedantic("Unsatisfied step count: " << m_unsatisfiedSteps.size()); + } + + void AddAbortStep(Step step) + { + Mutex::ScopedLock lock(&m_dependencyListMutex); + + m_abortSteps.push_front(step); + + LogPedantic("Abort step count: " << m_abortSteps.size()); + } + +public: + MultiTaskDecl(ImplementationType *implementation, + size_t maxParallelCount) + : m_activeStepsSemaphore(maxParallelCount), + m_implementation(implementation), + m_maxParallelCount(maxParallelCount) + { + } + + bool NextStep() + { + ConditionalStep stepToExecute; + typename ConditionalStepList::iterator executingStepIterator; + + // Take the main semaphore lock + Semaphore::ScopedLock semaphoreLock(&m_activeStepsSemaphore); + + // Take the dependency list lock + { + Mutex::ScopedLock listLock(&m_dependencyListMutex); + + // Get next step to execute + if (m_satisfiedSteps.empty()) + { + LogPedantic("No more satisfied steps to execute"); + return false; + } + + // Get next satisfied step to execute + stepToExecute = m_satisfiedSteps.front(); + m_satisfiedSteps.pop_front(); + + // Register it in executing step list + m_executingSteps.push_back(stepToExecute); + executingStepIterator = --m_executingSteps.end(); + + // Leave the dependency list lock + } + + // Execute step + (*m_implementation.*stepToExecute.step)(); + + // Take a lock again + { + Mutex::ScopedLock listLock(&m_dependencyListMutex); + + // Unregister executing step + m_executingSteps.erase(executingStepIterator); + + // Add historic step + m_historicSteps.push_back(stepToExecute); + + // Satisfy dependencies + SatisfyDependencies(stepToExecute); + + // Leave lock + } + + // Done + return true; + } + + bool Abort() + { + // Wait until all active steps are done + // This is achieved by taking all semaphore slots + ScopedArray semaphoreLocks( + new Semaphore::ScopedLock *[m_maxParallelCount]); + + for (size_t i = 0; i < m_maxParallelCount; ++i) + semaphoreLocks[i] = new Semaphore::ScopedLock( + &m_activeStepsSemaphore); + + // Result + bool result; + + // Take step lists lock + { + Mutex::ScopedLock mutexLock(&m_dependencyListMutex); + + // Do internal abort + result = AbortInternal(); + + // Leave steps list lock + } + + // Leave semaphore locks + for (size_t i = 0; i < m_maxParallelCount; ++i) + delete semaphoreLocks[i]; + + // Return result + return result; + } + + size_t GetStepCount() const + { + // Return sum of sizes of all lists + Mutex::ScopedLock lock(&m_dependencyListMutex); + + return static_cast( + m_unsatisfiedSteps.size() + + m_satisfiedSteps.size() + + m_executingSteps.size() + + m_historicSteps.size()); + } +}; +} // namespace DPL + +#endif // DPL_TASK_H diff --git a/modules/core/include/dpl/task_list.h b/modules/core/include/dpl/task_list.h new file mode 100644 index 0000000..b010dfc --- /dev/null +++ b/modules/core/include/dpl/task_list.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_list.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Radoslaw Wicik (r.wicik@samsung.com) + * @version 1.0 + * @brief Header file for task list + */ +#ifndef DPL_TASK_LIST_H +#define DPL_TASK_LIST_H + +#include +#include + +namespace DPL +{ +class TaskList + : public Task +{ +private: + typedef std::list Tasks; + + Tasks m_tasks; + Tasks::iterator m_currentTask; + bool m_switched; + + bool m_running; + +protected: + void AddTask(Task *task); + void SwitchToTask(Task *task); + +public: + TaskList(); + virtual ~TaskList(); + + bool NextStep(); + bool Abort(); + size_t GetTaskCount() const; + size_t GetStepCount() const; +}; +} // namespace DPL + +#endif // DPL_TASK_LIST_H diff --git a/modules/core/include/dpl/thread.h b/modules/core/include/dpl/thread.h new file mode 100644 index 0000000..b49ee09 --- /dev/null +++ b/modules/core/include/dpl/thread.h @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file thread.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of thread + */ +#ifndef DPL_THREAD_H +#define DPL_THREAD_H + +#include +//#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +class Thread + : private Noncopyable, + public WaitableHandleWatchSupport +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, CreateFailed) + DECLARE_EXCEPTION_TYPE(Base, DestroyFailed) + DECLARE_EXCEPTION_TYPE(Base, RunFailed) + DECLARE_EXCEPTION_TYPE(Base, QuitFailed) + DECLARE_EXCEPTION_TYPE(Base, UnmanagedThread) + }; + + typedef void (*EventDeleteProc)(void *event, void *userParam); + typedef void (*EventDispatchProc)(void *event, void *userParam); + +protected: + /** + * Main thread entry + * The method is intended to be overloaded with custom code. + * Default implementation just executes Exec method to process + * all thread exents + */ + virtual int ThreadEntry(); + + /** + * Start processing of thread events + */ + int Exec(); + +private: + struct InternalEvent + { + void *event; + void *userParam; + EventDispatchProc eventDispatchProc; + EventDeleteProc eventDeleteProc; + + InternalEvent(void *eventArg, + void *userParamArg, + EventDispatchProc eventDispatchProcArg, + EventDeleteProc eventDeleteProcArg) + : event(eventArg), + userParam(userParamArg), + eventDispatchProc(eventDispatchProcArg), + eventDeleteProc(eventDeleteProcArg) + { + } + }; + + struct InternalTimedEvent + : InternalEvent + { + unsigned long dueTimeMiliseconds; + unsigned long registerTimeMiliseconds; + + InternalTimedEvent(void *eventArg, + void *userParamArg, + unsigned long dueTimeMilisecondsArg, + unsigned long registerTimeMilisecondsArg, + EventDispatchProc eventDispatchProcArg, + EventDeleteProc eventDeleteProcArg) + : InternalEvent(eventArg, + userParamArg, + eventDispatchProcArg, + eventDeleteProcArg), + dueTimeMiliseconds(dueTimeMilisecondsArg), + registerTimeMiliseconds(registerTimeMilisecondsArg) + { + } + + bool operator<(const InternalTimedEvent &other) + { + return registerTimeMiliseconds + dueTimeMiliseconds > other.registerTimeMiliseconds + other.dueTimeMiliseconds; + } + }; + + // Internal event list + typedef std::list InternalEventList; + + // Internal timed event list + typedef std::vector InternalTimedEventVector; + + // State managment + pthread_t m_thread; + volatile bool m_abandon; + volatile bool m_running; + Mutex m_stateMutex; + WaitableEvent m_quitEvent; + + // Event processing + Mutex m_eventMutex; + InternalEventList m_eventList; + WaitableEvent m_eventInvoker; + + // Timed events processing + Mutex m_timedEventMutex; + InternalTimedEventVector m_timedEventVector; + WaitableEvent m_timedEventInvoker; + + // WaitableHandleWatchSupport + virtual Thread *GetInvokerThread(); + virtual void HandleDirectInvoker(); + bool m_directInvoke; + + // Internals + unsigned long GetCurrentTimeMiliseconds() const; + void ProcessEvents(); + void ProcessTimedEvents(); + + static void *StaticThreadEntry(void *param); + +public: + explicit Thread(); + virtual ~Thread(); + + /** + * Run thread. Does nothing if thread is already running + */ + void Run(); + + /** + * Send quit message to thread and wait for its end + * Does nothing is thread is not running + */ + void Quit(); + + /** + * Current thread retrieval + * Returns DPL thread handle or NULL if it is main program thread + */ + static Thread *GetCurrentThread(); + + /** + * Low-level event push, usually used only by EventSupport + */ + void PushEvent(void *event, EventDispatchProc eventDispatchProc, EventDeleteProc eventDeleteProc, void *userParam); + + /** + * Low-level timed event push, usually used only by EventSupport + */ + void PushTimedEvent(void *event, double dueTimeSeconds, EventDispatchProc eventDispatchProc, EventDeleteProc eventDeleteProc, void *userParam); + + /** + * Sleep for a number of seconds + */ + static void Sleep(uint64_t seconds); + + /** + * Sleep for a number of miliseconds + */ + static void MiliSleep(uint64_t miliseconds); + + /** + * Sleep for a number of microseconds + */ + static void MicroSleep(uint64_t microseconds); + + /** + * Sleep for a number of nanoseconds + */ + static void NanoSleep(uint64_t nanoseconds); +}; + +extern bool g_TLSforMainCreated; + +// In case of using TLV in main thread, pthread_exit(NULL) has to be called in +// this thread explicitly. +// On the other hand, possibly, because of the kernel bug, there exist +// a problem, if any other thread than main exist during pthread_exit call +// (process can become non-responsive) +// TODO further investigation is required. +template +class ThreadLocalVariable + : public Noncopyable +{ +public: + typedef Type ValueType; + + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, NullReference) + }; + +private: + pthread_key_t m_key; + + struct ManagedValue + { + ValueType value; + Optional guardKey; + }; + + static void MainThreadExitClean() + { + // There is a possible bug in kernel. If this function is called + // before ALL threads are closed, process will hang! + // Because of that, by default this function has to be called in well + // known "threads state". + + // pthread_exit(NULL); + } + + static void InternalDestroy(void *specific) + { + // Destroy underlying type + ManagedValue *instance = static_cast(specific); + if(instance->guardKey.IsNull()) + { + delete instance; + } + else + { + int result = pthread_setspecific(*(instance->guardKey), instance); + + Assert(result == 0 && + "Failed to set thread local variable"); + } + } + + Type &Reference(bool allowInstantiate = false) + { + ManagedValue *instance = + static_cast(pthread_getspecific(m_key)); + + if (!instance) + { + // Check if it is allowed to instantiate + if (!allowInstantiate) + Throw(typename Exception::NullReference); + + // checking, if specific data is created for Main thread + // If yes, pthread_exit(NULL) is required + if (!g_TLSforMainCreated) + { + if (Thread::GetCurrentThread() == NULL) + { + g_TLSforMainCreated = true; + atexit(&MainThreadExitClean); + } + } + + // Need to instantiate underlying type + instance = new ManagedValue(); + + int result = pthread_setspecific(m_key, instance); + + Assert(result == 0 && + "Failed to set thread local variable"); + } + + return instance->value; + } + +public: + ThreadLocalVariable() + { + int result = pthread_key_create(&m_key, &InternalDestroy); + + Assert(result == 0 && + "Failed to allocate thread local variable"); + } + + ~ThreadLocalVariable() + { + pthread_key_delete(m_key); + } + + Type &operator=(const Type &other) + { + Type &reference = Reference(true); + reference = other; + return reference; + } + + bool IsNull() const + { + return pthread_getspecific(m_key) == NULL; + } + + Type& operator*() + { + return Reference(); + } + + const Type& operator*() const + { + return Reference(); + } + + const Type* operator->() const + { + return &Reference(); + } + + Type* operator->() + { + return &Reference(); + } + + bool operator!() const + { + return IsNull(); + } + + void Reset() + { + ManagedValue *specific = + static_cast(pthread_getspecific(m_key)); + + if (!specific) + return; + + // TODO Should be an assert? is it developers fault to Reset Guarded + // value? + specific->guardKey = Optional::Null; + + InternalDestroy(specific); + + int result = pthread_setspecific(m_key, NULL); + + Assert(result == 0 && + "Failed to reset thread local variable"); + } + + // GuardValue(true) allows to defer destroy (by pthread internal + // functionality) thread specific value until GuardValue(false) will be + // called. + void GuardValue(bool guard) + { + ManagedValue *instance = + static_cast(pthread_getspecific(m_key)); + + Assert(instance && "Failed to get the value"); + + instance->guardKey = guard ? m_key : Optional::Null; + } +}; +} // namespace DPL + +#endif // DPL_THREAD_H diff --git a/modules/core/include/dpl/type_list.h b/modules/core/include/dpl/type_list.h new file mode 100644 index 0000000..e5c0f77 --- /dev/null +++ b/modules/core/include/dpl/type_list.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file type_list.h + * @author Bartosz Janiak (b.janiak@samsung.com) + * @version 1.0 + * @brief Generic type list template + */ +#ifndef DPL_TYPE_LIST_H +#define DPL_TYPE_LIST_H + +#include + +namespace DPL +{ +class TypeListGuard +{ +public: + template + struct Element + { + struct ERROR_TypeListElementIndexIsOutOfBounds; + typedef ERROR_TypeListElementIndexIsOutOfBounds Type; + }; + + static const size_t Size = 0; +}; + +template +class TypeList +{ +private: + class DummyClass + { + }; + + template + struct TypeCounter : public TypeCounter + { + }; + + template + struct TypeCounter + { + static const size_t Size = Enum; + }; + +public: + typedef TailType Tail; + typedef HeadType Head; + typedef TypeList ThisType; + + template + struct Element + { + typedef typename TailType::template Element::Type Type; + }; + + template + struct Element<0, DummyType> + { + typedef HeadType Type; + }; + + template + struct Contains + { + typedef typename TailType::template Contains::Yes Yes; + }; + + template + struct Contains + { + typedef int Yes; + }; + + static const size_t Size = TypeCounter::Size; +}; + +template +struct TypeListDecl +{ + typedef TypeList::Type> Type; +}; + +template<> +struct TypeListDecl +{ + typedef TypeListGuard Type; +}; +} // namespace DPL + +#endif // DPL_TYPE_LIST_H diff --git a/modules/core/include/dpl/union_cast.h b/modules/core/include/dpl/union_cast.h new file mode 100644 index 0000000..9576dbb --- /dev/null +++ b/modules/core/include/dpl/union_cast.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file union_cast.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Header file for union cast + */ +#ifndef DPL_UNION_CAST_H +#define DPL_UNION_CAST_H + +#include + +namespace DPL +{ +template +TargetType union_cast(const SourceType &data) +{ + union + { + SourceType source; + TargetType target; + } cast; + + std::memset(&cast, 0, sizeof(cast)); + + cast.source = data; + return cast.target; +} +} // namespace DPL + +#endif // DPL_UNION_CAST_H diff --git a/modules/core/include/dpl/unused.h b/modules/core/include/dpl/unused.h new file mode 100644 index 0000000..18729db --- /dev/null +++ b/modules/core/include/dpl/unused.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file unused.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of unused attribute from gcc + */ +#ifndef DPL_UNUSED_H +#define DPL_UNUSED_H + +#define DPL_UNUSED __attribute__((unused)) + +#endif // DPL_UNUSED_H diff --git a/modules/core/include/dpl/waitable_event.h b/modules/core/include/dpl/waitable_event.h new file mode 100644 index 0000000..ee53dfc --- /dev/null +++ b/modules/core/include/dpl/waitable_event.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file waitable_event.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of waitable event + */ +#ifndef DPL_WAITABLE_EVENT_H +#define DPL_WAITABLE_EVENT_H + +#include +#include +#include +#include + +namespace DPL +{ + +class WaitableEvent + : private Noncopyable +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, CreateFailed) + DECLARE_EXCEPTION_TYPE(Base, DestroyFailed) + DECLARE_EXCEPTION_TYPE(Base, SignalFailed) + DECLARE_EXCEPTION_TYPE(Base, ResetFailed) + }; + +private: + int m_pipe[2]; + +public: + WaitableEvent(); + virtual ~WaitableEvent(); + + WaitableHandle GetHandle() const; + + void Signal() const; + void Reset() const; +}; + +} // namespace DPL + +#endif // DPL_WAITABLE_EVENT_H diff --git a/modules/core/include/dpl/waitable_handle.h b/modules/core/include/dpl/waitable_handle.h new file mode 100644 index 0000000..865cca4 --- /dev/null +++ b/modules/core/include/dpl/waitable_handle.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file waitable_handle.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of waitable handle + */ +#ifndef DPL_WAITABLE_HANDLE_H +#define DPL_WAITABLE_HANDLE_H + +#include +#include +#include + +namespace DPL +{ + +/** + * Waitable unix wait handle definition + */ +typedef int WaitableHandle; + +/** + * Waitable handle list + */ +typedef std::vector WaitableHandleList; + +/** + * Wait mode + */ +class WaitMode +{ +public: + enum Type + { + Read, ///< Wait for readability state changes + Write ///< Wait for writability state changes + }; +}; + +/** + * Waitable handle list ex + */ +typedef std::vector > WaitableHandleListEx; + +/** + * Waitable handle index list + */ +typedef std::vector WaitableHandleIndexList; + +/** + * Wait exceptions + */ +DECLARE_EXCEPTION_TYPE(DPL::Exception, WaitFailed) + +/** + * Wait for single handle readability + * Convience function. + * + * @return Signaled waitable handle index list + * @throw WaitFailed Fatal error occurred while waiting for signal + */ +WaitableHandleIndexList WaitForSingleHandle(WaitableHandle handle, unsigned long miliseconds = 0xFFFFFFFF); + +/** + * Wait for single handle + * Convience function. + * + * @return Signaled waitable handle index list + * @throw WaitFailed Fatal error occurred while waiting for signal + */ +WaitableHandleIndexList WaitForSingleHandle(WaitableHandle handle, WaitMode::Type mode, unsigned long miliseconds = 0xFFFFFFFF); + +/** + * Wait for multiple handles readability + * + * @return Signaled waitable handle index list + * @throw WaitFailed Fatal error occurred while waiting for signal + */ +WaitableHandleIndexList WaitForMultipleHandles(const WaitableHandleList &handleList, unsigned long miliseconds = 0xFFFFFFFF); + +/** + * Wait for multiple handles readability + * + * @return Signaled waitable handle index list + * @throw WaitFailed Fatal error occurred while waiting for signal + */ +WaitableHandleIndexList WaitForMultipleHandles(const WaitableHandleListEx &handleListEx, unsigned long miliseconds = 0xFFFFFFFF); + +} // namespace DPL + +#endif // DPL_WAITABLE_HANDLE_H diff --git a/modules/core/include/dpl/waitable_handle_watch_support.h b/modules/core/include/dpl/waitable_handle_watch_support.h new file mode 100644 index 0000000..0ff29b1 --- /dev/null +++ b/modules/core/include/dpl/waitable_handle_watch_support.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file waitable_handle_watch_support.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of waitable handle watch support + */ +#ifndef DPL_WAITABLE_HANDLE_WATCH_SUPPORT_H +#define DPL_WAITABLE_HANDLE_WATCH_SUPPORT_H + +#include +#include +#include +#include +#include +#include + +namespace DPL +{ + +class Thread; + +class WaitableHandleWatchSupport +{ +public: + class WaitableHandleListener + { + public: + virtual ~WaitableHandleListener() {} + + virtual void OnWaitableHandleEvent(WaitableHandle waitableHandle, WaitMode::Type mode) = 0; + }; + +protected: + // Invoker waitable handle + // Signaled by Add/Remove methods + // After being signaled one must call Handle invoke to reset invoker + WaitableHandle WaitableInvokerHandle() const; + + // Waitable handle ex list + WaitableHandleListEx WaitableWatcherHandles() const; + + // Perform actions for signaled waitable handle + // Called in execution context, after + void HandleWatcher(WaitableHandle waitableHandle, WaitMode::Type mode); + + // Perform actions after invoker was signaled + void InvokerFinished(); + + // Get invoker context + virtual Thread *GetInvokerThread() = 0; + + // Invoke direct invoker + virtual void HandleDirectInvoker() = 0; + +private: + // Waitable event watchers + struct WaitableHandleWatcher + { + WaitableHandleListener *listener; + WaitMode::Type mode; + + WaitableHandleWatcher(WaitableHandleListener *l, WaitMode::Type m) + : listener(l), + mode(m) + { + } + }; + + typedef std::list WaitableHandleListenerList; + + struct WaitableHandleWatchers + { + WaitableHandleListenerList listeners; + size_t readListenersCount; + size_t writeListenersCount; + + WaitableHandleWatchers() + : readListenersCount(0), + writeListenersCount(0) + { + } + }; + + typedef std::map WaitableHandleWatchersMap; + + // Waitable event watch support + mutable RecursiveMutex m_watchersMutex; + WaitableHandleWatchersMap m_watchersMap; + WaitableEvent m_watchersInvoker; + WaitableEvent m_watchersInvokerCommit; + + // Invoke call + void CommitInvoker(); + +public: + /** + * Constructor + */ + explicit WaitableHandleWatchSupport(); + + /** + * Destructor + */ + virtual ~WaitableHandleWatchSupport(); + + /** + * Adds listener for specific waitable event + * + * @param[in] listener Listener to attach + * @param[in] waitableHandle Waitable handle to listen for changes + * @param[in] mode Type of changes to listen to + * @return none + * @see WaitMode::Type + */ + void AddWaitableHandleWatch(WaitableHandleListener *listener, WaitableHandle waitableHandle, WaitMode::Type mode); + + /** + * Remove listener for specific waitable event + * + * @param[in] listener Listener to detach + * @param[in] waitableHandle Waitable handle to unlisten for changes + * @param[in] mode Type of changes to unlisten to + * @return none + * @see WaitMode::Type + */ + void RemoveWaitableHandleWatch(WaitableHandleListener *listener, WaitableHandle waitableHandle, WaitMode::Type mode); + + /** + * Retrieve inherited context + * + * @return Inherited waitable handle watch support + */ + static WaitableHandleWatchSupport *InheritedContext(); +}; + +} // namespace DPL + +#endif // DPL_WAITABLE_HANDLE_WATCH_SUPPORT_H diff --git a/modules/core/include/dpl/workaround.h b/modules/core/include/dpl/workaround.h new file mode 100644 index 0000000..19c26ef --- /dev/null +++ b/modules/core/include/dpl/workaround.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file workaround.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of workaround + */ +#ifndef DPL_WORKAROUND_H +#define DPL_WORKAROUND_H + +/** + * Define following macro to track invalid waitable handles + * in WaitForSingle/WaitForMultiple functions + */ +#define DPL_ENABLE_WAITABLE_HANDLE_BADF_CHECK + +/** + * Define following macro to enable workaround for problem + * with GLIB loop integration and EBADF error handling + */ +#define DPL_ENABLE_GLIB_LOOP_INTEGRATION_WORKAROUND + +/** + * Define following macro to enable workaround for problem + * with invalid conversions in htons/ntohs macros + */ +#define DPL_ENABLE_HTONS_NTOHS_I386_WORKAROUND + +#endif // DPL_WORKAROUND_H diff --git a/modules/core/include/dpl/zip_input.h b/modules/core/include/dpl/zip_input.h new file mode 100644 index 0000000..f4b8e34 --- /dev/null +++ b/modules/core/include/dpl/zip_input.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file zip_input.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of zip input + */ +#ifndef DPL_ZIP_INPUT_H +#define DPL_ZIP_INPUT_H + +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +class ZipInput + : private Noncopyable +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, OpenFailed) + DECLARE_EXCEPTION_TYPE(Base, ReadGlobalInfoFailed) + DECLARE_EXCEPTION_TYPE(Base, ReadGlobalCommentFailed) + DECLARE_EXCEPTION_TYPE(Base, SeekFileFailed) + DECLARE_EXCEPTION_TYPE(Base, FileInfoFailed) + DECLARE_EXCEPTION_TYPE(Base, OpenFileFailed) + DECLARE_EXCEPTION_TYPE(Base, ReadFileFailed) + }; + + typedef std::pair FileHandle; + + struct FileDateTime + { + unsigned int second; //< seconds after the minute - [0,59] + unsigned int minute; //< minutes after the hour - [0,59] + unsigned int hour; //< hours since midnight - [0,23] + unsigned int day; //< day of the month - [1,31] + unsigned int month; //< months since January - [0,11] + unsigned int year; //< years - [1980..2044] + + FileDateTime() + : second(0), + minute(0), + hour(0), + day(0), + month(0), + year(0) + { + } + + FileDateTime(unsigned int secondArg, + unsigned int minuteArg, + unsigned int hourArg, + unsigned int dayArg, + unsigned int monthArg, + unsigned int yearArg) + : second(secondArg), + minute(minuteArg), + hour(hourArg), + day(dayArg), + month(monthArg), + year(yearArg) + { + } + }; + + struct FileInfo + { + // File handle + FileHandle handle; + + // File name and comment + std::string name; + std::string comment; + + // File information + unsigned long version; //< version made by + unsigned long versionNeeded; //< version needed to extract + unsigned long flag; //< general purpose bit flag + unsigned long compressionMethod; //< compression method + unsigned long dosDate; //< last mod file date in Dos fmt + unsigned long crc; //< crc-32 + off64_t compressedSize; //< compressed size + off64_t uncompressedSize; //< uncompressed size + unsigned long diskNumberStart; //< disk number start + unsigned long internalFileAttributes; //< internal file attributes + unsigned long externalFileAttributes; //< external file attributes + + FileDateTime dateTime; + + FileInfo() + : handle(), + name(), + comment(), + version(0), + versionNeeded(0), + flag(0), + compressionMethod(0), + dosDate(0), + crc(0), + compressedSize(0), + uncompressedSize(0), + diskNumberStart(0), + internalFileAttributes(0), + externalFileAttributes(0), + dateTime() + { + } + + FileInfo(const FileHandle &handleArg, + const std::string &nameArg, + const std::string &commentArg, + unsigned long versionArg, + unsigned long versionNeededArg, + unsigned long flagArg, + unsigned long compressionMethodArg, + unsigned long dosDateArg, + unsigned long crcArg, + const off64_t &compressedSizeArg, + const off64_t &uncompressedSizeArg, + unsigned long diskNumberStartArg, + unsigned long internalFileAttributesArg, + unsigned long externalFileAttributesArg, + const FileDateTime &dateTimeArg) + : handle(handleArg), + name(nameArg), + comment(commentArg), + version(versionArg), + versionNeeded(versionNeededArg), + flag(flagArg), + compressionMethod(compressionMethodArg), + dosDate(dosDateArg), + crc(crcArg), + compressedSize(compressedSizeArg), + uncompressedSize(uncompressedSizeArg), + diskNumberStart(diskNumberStartArg), + internalFileAttributes(internalFileAttributesArg), + externalFileAttributes(externalFileAttributesArg), + dateTime(dateTimeArg) + { + } + }; + + class File + : public DPL::AbstractInput + { + private: + void *m_file; + + friend class ZipInput; + File(class Device *device, FileHandle handle); + + public: + ~File(); + + virtual DPL::BinaryQueueAutoPtr Read(size_t size); + }; + +private: + class Device *m_device; + void *m_masterFile; + + size_t m_numberOfFiles; + size_t m_globalCommentSize; + std::string m_globalComment; + + // At least cache handles + typedef std::vector FileInfoList; + FileInfoList m_fileInfos; + + void ReadGlobalInfo(void *masterFile); + void ReadGlobalComment(void *masterFile); + void ReadInfos(void *masterFile); + +public: + typedef FileInfoList::const_iterator const_iterator; + typedef FileInfoList::const_reverse_iterator const_reverse_iterator; + typedef FileInfoList::size_type size_type; + +public: + /** + * Open zip file from file + */ + explicit ZipInput(const std::string &fileName); + + /** + * Destructor + */ + ~ZipInput(); + + // Iterators + const_iterator begin() const; + const_iterator end() const; + + const_reverse_iterator rbegin() const; + const_reverse_iterator rend() const; + + // Size, empty + size_type size() const; + bool empty() const; + + /** + * Open a binary file for given file handle + * + * @return file object + * @param[in] handle Zip file handle to open + * @exception std::bad_alloc Cannot allocate memory to hold additional data + * @exception SteamOpenFailed Cannot find file with given handle + * @see BinaryQueue::BufferDeleterFree + */ + File *OpenFile(FileHandle handle); + + /** + * Open a binary file for given file name + * + * @return file object + * @param[in] fileName Zip file name to open + * @exception std::bad_alloc Cannot allocate memory to hold additional data + * @exception SteamOpenFailed Cannot find file with given handle + * @see BinaryQueue::BufferDeleterFree + */ + File *OpenFile(const std::string &fileName); + + /** + * Get archive global comment + * + * @return Global archive comment + */ + const std::string &GetGlobalComment() const; +}; +} // namespace DPL + +#endif // DPL_ZIP_INPUT_H diff --git a/modules/core/src/DESCRIPTION b/modules/core/src/DESCRIPTION new file mode 100644 index 0000000..9043c93 --- /dev/null +++ b/modules/core/src/DESCRIPTION @@ -0,0 +1 @@ +Source files diff --git a/modules/core/src/abstract_waitable_input_adapter.cpp b/modules/core/src/abstract_waitable_input_adapter.cpp new file mode 100644 index 0000000..e7964ea --- /dev/null +++ b/modules/core/src/abstract_waitable_input_adapter.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_waitable_input_adapter.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of abstract waitable input adapter + */ +#include + +namespace DPL +{ + +AbstractWaitableInputAdapter::AbstractWaitableInputAdapter(AbstractInput *input) + : m_input(input) +{ + m_waitableEvent.Signal(); +} + +BinaryQueueAutoPtr AbstractWaitableInputAdapter::Read(size_t size) +{ + return m_input->Read(size); +} + +WaitableHandle AbstractWaitableInputAdapter::WaitableReadHandle() const +{ + return m_waitableEvent.GetHandle(); +} + +} // namespace DPL diff --git a/modules/core/src/abstract_waitable_input_output_adapter.cpp b/modules/core/src/abstract_waitable_input_output_adapter.cpp new file mode 100644 index 0000000..d07737b --- /dev/null +++ b/modules/core/src/abstract_waitable_input_output_adapter.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_waitable_input_output_adapter.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of abstract waitable input output adapter + */ +#include + +namespace DPL +{ + +AbstractWaitableInputOutputAdapter::AbstractWaitableInputOutputAdapter(AbstractInputOutput *inputOutput) + : AbstractWaitableInputAdapter(inputOutput), + AbstractWaitableOutputAdapter(inputOutput) +{ +} + +} // namespace DPL diff --git a/modules/core/src/abstract_waitable_output_adapter.cpp b/modules/core/src/abstract_waitable_output_adapter.cpp new file mode 100644 index 0000000..f849579 --- /dev/null +++ b/modules/core/src/abstract_waitable_output_adapter.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_waitable_output_adapter.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of abstract waitable output adapter + */ +#include + +namespace DPL +{ + +AbstractWaitableOutputAdapter::AbstractWaitableOutputAdapter(AbstractOutput *output) + : m_output(output) +{ + m_waitableEvent.Signal(); +} + +size_t AbstractWaitableOutputAdapter::Write(const BinaryQueue &buffer, size_t bufferSize) +{ + return m_output->Write(buffer, bufferSize); +} + +WaitableHandle AbstractWaitableOutputAdapter::WaitableWriteHandle() const +{ + return m_waitableEvent.GetHandle(); +} + +} // namespace DPL diff --git a/modules/core/src/address.cpp b/modules/core/src/address.cpp new file mode 100644 index 0000000..624fe7e --- /dev/null +++ b/modules/core/src/address.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file address.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of address + */ +#include +#include +#include + +namespace DPL +{ +Address::Address() + : m_port(0) +{ +} + +Address::Address(const std::string &address) + : m_address(address), + m_port(0) +{ +} + +Address::Address(const std::string &address, unsigned short port) + : m_address(address), + m_port(port) +{ +} + +Address::~Address() +{ +} + +std::string Address::GetAddress() const +{ + return m_address; +} + +unsigned short Address::GetPort() const +{ + return m_port; +} + +std::string Address::ToString() const +{ + std::ostringstream out; + out << m_address << ":" << m_port; + return out.str(); +} + +bool Address::operator<(const Address &addr) const +{ + return ToString() < addr.ToString(); +} + +} // namespace DPL diff --git a/modules/core/src/application.cpp b/modules/core/src/application.cpp new file mode 100644 index 0000000..f4a2147 --- /dev/null +++ b/modules/core/src/application.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file application.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of MVC application support + */ +#include +#include + +namespace // anonymous +{ +static DPL::Application *g_application = NULL; +} // namespace anonymous + +namespace DPL +{ +int Application::app_create(void *data) +{ + Application *This=static_cast(data); + This->OnCreate(); + return 0; +} + +int Application::app_terminate(void *data) +{ + Application *This=static_cast(data); + This->OnTerminate(); + return 0; +} + +int Application::app_pause(void *data) +{ + Application *This=static_cast(data); + This->OnPause(); + return 0; +} + +int Application::app_resume(void *data) +{ + Application *This=static_cast(data); + This->OnResume(); + return 0; +} + +int Application::app_reset(bundle *b, void *data) +{ + Application *This=static_cast(data); + This->OnReset(b); + return 0; +} + +Application::Application(int argc, char** argv, + const std::string& applicationName, + bool showMainWindow) + : m_argc(argc), + m_argv(argv), + m_applicationName(applicationName), + m_mainWindowVisible(showMainWindow) +{ + if (g_application != NULL) + ThrowMsg(Exception::TooManyInstances, "Only single instance of Application allowed at one time!"); + + g_application = this; +} + +Application::~Application() +{ + g_application = NULL; +} + +int Application::Exec() +{ + LogPedantic("Starting application framework..."); + + struct appcore_ops ops; + ops.create = app_create; + ops.terminate = app_terminate; + ops.pause = app_pause; + ops.resume = app_resume; + ops.reset = app_reset; + ops.data=this; + + int result = appcore_efl_main(m_applicationName.c_str(), &m_argc, &m_argv, &ops); + + LogPedantic("Exited application framework"); + + return result; +} + +void Application::OnCreate() +{ + LogPedantic("On application create"); +} + +void Application::OnStart() +{ + LogPedantic("On application start"); +} + +void Application::OnStop() +{ + LogPedantic("On application stop"); +} + +void Application::OnResume() +{ + LogPedantic("On application resume"); +} + +void Application::OnPause() +{ + LogPedantic("On application pause"); +} + +void Application::OnRelaunch() +{ + LogPedantic("On application relaunch"); +} + +void Application::OnReset(bundle *b) +{ + (void)b; + LogPedantic("On application reset"); +} + +void Application::OnTerminate() +{ + LogPedantic("On application terminate"); +} + +void Application::OnLowMemory() +{ + LogPedantic("On application low memory"); +} + +void Application::OnLowBattery() +{ + LogPedantic("On application low battery"); +} + +void Application::OnLanguageChanged() +{ + LogPedantic("On application language changed"); +} + +void Application::Quit() +{ + elm_exit(); +} + +DPL::Atomic ApplicationExt::m_useCount(0); + +ApplicationExt::ApplicationExt(int argc, char** argv, const std::string& applicationName, bool showMainWindow) : + Application(argc, argv, applicationName, showMainWindow) +{ +} + +ApplicationExt::~ApplicationExt() +{ +} + +int ApplicationExt::Exec() +{ + if (0 == m_useCount.CompareAndExchange(0, 1)) + { + return Application::Exec(); + } + else + { + elm_run(); + } + return 0; +} + +void ApplicationExt::Quit() +{ + elm_exit(); +} +} // namespace DPL diff --git a/modules/core/src/apply.cpp b/modules/core/src/apply.cpp new file mode 100644 index 0000000..d299b57 --- /dev/null +++ b/modules/core/src/apply.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file apply.cpp + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of apply functionality + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/core/src/assert.cpp b/modules/core/src/assert.cpp new file mode 100644 index 0000000..9a363dc --- /dev/null +++ b/modules/core/src/assert.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file assert.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of assert + */ +#include +#include +#include +#include + +namespace DPL +{ +void AssertProc(const char *condition, const char *file, int line, const char *function) +{ +#define INTERNAL_LOG(message) \ + do \ + { \ + std::ostringstream platformLog; \ + platformLog << message; \ + DPL::Log::LogSystemSingleton::Instance().Pedantic( \ + platformLog.str().c_str(), \ + __FILE__, __LINE__, __FUNCTION__); \ + } while (0) + + // Try to log failed assertion to log system + Try + { + INTERNAL_LOG("################################################################################"); + INTERNAL_LOG("### DPL assertion failed! ###"); + INTERNAL_LOG("################################################################################"); + INTERNAL_LOG("### Condition: " << condition); + INTERNAL_LOG("### File: " << file); + INTERNAL_LOG("### Line: " << line); + INTERNAL_LOG("### Function: " << function); + INTERNAL_LOG("################################################################################"); + } + catch (Exception) + { + // Just ignore possible double errors + } + + // Fail with c-library abort + abort(); +} +} // namespace DPL diff --git a/modules/core/src/atomic.cpp b/modules/core/src/atomic.cpp new file mode 100644 index 0000000..65af06e --- /dev/null +++ b/modules/core/src/atomic.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file atomic.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of atomic + */ +#include + +namespace DPL +{ +Atomic::Atomic(ValueType value) + : m_value(value) +{ +} + +Atomic::ValueType Atomic::ExchangeAndAdd(ValueType value) +{ + return g_atomic_int_exchange_and_add(&m_value, value); +} + +bool Atomic::CompareAndExchange(ValueType oldValue, ValueType newValue) +{ + return g_atomic_int_compare_and_exchange(&m_value, oldValue, newValue); +} + +bool Atomic::operator--() +{ + return g_atomic_int_dec_and_test(&m_value) != TRUE; +} + +void Atomic::operator++() +{ + g_atomic_int_inc(&m_value); +} + +Atomic::operator ValueType() const +{ + return g_atomic_int_get(&m_value); +} +} // namespace DPL diff --git a/modules/core/src/binary_queue.cpp b/modules/core/src/binary_queue.cpp new file mode 100644 index 0000000..42b351e --- /dev/null +++ b/modules/core/src/binary_queue.cpp @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file binary_queue.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of binary queue + */ +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +BinaryQueue::BinaryQueue() + : m_size(0) +{ +} + +BinaryQueue::BinaryQueue(const BinaryQueue &other) + : m_size(0) +{ + AppendCopyFrom(other); +} + +BinaryQueue::~BinaryQueue() +{ + // Remove all remainig buckets + Clear(); +} + +const BinaryQueue &BinaryQueue::operator=(const BinaryQueue &other) +{ + if (this != &other) + { + Clear(); + AppendCopyFrom(other); + } + + return *this; +} + +void BinaryQueue::AppendCopyFrom(const BinaryQueue &other) +{ + // To speed things up, always copy as one bucket + void *bufferCopy = malloc(other.m_size); + + if (bufferCopy == NULL) + throw std::bad_alloc(); + + try + { + other.Flatten(bufferCopy, other.m_size); + AppendUnmanaged(bufferCopy, other.m_size, &BufferDeleterFree, NULL); + } + catch (const std::bad_alloc &) + { + // Free allocated memory + free(bufferCopy); + throw; + } +} + +void BinaryQueue::AppendMoveFrom(BinaryQueue &other) +{ + // Copy all buckets + std::copy(other.m_buckets.begin(), other.m_buckets.end(), std::back_inserter(m_buckets)); + m_size += other.m_size; + + // Clear other, but do not free memory + other.m_buckets.clear(); + other.m_size = 0; +} + +void BinaryQueue::AppendCopyTo(BinaryQueue &other) const +{ + other.AppendCopyFrom(*this); +} + +void BinaryQueue::AppendMoveTo(BinaryQueue &other) +{ + other.AppendMoveFrom(*this); +} + +void BinaryQueue::Clear() +{ + std::for_each(m_buckets.begin(), m_buckets.end(), &DeleteBucket); + m_buckets.clear(); + m_size = 0; +} + +void BinaryQueue::AppendCopy(const void* buffer, size_t bufferSize) +{ + // Create data copy with malloc/free + void *bufferCopy = malloc(bufferSize); + + // Check if allocation succeded + if (bufferCopy == NULL) + throw std::bad_alloc(); + + // Copy user data + memcpy(bufferCopy, buffer, bufferSize); + + try + { + // Try to append new bucket + AppendUnmanaged(bufferCopy, bufferSize, &BufferDeleterFree, NULL); + } + catch (const std::bad_alloc &) + { + // Free allocated memory + free(bufferCopy); + throw; + } +} + +void BinaryQueue::AppendUnmanaged(const void* buffer, size_t bufferSize, BufferDeleter deleter, void* userParam) +{ + // Do not attach empty buckets + if (bufferSize == 0) + { + deleter(buffer, bufferSize, userParam); + return; + } + + // Just add new bucket with selected deleter + m_buckets.push_back(new Bucket(buffer, bufferSize, deleter, userParam)); + + // Increase total queue size + m_size += bufferSize; +} + +size_t BinaryQueue::Size() const +{ + return m_size; +} + +bool BinaryQueue::Empty() const +{ + return m_size == 0; +} + +void BinaryQueue::Consume(size_t size) +{ + // Check parameters + if (size > m_size) + Throw(Exception::OutOfData); + + size_t bytesLeft = size; + + // Consume data and/or remove buckets + while (bytesLeft > 0) + { + // Get consume size + size_t count = std::min(bytesLeft, m_buckets.front()->left); + + m_buckets.front()->ptr = static_cast(m_buckets.front()->ptr) + count; + m_buckets.front()->left -= count; + bytesLeft -= count; + m_size -= count; + + if (m_buckets.front()->left == 0) + { + DeleteBucket(m_buckets.front()); + m_buckets.pop_front(); + } + } +} + +void BinaryQueue::Flatten(void *buffer, size_t bufferSize) const +{ + // Check parameters + if (bufferSize == 0) + return; + + if (bufferSize > m_size) + Throw(Exception::OutOfData); + + size_t bytesLeft = bufferSize; + void *ptr = buffer; + BucketList::const_iterator bucketIterator = m_buckets.begin(); + Assert(m_buckets.end() != bucketIterator); + + // Flatten data + while (bytesLeft > 0) + { + // Get consume size + size_t count = std::min(bytesLeft, (*bucketIterator)->left); + + // Copy data to user pointer + memcpy(ptr, (*bucketIterator)->ptr, count); + + // Update flattened bytes count + bytesLeft -= count; + ptr = static_cast(ptr) + count; + + // Take next bucket + ++bucketIterator; + } +} + +void BinaryQueue::FlattenConsume(void *buffer, size_t bufferSize) +{ + // FIXME: Optimize + Flatten(buffer, bufferSize); + Consume(bufferSize); +} + +void BinaryQueue::DeleteBucket(BinaryQueue::Bucket *bucket) +{ + delete bucket; +} + +void BinaryQueue::BufferDeleterFree(const void* data, size_t dataSize, void* userParam) +{ + (void)dataSize; + (void)userParam; + + // Default free deleter + free(const_cast(data)); +} + +BinaryQueue::Bucket::Bucket(const void* data, size_t dataSize, BufferDeleter dataDeleter, void* userParam) + : buffer(data), + ptr(data), + size(dataSize), + left(dataSize), + deleter(dataDeleter), + param(userParam) +{ + Assert(data != NULL); + Assert(deleter != NULL); +} + +BinaryQueue::Bucket::~Bucket() +{ + // Invoke deleter on bucket data + deleter(buffer, size, param); +} + +BinaryQueue::BucketVisitor::~BucketVisitor() +{ +} + +BinaryQueue::BucketVisitorCall::BucketVisitorCall(BucketVisitor *visitor) + : m_visitor(visitor) +{ +} + +BinaryQueue::BucketVisitorCall::~BucketVisitorCall() +{ +} + +void BinaryQueue::BucketVisitorCall::operator()(Bucket *bucket) const +{ + m_visitor->OnVisitBucket(bucket->ptr, bucket->left); +} + +void BinaryQueue::VisitBuckets(BucketVisitor *visitor) const +{ + Assert(visitor != NULL); + + // Visit all buckets + std::for_each(m_buckets.begin(), m_buckets.end(), BucketVisitorCall(visitor)); +} + +BinaryQueueAutoPtr BinaryQueue::Read(size_t size) +{ + // Simulate input stream + size_t available = std::min(size, m_size); + + ScopedFree bufferCopy(malloc(available)); + + if (!bufferCopy) + throw std::bad_alloc(); + + BinaryQueueAutoPtr result(new BinaryQueue()); + + Flatten(bufferCopy.Get(), available); + result->AppendUnmanaged(bufferCopy.Get(), available, &BufferDeleterFree, NULL); + bufferCopy.Release(); + Consume(available); + + return result; +} + +size_t BinaryQueue::Write(const BinaryQueue &buffer, size_t bufferSize) +{ + // Simulate output stream + AppendCopyFrom(buffer); + return bufferSize; +} +} // namespace DPL diff --git a/modules/core/src/char_traits.cpp b/modules/core/src/char_traits.cpp new file mode 100644 index 0000000..13ea72e --- /dev/null +++ b/modules/core/src/char_traits.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file char_traits.cpp + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @version 1.0 + * @biref Char traits are used to create basic_string extended with additional features + * Current char traits could be extended in feature to boost performance + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/core/src/colors.cpp b/modules/core/src/colors.cpp new file mode 100644 index 0000000..19e6d3a --- /dev/null +++ b/modules/core/src/colors.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file colors.cpp + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief Some constants with definition of colors for Console + * and html output + */ + +#include + + +namespace DPL +{ + +namespace Colors +{ + +namespace Text +{ + +const char* BOLD_GREEN_BEGIN = "\033[1;32m"; +const char* BOLD_GREEN_END = "\033[m"; +const char* RED_BEGIN = "\033[0;31m"; +const char* RED_END = "\033[m"; +const char* PURPLE_BEGIN = "\033[0;35m"; +const char* PURPLE_END = "\033[m"; +const char* GREEN_BEGIN = "\033[0;32m"; +const char* GREEN_END = "\033[m"; +const char* CYAN_BEGIN = "\033[0;36m"; +const char* CYAN_END = "\033[m"; +const char* BOLD_RED_BEGIN = "\033[1;31m"; +const char* BOLD_RED_END = "\033[m"; +const char* BOLD_YELLOW_BEGIN = "\033[1;33m"; +const char* BOLD_YELLOW_END = "\033[m"; +const char* BOLD_GOLD_BEGIN = "\033[0;33m"; +const char* BOLD_GOLD_END = "\033[m"; +const char* BOLD_WHITE_BEGIN = "\033[1;37m"; +const char* BOLD_WHITE_END = "\033[m"; + +} //namespace Text + +namespace Html +{ + +const char* BOLD_GREEN_BEGIN = ""; +const char* BOLD_GREEN_END = ""; +const char* PURPLE_BEGIN = ""; +const char* PURPLE_END = ""; +const char* RED_BEGIN = ""; +const char* RED_END = ""; +const char* GREEN_BEGIN = ""; +const char* GREEN_END = ""; +const char* CYAN_BEGIN = ""; +const char* CYAN_END = ""; +const char* BOLD_RED_BEGIN = ""; +const char* BOLD_RED_END = ""; +const char* BOLD_YELLOW_BEGIN = ""; +const char* BOLD_YELLOW_END = ""; +const char* BOLD_GOLD_BEGIN = ""; +const char* BOLD_GOLD_END = ""; +const char* BOLD_WHITE_BEGIN = ""; +const char* BOLD_WHITE_END = ""; + +} //namespace Html + +} //namespace Colors + +} //namespace DPL diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp new file mode 100644 index 0000000..465a9b9 --- /dev/null +++ b/modules/core/src/copy.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file copy.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of copy + */ +#include +#include +#include + +namespace DPL +{ +namespace // anonymous +{ +const size_t DEFAULT_COPY_BUFFER_SIZE = 16768; +} // namespace anonymous + +void Copy(AbstractWaitableInput *input, AbstractWaitableOutput *output) +{ + Try + { + while (true) + { + BinaryQueueAutoPtr buffer; + + while (true) + { + // Try to get data immediately + buffer = input->Read(DEFAULT_COPY_BUFFER_SIZE); + + // Do we need to wait for data ? + if (!buffer.get()) + { + WaitForSingleHandle(input->WaitableReadHandle(), WaitMode::Read); + continue; + } + + if (buffer->Empty()) + return; // Done + + // Ok, to process + break; + } + + // Write out all data + while (!buffer->Empty()) + { + // Try to write all data immediately + size_t count = output->Write(*buffer, buffer->Size()); + + // Do we need to wait for writing data ? + if (count == 0) + { + WaitForSingleHandle(output->WaitableWriteHandle(), WaitMode::Write); + continue; + } + + // Consume data + buffer->Consume(count); + } + } + } + Catch (DPL::Exception) + { + ReThrow(CopyFailed); + } +} + +void Copy(AbstractWaitableInput *input, AbstractWaitableOutput *output, size_t totalBytes) +{ + Try + { + size_t bytesLeft = totalBytes; + + while (bytesLeft > 0) + { + BinaryQueueAutoPtr buffer; + + // Copy at most left bytes + size_t bytesToCopy = bytesLeft > DEFAULT_COPY_BUFFER_SIZE ? DEFAULT_COPY_BUFFER_SIZE : bytesLeft; + + while (true) + { + // Try to get data immediately + buffer = input->Read(bytesToCopy); + + // Do we need to wait for data ? + if (!buffer.get()) + { + WaitForSingleHandle(input->WaitableReadHandle(), WaitMode::Read); + continue; + } + + if (buffer->Empty()) + ThrowMsg(CopyFailed, "Unexpected end of abstract input"); + + // Ok, to process + break; + } + + // Write out all data + while (!buffer->Empty()) + { + // Try to write all data immediately + size_t count = output->Write(*buffer, buffer->Size()); + + // Do we need to wait for writing data ? + if (count == 0) + { + WaitForSingleHandle(output->WaitableWriteHandle(), WaitMode::Write); + continue; + } + + // Consume data + buffer->Consume(count); + bytesLeft -= count; + } + } + } + Catch (DPL::Exception) + { + ReThrow(CopyFailed); + } +} +} // namespace DPL diff --git a/modules/core/src/errno_string.cpp b/modules/core/src/errno_string.cpp new file mode 100644 index 0000000..1637d87 --- /dev/null +++ b/modules/core/src/errno_string.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file errno_string.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of errno string + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace // anonymous +{ +const size_t DEFAULT_ERRNO_STRING_SIZE = 32; +} // namespace anonymous + +std::string GetErrnoString(int error) +{ + size_t size = DEFAULT_ERRNO_STRING_SIZE; + char *buffer = NULL; + + for (;;) + { + // Add one extra characted for end of string null value + char *newBuffer = static_cast(::realloc(buffer, size + 1)); + + if (!newBuffer) + { + // Failed to realloc + ::free(buffer); + throw std::bad_alloc(); + } + + // Setup reallocated buffer + buffer = newBuffer; + ::memset(buffer, 0, size + 1); + + // Try to retrieve error string +#if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE + // The XSI-compliant version of strerror_r() is provided if: + int result = ::strerror_r(error, buffer, size); + + if (result == 0) + { + ScopedFree scopedBufferFree(buffer); + return std::string(buffer); + } +#else + errno = 0; + + // Otherwise, the GNU-specific version is provided. + char *result = ::strerror_r(error, buffer, size); + + if (result != NULL) + { + ScopedFree scopedBufferFree(buffer); + return std::string(result); + } +#endif + + // Interpret errors + switch (errno) + { + case EINVAL: + // We got an invalid errno value + ::free(buffer); + ThrowMsg(InvalidErrnoValue, "Invalid errno value: " << error); + + case ERANGE: + // Incease buffer size and retry + size <<= 1; + continue; + + default: + Assert(0 && "Invalid errno value after call to strerror_r!"); + } + } +} +} // namespace DPL diff --git a/modules/core/src/exception.cpp b/modules/core/src/exception.cpp new file mode 100644 index 0000000..9d6de98 --- /dev/null +++ b/modules/core/src/exception.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file exception.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation of exception system + */ +#include +#include +#include + +namespace DPL +{ +Exception* Exception::m_lastException = NULL; +unsigned int Exception::m_exceptionCount = 0; +void (*Exception::m_terminateHandler)() = NULL; + +void LogUnhandledException(const std::string &str) +{ + // Logging to console + printf("%s\n", str.c_str()); + + // Logging to dlog + LogPedantic(str); +} + +void LogUnhandledException(const std::string &str, const char *filename, int line, const char *function) +{ + // Logging to console + std::ostringstream msg; + msg << "\033[1;5;31m\n=== [" << filename << ":" << line << "] " << function << " ===\033[m"; + msg << str; + printf("%s\n", msg.str().c_str()); + + // Logging to dlog + DPL::Log::LogSystemSingleton::Instance().Error(str.c_str(), filename, line, function); +} +} // namespace DPL diff --git a/modules/core/src/fast_delegate.cpp b/modules/core/src/fast_delegate.cpp new file mode 100644 index 0000000..94b3ca3 --- /dev/null +++ b/modules/core/src/fast_delegate.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file fast_delegate.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of fast delegate + */ +#include diff --git a/modules/core/src/file_input.cpp b/modules/core/src/file_input.cpp new file mode 100644 index 0000000..6e536db --- /dev/null +++ b/modules/core/src/file_input.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file named_input_pipe.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of named input pipe + */ +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace // anonymous +{ +const size_t DEFAULT_READ_BUFFER_SIZE = 4096; +} // namespace anonymous + +FileInput::FileInput() + : m_fd(-1) +{ +} + +FileInput::FileInput(const std::string& fileName) + : m_fd(-1) +{ + Open(fileName); +} + +FileInput::~FileInput() +{ + Close(); +} + +void FileInput::Open(const std::string& fileName) +{ + // Open non-blocking + int fd = TEMP_FAILURE_RETRY(open(fileName.c_str(), O_RDONLY | O_NONBLOCK)); + + // Throw an exception if an error occurred + if (fd == -1) + ThrowMsg(Exception::OpenFailed, fileName); + + // Close if any existing + Close(); + + // Save new descriptor + m_fd = fd; + + LogPedantic("Opened file: " << fileName); +} + +void FileInput::Close() +{ + if (m_fd == -1) + return; + + if (TEMP_FAILURE_RETRY(close(m_fd)) == -1) + Throw(Exception::CloseFailed); + + m_fd = -1; + + LogPedantic("Closed file"); +} + +BinaryQueueAutoPtr FileInput::Read(size_t size) +{ + size_t bytesToRead = size > DEFAULT_READ_BUFFER_SIZE ? DEFAULT_READ_BUFFER_SIZE : size; + + // Malloc default read buffer size + // It is unmanaged, so it can be then attached directly to binary queue + void *buffer = malloc(bytesToRead); + + if (buffer == NULL) + throw std::bad_alloc(); + + LogPedantic("Trying to read " << bytesToRead << " bytes"); + + ssize_t result = TEMP_FAILURE_RETRY(read(m_fd, buffer, bytesToRead)); + + LogPedantic("Read " << result << " bytes from file"); + + if (result > 0) + { + // Succedded to read socket data + BinaryQueueAutoPtr binaryQueue(new BinaryQueue()); + + // Append unmanaged memory + binaryQueue->AppendUnmanaged(buffer, result, &BinaryQueue::BufferDeleterFree, NULL); + + // Return buffer + return binaryQueue; + } + else if (result == 0) + { + // Socket was gracefuly closed + free(buffer); + + // Return empty buffer + return BinaryQueueAutoPtr(new BinaryQueue()); + } + else + { + // Must first save errno value, because it may be altered + int lastErrno = errno; + + // Free buffer + free(buffer); + + // Interpret error result + (void)lastErrno; + + // FIXME: Handle specific errno + Throw(AbstractInput::Exception::ReadFailed); + } +} + +WaitableHandle FileInput::WaitableReadHandle() const +{ + return static_cast(m_fd); +} +} // namespace DPL diff --git a/modules/core/src/file_input_mapping.cpp b/modules/core/src/file_input_mapping.cpp new file mode 100644 index 0000000..2bfcd15 --- /dev/null +++ b/modules/core/src/file_input_mapping.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file file_input_mapping.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of file input mapping + */ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +FileInputMapping::FileInputMapping(const std::string &fileName) + : m_handle(-1), + m_size(0), + m_address(NULL) +{ + // Open device and map it to user space + int file = TEMP_FAILURE_RETRY(open(fileName.c_str(), O_RDONLY)); + + if (file == -1) + { + int error = errno; + ThrowMsg(FileInputMapping::Exception::OpenFailed, + "Failed to open file. errno = " << error); + } + + // Scoped close on file + ScopedClose scopedClose(file); + + // Calculate file size + off64_t size = lseek64(file, 0, SEEK_END); + + if (size == static_cast(-1)) + { + int error = errno; + ThrowMsg(FileInputMapping::Exception::OpenFailed, + "Failed to seek file. errno = " << error); + } + + // Map file to usespace + void *address = mmap(0, static_cast(size), + PROT_READ, MAP_SHARED, file, 0); + + if (address == MAP_FAILED) + { + int error = errno; + ThrowMsg(FileInputMapping::Exception::OpenFailed, + "Failed to map file. errno = " << error); + } + + // Release scoped close + m_handle = scopedClose.Release(); + + // Save mapped up address + m_size = size; + m_address = static_cast(address); + + LogPedantic("Created file mapping: " << fileName << + " of size: " << m_size << + " at address: " << std::hex << static_cast(m_address)); +} + +FileInputMapping::~FileInputMapping() +{ + // Close mapping + if (munmap(m_address, static_cast(m_size)) == -1) + { + int error = errno; + LogPedantic("Failed to munmap file. errno = " << error); + } + + // Close file descriptor + if (TEMP_FAILURE_RETRY(close(m_handle)) == -1) + { + int error = errno; + LogPedantic("Failed to close file. errno = " << error); + } +} + +off64_t FileInputMapping::GetSize() const +{ + return m_size; +} + +const unsigned char *FileInputMapping::GetAddress() const +{ + return m_address; +} +} // namespace DPL diff --git a/modules/core/src/file_output.cpp b/modules/core/src/file_output.cpp new file mode 100644 index 0000000..8b09042 --- /dev/null +++ b/modules/core/src/file_output.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file file_output.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of file output + */ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +FileOutput::FileOutput() + : m_fd(-1) +{ +} + +FileOutput::FileOutput(const std::string& fileName) + : m_fd(-1) +{ + Open(fileName); +} + +FileOutput::~FileOutput() +{ + Close(); +} + +void FileOutput::Open(const std::string& fileName) +{ + // Open non-blocking + int fd = TEMP_FAILURE_RETRY(open(fileName.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, 0664)); + + // Throw an exception if an error occurred + if (fd == -1) + ThrowMsg(Exception::OpenFailed, fileName); + + // Close if any existing + Close(); + + // Save new descriptor + m_fd = fd; + + LogPedantic("Opened file: " << fileName); +} + +void FileOutput::Close() +{ + if (m_fd == -1) + return; + + if (TEMP_FAILURE_RETRY(close(m_fd)) == -1) + Throw(Exception::CloseFailed); + + m_fd = -1; + + LogPedantic("Closed file"); +} + +size_t FileOutput::Write(const BinaryQueue &buffer, size_t bufferSize) +{ + // Adjust write size + if (bufferSize > buffer.Size()) + bufferSize = buffer.Size(); + + // FIXME: User write visitor to write ! + // WriteVisitor visitor + + ScopedFree flattened(malloc(bufferSize)); + buffer.Flatten(flattened.Get(), bufferSize); + + LogPedantic("Trying to write " << bufferSize << " bytes"); + + ssize_t result = TEMP_FAILURE_RETRY(write(m_fd, flattened.Get(), bufferSize)); + + LogPedantic("Wrote " << result << " bytes to file"); + + if (result > 0) + { + // Successfuly written some bytes + return static_cast(result); + } + else if (result == 0) + { + // This is abnormal result + ThrowMsg(CommonException::InternalError, "Invalid write result, 0 bytes written"); + } + else + { + // Interpret error result + // FIXME: Handle errno + Throw(AbstractOutput::Exception::WriteFailed); + } +} + +WaitableHandle FileOutput::WaitableWriteHandle() const +{ + return static_cast(m_fd); +} +} // namespace DPL diff --git a/modules/core/src/generic_event.cpp b/modules/core/src/generic_event.cpp new file mode 100644 index 0000000..3ad97c4 --- /dev/null +++ b/modules/core/src/generic_event.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_event.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of MVC generic event + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/core/src/lexical_cast.cpp b/modules/core/src/lexical_cast.cpp new file mode 100644 index 0000000..1d9ad17 --- /dev/null +++ b/modules/core/src/lexical_cast.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file lexical_cast.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for lexical cast + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/core/src/main.cpp b/modules/core/src/main.cpp new file mode 100644 index 0000000..4e8078c --- /dev/null +++ b/modules/core/src/main.cpp @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file main.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of main for EFL + */ +#include +#include +#include +#include +#include +#include +#include + +IMPLEMENT_SINGLETON(DPL::Main) + +namespace DPL +{ +namespace // anonymous +{ +// Late EFL event handling +Main *g_lateMain = NULL; +} // namespace anonymous + +Main::Main() +#ifdef DPL_ENABLE_GLIB_LOOP_INTEGRATION_WORKAROUND + // GLIB loop intergration workaround + : m_oldEcoreSelect(NULL) +#endif // DPL_ENABLE_GLIB_LOOP_INTEGRATION_WORKAROUND +{ + // Late EFL event handling + Assert(g_lateMain == NULL); + g_lateMain = this; + + // Increment ECORE init count to ensure we have all + // subsystems correctly set-up until main dispatcher dtor + // This is especially important when MainEventDispatcher + // is a global object destroyed no earlier than crt destroy routine + ecore_init(); + +#ifdef DPL_ENABLE_GLIB_LOOP_INTEGRATION_WORKAROUND + // GLIB loop intergration workaround + union ConvertPointer + { + Ecore_Select_Function pointer; + EcoreSelectType function; + } convert; + + convert.pointer = ecore_main_loop_select_func_get(); + m_oldEcoreSelect = convert.function; + + ecore_main_loop_select_func_set(&EcoreSelectInterceptor); +#endif // DPL_ENABLE_GLIB_LOOP_INTEGRATION_WORKAROUND + + // Register event invoker + m_invokerHandler = ecore_main_fd_handler_add(WaitableHandleWatchSupport::WaitableInvokerHandle(), + ECORE_FD_READ, &StaticDispatchInvoker, this, NULL, NULL); + + if (m_invokerHandler == NULL) + ThrowMsg(Exception::CreateFailed, "Failed to register invoker handler!"); + + // It is impossible that there exist watchers at this time + // No need to add watchers + LogPedantic("ECORE event handler registered"); +} + +Main::~Main() +{ + // Remove any watchers + for (EcoreFdHandlerList::iterator iterator = m_readWatchersList.begin(); iterator != m_readWatchersList.end(); ++iterator) + ecore_main_fd_handler_del(*iterator); + + m_readWatchersList.clear(); + + for (EcoreFdHandlerList::iterator iterator = m_writeWatchersList.begin(); iterator != m_writeWatchersList.end(); ++iterator) + ecore_main_fd_handler_del(*iterator); + + m_writeWatchersList.clear(); + + // Remove event invoker + ecore_main_fd_handler_del(m_invokerHandler); + m_invokerHandler = NULL; + + // Decrement ECORE init count + // We do not need ecore routines any more + ecore_shutdown(); + + // Late EFL event handling + Assert(g_lateMain == this); + g_lateMain = NULL; +} + +Eina_Bool Main::StaticDispatchInvoker(void *data, Ecore_Fd_Handler *fd_handler) +{ + LogPedantic("Static ECORE dispatch invoker"); + + Main *This = static_cast
(data); + (void)fd_handler; + + Assert(This != NULL); + + // Late EFL event handling + if (g_lateMain == NULL) + { + LogPedantic("WARNING: Late EFL invoker dispatch!"); + } + else + { + This->DispatchInvoker(); + } + + return ECORE_CALLBACK_RENEW; +} + +Eina_Bool Main::StaticDispatchReadWatcher(void* data, Ecore_Fd_Handler* fd_handler) +{ + LogPedantic("Static ECORE dispatch read watcher"); + + Main *This = static_cast
(data); + + Assert(This != NULL); + + // Late EFL event handling + if (g_lateMain == NULL) + { + LogPedantic("WARNING: Late EFL read watcher dispatch!"); + } + else + { + This->DispatchReadWatcher(static_cast(ecore_main_fd_handler_fd_get(fd_handler))); + } + + return ECORE_CALLBACK_RENEW; +} + +Eina_Bool Main::StaticDispatchWriteWatcher(void* data, Ecore_Fd_Handler* fd_handler) +{ + LogPedantic("Static ECORE dispatch write watcher"); + + Main *This = static_cast
(data); + + Assert(This != NULL); + + // Late EFL event handling + if (g_lateMain == NULL) + { + LogPedantic("WARNING: Late EFL write watcher dispatch!"); + } + else + { + This->DispatchWriteWatcher(static_cast(ecore_main_fd_handler_fd_get(fd_handler))); + } + + return ECORE_CALLBACK_RENEW; +} + +void Main::DispatchInvoker() +{ + LogPedantic("Dispatching invoker..."); + + // Reload watch list + ReloadWatchList(); + + // Handle base invoker + WaitableHandleWatchSupport::InvokerFinished(); + + LogPedantic("Invoker dispatched"); +} + +void Main::ReloadWatchList() +{ + LogPedantic("Reloading watch list... (" << m_readWatchersList.size() << " + " << m_writeWatchersList.size() << ")"); + + // Reload list of watchers + WaitableHandleListEx waitableWatcherHandles = WaitableHandleWatchSupport::WaitableWatcherHandles(); + + // Remove not existing read watchers + EcoreFdHandlerList::iterator watchersIterator = m_readWatchersList.begin(); + WaitableHandleListEx::iterator handlesIterator; + + while (watchersIterator != m_readWatchersList.end()) + { + bool found = false; + + for (handlesIterator = waitableWatcherHandles.begin(); handlesIterator != waitableWatcherHandles.end(); ++handlesIterator) + { + if (handlesIterator->second == WaitMode::Read && + handlesIterator->first == static_cast(ecore_main_fd_handler_fd_get(*watchersIterator))) + { + found = true; + break; + } + } + + if (!found) + { + // Unregister handler + ecore_main_fd_handler_del(*watchersIterator); + + // Remove iterator + EcoreFdHandlerList::iterator next = watchersIterator; + ++next; + + m_readWatchersList.erase(watchersIterator); + watchersIterator = next; + } + else + { + ++watchersIterator; + } + } + + // Remove not existing write watchers + watchersIterator = m_writeWatchersList.begin(); + + while (watchersIterator != m_writeWatchersList.end()) + { + bool found = false; + + for (handlesIterator = waitableWatcherHandles.begin(); handlesIterator != waitableWatcherHandles.end(); ++handlesIterator) + { + if (handlesIterator->second == WaitMode::Write && + handlesIterator->first == static_cast(ecore_main_fd_handler_fd_get(*watchersIterator))) + { + found = true; + break; + } + } + + if (!found) + { + // Unregister handler + ecore_main_fd_handler_del(*watchersIterator); + + // Remove iterator + EcoreFdHandlerList::iterator next = watchersIterator; + ++next; + + m_writeWatchersList.erase(watchersIterator); + watchersIterator = next; + } + else + { + ++watchersIterator; + } + } + + // Add new read/write watchers + for (handlesIterator = waitableWatcherHandles.begin(); handlesIterator != waitableWatcherHandles.end(); ++handlesIterator) + { + if (handlesIterator->second == WaitMode::Read) + { + bool found = false; + + for (watchersIterator = m_readWatchersList.begin(); watchersIterator != m_readWatchersList.end(); ++watchersIterator) + { + if (handlesIterator->first == static_cast(ecore_main_fd_handler_fd_get(*watchersIterator))) + { + found = true; + break; + } + } + + if (!found) + { + Ecore_Fd_Handler *handler = ecore_main_fd_handler_add(handlesIterator->first, + ECORE_FD_READ, &StaticDispatchReadWatcher, this, NULL, NULL); + if (handler == NULL) + ThrowMsg(Exception::CreateFailed, "Failed to register read watcher handler!"); + + // Push new watcher to list + m_readWatchersList.push_back(handler); + } + } + else if (handlesIterator->second == WaitMode::Write) + { + bool found = false; + + for (watchersIterator = m_writeWatchersList.begin(); watchersIterator != m_writeWatchersList.end(); ++watchersIterator) + { + if (handlesIterator->first == static_cast(ecore_main_fd_handler_fd_get(*watchersIterator))) + { + found = true; + break; + } + } + + if (!found) + { + Ecore_Fd_Handler *handler = ecore_main_fd_handler_add(handlesIterator->first, + ECORE_FD_WRITE, &StaticDispatchWriteWatcher, this, NULL, NULL); + if (handler == NULL) + ThrowMsg(Exception::CreateFailed, "Failed to register write watcher handler!"); + + // Push new watcher to list + m_writeWatchersList.push_back(handler); + } + } + else + { + Assert(0); + } + } + + LogPedantic("Watch list reloaded (" << m_readWatchersList.size() << " + " << m_writeWatchersList.size() << ")"); +} + +void Main::DispatchReadWatcher(WaitableHandle waitableHandle) +{ + LogPedantic("Dispatching read watcher..."); + + // Handle watcher + WaitableHandleWatchSupport::HandleWatcher(waitableHandle, WaitMode::Read); + + LogPedantic("Watcher dispatched"); +} + +void Main::DispatchWriteWatcher(WaitableHandle waitableHandle) +{ + LogPedantic("Dispatching write watcher..."); + + // Handle watcher + WaitableHandleWatchSupport::HandleWatcher(waitableHandle, WaitMode::Write); + + LogPedantic("Watcher dispatched"); +} + +Thread *Main::GetInvokerThread() +{ + return NULL; +} + +void Main::HandleDirectInvoker() +{ + // Handle direct invoker + ReloadWatchList(); +} + +#ifdef DPL_ENABLE_GLIB_LOOP_INTEGRATION_WORKAROUND +// GLIB loop intergration workaround +int Main::EcoreSelectInterceptor(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) +{ +#if 0 + // Check each descriptor to see if it is valid + for (int i = 0; i < nfds; i++) + { + if (FD_ISSET(i, readfds) || FD_ISSET(i, writefds) || FD_ISSET(i, exceptfds)) + { + // Try to get descriptor flags + int result = fcntl(i, F_GETFL); + + if (result == -1) + { + if (errno == EBADF) + { + // This a bad descriptor. Remove all occurrences of it. + if (FD_ISSET(i, readfds)) + { + LogPedantic("GLIB_LOOP_INTEGRATION_WORKAROUND: Bad read descriptor: " << i); + FD_CLR(i, readfds); + } + + if (FD_ISSET(i, writefds)) + { + LogPedantic("GLIB_LOOP_INTEGRATION_WORKAROUND: Bad write descriptor: " << i); + FD_CLR(i, writefds); + } + + if (FD_ISSET(i, exceptfds)) + { + LogPedantic("GLIB_LOOP_INTEGRATION_WORKAROUND: Bad exception descriptor: " << i); + FD_CLR(i, exceptfds); + } + } + else + { + // Unexpected error + Assert(0); + } + } + } + } + + // Find out new maximum + int newNfds = 0; + + for (int i = 0; i < nfds; i++) + { + if (FD_ISSET(i, readfds) || FD_ISSET(i, writefds) || FD_ISSET(i, exceptfds)) + newNfds = i; + } + + return MainSingleton::Instance().m_oldEcoreSelect(newNfds + 1, readfds, writefds, exceptfds, timeout); + +#else + + // We have to check error code here and make another try because of some glib error's. + fd_set rfds, wfds, efds; + memcpy(&rfds, readfds, sizeof(fd_set)); + memcpy(&wfds, writefds, sizeof(fd_set)); + memcpy(&efds, exceptfds, sizeof(fd_set)); + + int ret = MainSingleton::Instance().m_oldEcoreSelect(nfds, readfds, writefds, exceptfds, timeout); + + if (ret == -1) + { + // Check each descriptor to see if it is valid + for (int i = 0; i < nfds; i++) + { + if (FD_ISSET(i, readfds) || FD_ISSET(i, writefds) || FD_ISSET(i, exceptfds)) + { + // Try to get descriptor flags + int result = fcntl(i, F_GETFL); + + if (result == -1) + { + if (errno == EBADF) + { + // This a bad descriptor. Remove all occurrences of it. + if (FD_ISSET(i, readfds)) + { + LogPedantic("GLIB_LOOP_INTEGRATION_WORKAROUND: Bad read descriptor: " << i); + FD_CLR(i, readfds); + } + + if (FD_ISSET(i, writefds)) + { + LogPedantic("GLIB_LOOP_INTEGRATION_WORKAROUND: Bad write descriptor: " << i); + FD_CLR(i, writefds); + } + + if (FD_ISSET(i, exceptfds)) + { + LogPedantic("GLIB_LOOP_INTEGRATION_WORKAROUND: Bad exception descriptor: " << i); + FD_CLR(i, exceptfds); + } + } + else + { + // Unexpected error + Assert(0); + } + } + } + } + + LogPedantic("GLIB_LOOP_INTEGRATION_WORKAROUND: Bad read descriptor. Retrying with default select."); + + //Retry with old values and return new error + memcpy(readfds, &rfds, sizeof(fd_set)); + memcpy(writefds, &wfds, sizeof(fd_set)); + memcpy(exceptfds, &efds, sizeof(fd_set)); + + // Trying to do it very short + timeval tm; + tm.tv_sec = 0; + tm.tv_usec = 10; + + if (timeout) + ret = select(nfds, readfds, writefds, exceptfds, &tm); + else + ret = select(nfds, readfds, writefds, exceptfds, NULL); + } + + return ret; +#endif +} +#endif // DPL_ENABLE_GLIB_LOOP_INTEGRATION_WORKAROUND +} // namespace DPL diff --git a/modules/core/src/mutex.cpp b/modules/core/src/mutex.cpp new file mode 100644 index 0000000..7d18eda --- /dev/null +++ b/modules/core/src/mutex.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file mutex.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of mutex + */ +#include +#include +#include +#include + +namespace DPL +{ +Mutex::Mutex() +{ + if (pthread_mutex_init(&m_mutex, NULL) != 0) + { + int error = errno; + + LogPedantic("Failed to create mutex. Errno: " << error); + + ThrowMsg(Exception::CreateFailed, + "Failed to create mutex. Errno: " << error); + } +} + +Mutex::~Mutex() +{ + if (pthread_mutex_destroy(&m_mutex) != 0) + { + int error = errno; + + LogPedantic("Failed to destroy mutex. Errno: " << error); + } +} + +void Mutex::Lock() const +{ + if (pthread_mutex_lock(&m_mutex) != 0) + { + int error = errno; + + LogPedantic("Failed to lock mutex. Errno: " << error); + + ThrowMsg(Exception::LockFailed, + "Failed to lock mutex. Errno: " << error); + } +} + +void Mutex::Unlock() const +{ + if (pthread_mutex_unlock(&m_mutex) != 0) + { + int error = errno; + + LogPedantic("Failed to unlock mutex. Errno: " << error); + + ThrowMsg(Exception::UnlockFailed, + "Failed to unlock mutex. Errno: " << error); + } +} + +Mutex::ScopedLock::ScopedLock(Mutex *mutex) + : m_mutex(mutex) +{ + Assert(mutex != NULL); + m_mutex->Lock(); +} + +Mutex::ScopedLock::~ScopedLock() +{ + Try + { + m_mutex->Unlock(); + } + Catch (Mutex::Exception::UnlockFailed) + { + LogPedantic("Failed to leave mutex scoped lock"); + } +} +} // namespace DPL diff --git a/modules/core/src/named_base_pipe.cpp b/modules/core/src/named_base_pipe.cpp new file mode 100644 index 0000000..6d18bc6 --- /dev/null +++ b/modules/core/src/named_base_pipe.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file named_base_pipe.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of named base pipe + */ +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace // anonymous +{ +const mode_t FIFO_MODE = 0600; +} // namespace anonymous + +NamedBasePipe::~NamedBasePipe() +{ +} + +void NamedBasePipe::Create(const std::string &pipeName) +{ + // Create new fifo + int status = mkfifo(pipeName.c_str(), FIFO_MODE); + + if (status == -1) + { + // Ignore error it it already exists + if (errno == EEXIST) + ThrowMsg(Exception::AlreadyExist, pipeName); + else + ThrowMsg(Exception::CreateFailed, pipeName); + } +} + +void NamedBasePipe::Destroy(const std::string &fileName) +{ + // Destroy fifo + unlink(fileName.c_str()); // FIXME: Add error handling +} +} // namespace DPL diff --git a/modules/core/src/named_input_pipe.cpp b/modules/core/src/named_input_pipe.cpp new file mode 100644 index 0000000..bda52f2 --- /dev/null +++ b/modules/core/src/named_input_pipe.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file named_input_pipe.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of named input pipe + */ +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace // anonymous +{ +const size_t DEFAULT_READ_BUFFER_SIZE = 1024; +} // namespace anonymous + +NamedInputPipe::NamedInputPipe() + : m_fifo(-1) +{ +} + +NamedInputPipe::~NamedInputPipe() +{ + Close(); +} + +void NamedInputPipe::Open(const std::string& pipeName) +{ + // Open pipe for reading + int fifo = TEMP_FAILURE_RETRY(open(pipeName.c_str(), O_RDONLY | O_NONBLOCK)); + + if (fifo == -1) + ThrowMsg(Exception::OpenFailed, pipeName); + + m_fifo = fifo; +} + +void NamedInputPipe::Close() +{ + if (m_fifo == -1) + return; + + if (TEMP_FAILURE_RETRY(close(m_fifo)) == -1) + Throw(Exception::CloseFailed); + + m_fifo = -1; +} + +BinaryQueueAutoPtr NamedInputPipe::Read(size_t size) +{ + size_t bytesToRead = size > DEFAULT_READ_BUFFER_SIZE ? DEFAULT_READ_BUFFER_SIZE : size; + + // Malloc default read buffer size + // It is unmanaged, so it can be then attached directly to binary queue + void *buffer = malloc(bytesToRead); + + if (buffer == NULL) + throw std::bad_alloc(); + + ssize_t result = TEMP_FAILURE_RETRY(read(m_fifo, buffer, bytesToRead)); + + if (result > 0) + { + // Succedded to read socket data + BinaryQueueAutoPtr binaryQueue(new BinaryQueue()); + + // Append unmanaged memory + binaryQueue->AppendUnmanaged(buffer, result, &BinaryQueue::BufferDeleterFree, NULL); + + // Return buffer + return binaryQueue; + } + else if (result == 0) + { + // Socket was gracefuly closed + free(buffer); + + // Return empty buffer + return BinaryQueueAutoPtr(new BinaryQueue()); + } + else + { + // Must first save errno value, because it may be altered + int lastErrno = errno; + + // Free buffer + free(buffer); + + // Interpret error result + (void)lastErrno; + + // FIXME: Handle specific errno + Throw(AbstractInput::Exception::ReadFailed); + } +} + +int NamedInputPipe::WaitableReadHandle() const +{ + return m_fifo; +} +} // namespace DPL diff --git a/modules/core/src/named_output_pipe.cpp b/modules/core/src/named_output_pipe.cpp new file mode 100644 index 0000000..5dd7215 --- /dev/null +++ b/modules/core/src/named_output_pipe.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file named_output_pipe.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of named output pipe + */ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +NamedOutputPipe::NamedOutputPipe() + : m_fifo(-1) +{ +} + +NamedOutputPipe::~NamedOutputPipe() +{ + Close(); +} + +void NamedOutputPipe::Open(const std::string& pipeName) +{ + // Then open it for reading or writing + int fifo = TEMP_FAILURE_RETRY(open(pipeName.c_str(), O_WRONLY | O_NONBLOCK)); + + if (fifo == -1) + ThrowMsg(Exception::OpenFailed, pipeName); + + m_fifo = fifo; +} + +void NamedOutputPipe::Close() +{ + if (m_fifo == -1) + return; + + if (TEMP_FAILURE_RETRY(close(m_fifo)) == -1) + Throw(Exception::CloseFailed); + + m_fifo = -1; +} + +size_t NamedOutputPipe::Write(const BinaryQueue &buffer, size_t bufferSize) +{ + // Adjust write size + if (bufferSize > buffer.Size()) + bufferSize = buffer.Size(); + + // FIXME: User write visitor to write ! + // WriteVisitor visitor + + ScopedFree flattened(malloc(bufferSize)); + buffer.Flatten(flattened.Get(), bufferSize); + + ssize_t result = TEMP_FAILURE_RETRY(write(m_fifo, flattened.Get(), bufferSize)); + + if (result > 0) + { + // Successfuly written some bytes + return static_cast(result); + } + else if (result == 0) + { + // This is abnormal result + ThrowMsg(CommonException::InternalError, "Invalid socket write result, 0 bytes written"); + } + else + { + // Interpret error result + // FIXME: Handle errno + Throw(AbstractOutput::Exception::WriteFailed); + } +} + +int NamedOutputPipe::WaitableWriteHandle() const +{ + return m_fifo; +} +} // namespace DPL diff --git a/modules/core/src/noncopyable.cpp b/modules/core/src/noncopyable.cpp new file mode 100644 index 0000000..d0af48a --- /dev/null +++ b/modules/core/src/noncopyable.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file noncopyable.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of noncopyable + */ +#include + +namespace DPL +{ +Noncopyable::Noncopyable() +{ +} + +Noncopyable::~Noncopyable() +{ +} +} // namespace DPL diff --git a/modules/core/src/once.cpp b/modules/core/src/once.cpp new file mode 100644 index 0000000..5ac0c1e --- /dev/null +++ b/modules/core/src/once.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file once.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of once + */ +#include + +namespace DPL +{ +void Once::Call(Delegate delegate) +{ + // First chance test + if (m_atomic == 1) + return; + + // Enter mutex + Mutex::ScopedLock lock(&m_mutex); + + // Second chance test + if (m_atomic == 1) + return; + + // Initialization: call delegate + delegate(); + + // Initialization: done + ++m_atomic; +} +} // namespace DPL diff --git a/modules/core/src/read_write_mutex.cpp b/modules/core/src/read_write_mutex.cpp new file mode 100644 index 0000000..7807f7f --- /dev/null +++ b/modules/core/src/read_write_mutex.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file read_write_mutex.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of read write mutex + */ +#include +#include + +namespace DPL +{ +ReadWriteMutex::ReadWriteMutex() +{ + if (pthread_rwlock_init(&m_rwlock, NULL) != 0) + Throw(Exception::CreateFailed); +} + +ReadWriteMutex::~ReadWriteMutex() +{ + if (pthread_rwlock_destroy(&m_rwlock) != 0) + Throw(Exception::DestroyFailed); +} + +void ReadWriteMutex::ReadLock() const +{ + if (pthread_rwlock_rdlock(&m_rwlock) != 0) + Throw(Exception::ReadLockFailed); +} + +void ReadWriteMutex::WriteLock() const +{ + if (pthread_rwlock_wrlock(&m_rwlock) != 0) + Throw(Exception::WriteLockFailed); +} + +void ReadWriteMutex::Unlock() const +{ + if (pthread_rwlock_unlock(&m_rwlock) != 0) + Throw(Exception::UnlockFailed); +} + +ReadWriteMutex::ScopedReadLock::ScopedReadLock(ReadWriteMutex *mutex) + : m_mutex(mutex) +{ + Assert(mutex != NULL); + m_mutex->ReadLock(); +} + +ReadWriteMutex::ScopedReadLock::~ScopedReadLock() +{ + m_mutex->Unlock(); +} + +ReadWriteMutex::ScopedWriteLock::ScopedWriteLock(ReadWriteMutex *mutex) + : m_mutex(mutex) +{ + Assert(mutex != NULL); + m_mutex->WriteLock(); +} + +ReadWriteMutex::ScopedWriteLock::~ScopedWriteLock() +{ + m_mutex->Unlock(); +} +} // namespace DPL diff --git a/modules/core/src/recursive_mutex.cpp b/modules/core/src/recursive_mutex.cpp new file mode 100644 index 0000000..19e9dea --- /dev/null +++ b/modules/core/src/recursive_mutex.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file recursive_mutex.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of recursive mutex + */ +#include +#include + +namespace DPL +{ +RecursiveMutex::RecursiveMutex() +{ + pthread_mutexattr_t attr; + + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + + if (pthread_mutex_init(&m_mutex, &attr) != 0) + Throw(Exception::CreateFailed); +} + +RecursiveMutex::~RecursiveMutex() +{ + if (pthread_mutex_destroy(&m_mutex) != 0) + Throw(Exception::DestroyFailed); +} + +void RecursiveMutex::Lock() const +{ + if (pthread_mutex_lock(&m_mutex) != 0) + Throw(Exception::LockFailed); +} + +void RecursiveMutex::Unlock() const +{ + if (pthread_mutex_unlock(&m_mutex) != 0) + Throw(Exception::UnlockFailed); +} + +RecursiveMutex::ScopedLock::ScopedLock(RecursiveMutex *mutex) + : m_mutex(mutex) +{ + Assert(mutex != NULL); + m_mutex->Lock(); +} + +RecursiveMutex::ScopedLock::~ScopedLock() +{ + m_mutex->Unlock(); +} +} // namespace DPL diff --git a/modules/core/src/semaphore.cpp b/modules/core/src/semaphore.cpp new file mode 100644 index 0000000..82d53ff --- /dev/null +++ b/modules/core/src/semaphore.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file semaphore.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of semaphore + */ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +void Semaphore::Remove(const std::string &fileName) +{ + if (sem_unlink(fileName.c_str()) == -1) { + int error = errno; + LogPedantic("Failed to unlink semaphore. Errno: " << error); + ThrowMsg(Exception::RemoveFailed, + "Failed to unlink semaphore. Errno: " << error); + } +} + +Semaphore::Semaphore(size_t maxLockCount) +{ + LogPedantic("Allocating unnamed semaphore:"); + LogPedantic(" Maximum lock count: " << maxLockCount); + + if (-1 == sem_init(&m_semaphore.unnamed.handle, + 0, + static_cast(maxLockCount))) + { + int error = errno; + + LogPedantic("Failed to create semaphore. Errno: " << error); + + ThrowMsg(Exception::CreateFailed, + "Failed to create semaphore. Errno: " << error); + } + + m_type = Type_Unnamed; +} + +Semaphore::Semaphore(const std::string &fileName, + bool allowCreate, + bool exclusiveCreate, + size_t maxLockCount, + int permissions, + bool unlinkOnDestroy) +{ + LogPedantic("Allocating named semaphore:"); + LogPedantic(" File name: " << fileName); + LogPedantic(" Maximum lock count: " << maxLockCount); + LogPedantic(" File name: " << fileName); + LogPedantic(" Allowed create: " << allowCreate); + LogPedantic(" Exclusive create: " << exclusiveCreate); + LogPedantic(" Permissions: " << permissions); + LogPedantic(" Unlink on destroy: " << unlinkOnDestroy); + + sem_t *semaphore; + + do + { + if (allowCreate) + { + if (exclusiveCreate) + { + semaphore = sem_open(fileName.c_str(), + O_CREAT | O_EXCL, + permissions, + static_cast(maxLockCount)); + } + else + { + semaphore = sem_open(fileName.c_str(), + O_CREAT, + permissions, + static_cast(maxLockCount)); + } + } + else + { + semaphore = sem_open(fileName.c_str(), 0); + } + } + while (semaphore == SEM_FAILED && errno == EINTR); + + if (semaphore == SEM_FAILED) + { + int error = errno; + + LogPedantic("Failed to create semaphore '" << fileName + << "'. Errno: " << error); + + ThrowMsg(Exception::CreateFailed, + "Failed to create semaphore '" << fileName + << "'. Errno: " << error); + } + + m_semaphore.named.handle = semaphore; + + m_semaphore.named.name = strdup(fileName.c_str()); // May be NULL + + if (m_semaphore.named.name == NULL) + LogPedantic("Out of memory while duplicating semaphore name"); + + m_semaphore.named.unlinkOnDestroy = unlinkOnDestroy; + + m_type = Type_Named; +} + +Semaphore::~Semaphore() +{ + InternalDestroy(); +} + +sem_t *Semaphore::InternalGet() const +{ + switch (m_type) + { + case Type_Unnamed: + return &m_semaphore.unnamed.handle; + + case Type_Named: + return m_semaphore.named.handle; + + default: + Assert(false && "Invalid type"); + } + + return NULL; +} + +void Semaphore::InternalDestroy() +{ + switch (m_type) + { + case Type_Unnamed: + if (sem_destroy(&m_semaphore.unnamed.handle) == -1) + { + int error = errno; + + LogPedantic("Failed to destroy semaphore. Errno: " << error); + } + break; + + case Type_Named: + if (sem_close(m_semaphore.named.handle) == -1) + { + int error = errno; + + LogPedantic("Failed to close semaphore. Errno: " << error); + } + + if (m_semaphore.named.name != NULL) + { + // Unlink named semaphore + if (m_semaphore.named.unlinkOnDestroy && + sem_unlink(m_semaphore.named.name) == -1) + { + int error = errno; + + LogPedantic("Failed to unlink semaphore. Errno: " + << error); + } + + // Free name + free(m_semaphore.named.name); + } + break; + + default: + Assert(false && "Invalid type"); + } +} + +void Semaphore::Lock() const +{ + if (TEMP_FAILURE_RETRY(sem_wait(InternalGet())) != 0) + { + int error = errno; + + LogPedantic("Failed to lock semaphore. Errno: " << error); + + ThrowMsg(Exception::LockFailed, + "Failed to lock semaphore. Errno: " << error); + } +} + +void Semaphore::Unlock() const +{ + if (sem_post(InternalGet()) != 0) + { + int error = errno; + + LogPedantic("Failed to unlock semaphore. Errno: " << error); + + ThrowMsg(Exception::UnlockFailed, + "Failed to unlock semaphore. Errno: " << error); + } +} + +Semaphore::ScopedLock::ScopedLock(Semaphore *semaphore) + : m_semaphore(semaphore) +{ + Assert(semaphore != NULL); + m_semaphore->Lock(); +} + +Semaphore::ScopedLock::~ScopedLock() +{ + Try + { + m_semaphore->Unlock(); + } + Catch (Semaphore::Exception::UnlockFailed) + { + LogPedantic("Failed to leave semaphore scoped lock"); + } +} +} // namespace DPL diff --git a/modules/core/src/serialization.cpp b/modules/core/src/serialization.cpp new file mode 100644 index 0000000..b6e7da8 --- /dev/null +++ b/modules/core/src/serialization.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file serialization.cpp + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of data serialization. + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/core/src/single_instance.cpp b/modules/core/src/single_instance.cpp new file mode 100644 index 0000000..cca8365 --- /dev/null +++ b/modules/core/src/single_instance.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file single_instance.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of single instance + */ +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace // anonumous +{ +const char *LOCK_PREFIX_PATH = "/tmp/dpl_single_instance_"; +} +SingleInstance::SingleInstance() + : m_locked(false), + m_fdLock(-1) +{ +} + +SingleInstance::~SingleInstance() +{ + Assert(!m_locked && "Single instance must be released before exit!"); +} + +bool SingleInstance::TryLock(const std::string &lockName) +{ + LogPedantic("Locking single instance: " << lockName); + + struct flock lock; + + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 1; + + // Open lock file + m_fdLock = TEMP_FAILURE_RETRY(open((std::string(LOCK_PREFIX_PATH) + lockName).c_str(), O_WRONLY | O_CREAT, 0666)); + + if (m_fdLock == -1) + ThrowMsg(Exception::LockError, "Cannot open single instance lock file!"); + + // Lock file + int result = TEMP_FAILURE_RETRY(fcntl(m_fdLock, F_SETLK, &lock)); + + // Was the instance successfuly locked ? + if (result == 0) + { + LogPedantic("Instance locked: " << lockName); + + // It is locked now + m_locked = true; + + // Done + return true; + } + + if (errno == EACCES || errno == EAGAIN) + { + LogPedantic("Instance is already running: " << lockName); + return false; + } + + // This is lock error + ThrowMsg(Exception::LockError, "Cannot lock single instance lock file!"); +} + +void SingleInstance::Release() +{ + if (!m_locked) + return; + + LogPedantic("Unlocking single instance"); + + // Unlock file + struct flock lock; + + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 1; + + int result = TEMP_FAILURE_RETRY(fcntl(m_fdLock, F_SETLK, &lock)); + + // Was the instance successfuly unlocked ? + if (result == -1) + ThrowMsg(Exception::LockError, "Cannot unlock single instance lock file!"); + + // Close lock file + if (TEMP_FAILURE_RETRY(close(m_fdLock)) == -1) + ThrowMsg(Exception::LockError, "Cannot close single instance lock file!"); + + m_fdLock = -1; + + // Done + m_locked = false; + LogPedantic("Instance unlocked"); +} +} // namespace DPL diff --git a/modules/core/src/singleton.cpp b/modules/core/src/singleton.cpp new file mode 100644 index 0000000..a6680f1 --- /dev/null +++ b/modules/core/src/singleton.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_event.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of singleton + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/core/src/string.cpp b/modules/core/src/string.cpp new file mode 100644 index 0000000..d945ebd --- /dev/null +++ b/modules/core/src/string.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file string.cpp + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// TODO: Completely move to ICU +namespace DPL +{ +namespace //anonymous +{ +class ASCIIValidator +{ + const std::string& m_TestedString; + +public: + ASCIIValidator(const std::string& aTestedString); + + void operator()(char aCharacter) const; +}; + +ASCIIValidator::ASCIIValidator(const std::string& aTestedString) + : m_TestedString(aTestedString) +{ +} + +void ASCIIValidator::operator()(char aCharacter) const +{ + // Check for ASCII data range + if (aCharacter <= 0) + { + ThrowMsg(StringException::InvalidASCIICharacter, + "invalid character code " << static_cast(aCharacter) + << " from string [" << m_TestedString + << "] passed as ASCII"); + } +} + +const iconv_t gc_IconvOperError = reinterpret_cast(-1); +const size_t gc_IconvConvertError = static_cast(-1); +} // namespace anonymous + +String FromUTF8String(const std::string& aIn) +{ + if (aIn.empty()) + + return String(); + + size_t inbytes = aIn.size(); + + // Default iconv UTF-32 module adds BOM (4 bytes) in from of string + // The worst case is when 8bit UTF-8 char converts to 32bit UTF-32 + // newsize = oldsize * 4 + end + bom + // newsize - bytes for UTF-32 string + // oldsize - letters in UTF-8 string + // end - end character for UTF-32 (\0) + // bom - Unicode header in front of string (0xfeff) + size_t outbytes = sizeof(wchar_t)*(inbytes + 2); + std::vector output(inbytes + 2, 0); + + size_t outbytesleft = outbytes; + char* inbuf = const_cast(aIn.c_str()); + + // vector is used to provide buffer for iconv which expects char* buffer + // but during conversion from UTF32 uses internaly wchar_t + char* outbuf = reinterpret_cast(&output[0]); + + iconv_t iconvHandle = iconv_open("UTF-32","UTF-8"); + + if (gc_IconvOperError == iconvHandle) + { + int error = errno; + + ThrowMsg(StringException::IconvInitErrorUTF8ToUTF32, + "iconv_open failed for " << "UTF-32 <- UTF-8" << + "error: " << GetErrnoString(error)); + } + + size_t iconvRet = iconv(iconvHandle, &inbuf, &inbytes, &outbuf, &outbytesleft); + + iconv_close(iconvHandle); + + if (gc_IconvConvertError == iconvRet) + { + ThrowMsg(StringException::IconvConvertErrorUTF8ToUTF32, + "iconv failed for " << "UTF-32 <- UTF-8" << "error: " + << GetErrnoString()); + return String(); + } + + // Ignore BOM in front of UTF-32 + return &output[1]; +} + +std::string ToUTF8String(const DPL::String& aIn) +{ + if (aIn.empty()) + + return std::string(); + + size_t inbytes = aIn.size() * sizeof(wchar_t); + size_t outbytes = inbytes + sizeof(char); + + // wstring returns wchar_t but iconv expects char* + // iconv internally is processing input as wchar_t + char* inbuf = reinterpret_cast(const_cast(aIn.c_str())); + std::vector output(inbytes, 0); + char* outbuf = &output[0]; + + size_t outbytesleft = outbytes; + + iconv_t iconvHandle = iconv_open("UTF-8","UTF-32"); + + if (gc_IconvOperError == iconvHandle) + { + ThrowMsg(StringException::IconvInitErrorUTF32ToUTF8, + "iconv_open failed for " << "UTF-8 <- UTF-32" + << "error: " << GetErrnoString()); + return std::string(); + } + + size_t iconvRet = iconv(iconvHandle, &inbuf, &inbytes, &outbuf, &outbytesleft); + + iconv_close(iconvHandle); + + if (gc_IconvConvertError == iconvRet) + { + ThrowMsg(StringException::IconvConvertErrorUTF32ToUTF8, + "iconv failed for " << "UTF-8 <- UTF-32" + << "error: " << GetErrnoString()); + return std::string(); + } + + return &output[0]; +} + +String FromASCIIString(const std::string& aString) +{ + String output; + + std::for_each(aString.begin(), aString.end(), ASCIIValidator(aString)); + std::copy(aString.begin(), aString.end(), std::back_inserter(output)); + + return output; +} + +String FromUTF32String(const std::wstring& aString) +{ + return String(&aString[0]); +} + +static UChar *ConvertToICU(const String &inputString) +{ + ScopedArray outputString; + int32_t size = 0; + int32_t convertedSize = 0; + UErrorCode error = U_ZERO_ERROR; + + // Calculate size of output string + ::u_strFromWCS(NULL, + 0, + &size, + inputString.c_str(), + -1, + &error); + + if (error == U_ZERO_ERROR || + error == U_BUFFER_OVERFLOW_ERROR) + { + // What buffer size is ok ? + LogPedantic("ICU: Output buffer size: " << size); + } + else + { + ThrowMsg(StringException::ICUInvalidCharacterFound, + "ICU: Failed to retrieve output string size. Error: " + << error); + } + + // Allocate proper buffer + outputString.Reset(new UChar[size + 1]); + ::memset(outputString.Get(), 0, sizeof(UChar) * (size + 1)); + + error = U_ZERO_ERROR; + + // Do conversion + ::u_strFromWCS(outputString.Get(), + size + 1, + &convertedSize, + inputString.c_str(), + -1, + &error); + + if (!U_SUCCESS(error)) + { + ThrowMsg(StringException::ICUInvalidCharacterFound, + "ICU: Failed to convert string. Error: " << error); + } + + // Done + return outputString.Release(); +} + +int StringCompare(const String &left, + const String &right, + bool caseInsensitive) +{ + // Convert input strings + ScopedArray leftICU(ConvertToICU(left)); + ScopedArray rightICU(ConvertToICU(right)); + + if (caseInsensitive) + { + return static_cast(u_strcasecmp(leftICU.Get(), rightICU.Get(), 0)); + } + else + { + return static_cast(u_strcmp(leftICU.Get(), rightICU.Get())); + } +} +} //namespace DPL + +std::ostream& operator<<(std::ostream& aStream, const DPL::String& aString) +{ + return aStream << DPL::ToUTF8String(aString); +} diff --git a/modules/core/src/task.cpp b/modules/core/src/task.cpp new file mode 100644 index 0000000..a8f18a4 --- /dev/null +++ b/modules/core/src/task.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Radoslaw Wicik (r.wicik@samsung.com) + * @version 1.0 + * @brief Implementation file for abstaract task definition + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/core/src/task_list.cpp b/modules/core/src/task_list.cpp new file mode 100644 index 0000000..89e0ff8 --- /dev/null +++ b/modules/core/src/task_list.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file task_list.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Radoslaw Wicik (r.wicik@samsung.com) + * @version 1.0 + * @brief Implementation file for task list + */ +#include +#include + +namespace DPL +{ +TaskList::TaskList() + : m_switched(false), + m_running(false) +{ + m_currentTask = m_tasks.end(); +} + +TaskList::~TaskList() +{ + for (Tasks::iterator i = m_tasks.begin(); i != m_tasks.end(); ++i) + delete *i; +} + +void TaskList::AddTask(Task *task) +{ + Assert(!m_running && "AddTask is not allowed after calling NextStep"); + m_tasks.push_back(task); + m_currentTask = m_tasks.begin(); +} + +bool TaskList::NextStep() +{ + m_running = true; + + Assert(m_currentTask != m_tasks.end() && "Task list is empty or all tasks done"); + + m_switched = false; + + bool result = (*m_currentTask)->NextStep(); + + if (result || m_switched) + return true; + + return ++m_currentTask != m_tasks.end(); +} + +bool TaskList::Abort() +{ + m_tasks.erase(m_currentTask,m_tasks.end()); + m_tasks.reverse(); + for (Tasks::iterator i = m_tasks.begin(); i != m_tasks.end();) + { + //If given task does not have any "abortSteps", remove it from the list + if(!(*i)->Abort()) + { + delete *i; + i=m_tasks.erase(i); + continue; + } + ++i; + } + + if(m_tasks.empty()) + return false; + + m_currentTask=m_tasks.begin(); + + return true; +} + +void TaskList::SwitchToTask(Task *task) +{ + Tasks::iterator i = std::find(m_tasks.begin(), m_tasks.end(), task); + Assert(i != m_tasks.end()); + m_currentTask = i; + m_switched = true; +} + +size_t TaskList::GetTaskCount() const +{ + return static_cast(m_tasks.size()); +} + +size_t TaskList::GetStepCount() const +{ + size_t count = 0; + + for (Tasks::const_iterator i = m_tasks.begin(); i != m_tasks.end(); ++i) + count += (*i)->GetStepCount(); + + return count; +} + +} // namespace DPL diff --git a/modules/core/src/thread.cpp b/modules/core/src/thread.cpp new file mode 100644 index 0000000..ec7166a --- /dev/null +++ b/modules/core/src/thread.cpp @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file thread.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of thread + */ +#include +#include +#include +#include +#include +#include +#include + +namespace // anonymous +{ +static const size_t NANOSECONDS_PER_SECOND = + static_cast(1000 * 1000 * 1000); + +static const size_t NANOSECONDS_PER_MILISECOND = + static_cast(1000 * 1000); + +static const size_t NANOSECONDS_PER_MICROSECOND = + static_cast(1000); + +static const pthread_t g_mainThread = pthread_self(); + +class ThreadSpecific +{ +public: + pthread_key_t threadSpecific; + + ThreadSpecific() + : threadSpecific(0) + { + threadSpecific=0; + pthread_key_create(&threadSpecific, NULL); + } + + virtual ~ThreadSpecific() + { + pthread_key_delete(threadSpecific); + } +}; + +static ThreadSpecific g_threadSpecific; +} // namespace anonymous + +namespace DPL +{ +bool g_TLSforMainCreated = false; + +Thread::Thread() + : m_thread(0), + m_abandon(false), + m_running(false), + m_directInvoke(false) +{ +} + +Thread::~Thread() +{ + // Ensure that we quit thread + // Always wait thread by yourself; if thread is still running + // this may be sometimes very bad. When derived, some resources + // may leak or be doubly freed + Quit(); + + // Remove any remainig events + // Thread proc is surely not running now + for (InternalEventList::iterator iterator = m_eventList.begin(); iterator != m_eventList.end(); ++iterator) + iterator->eventDeleteProc(iterator->event, iterator->userParam); + + m_eventList.clear(); +} + +Thread *Thread::GetCurrentThread() +{ + if (pthread_equal(pthread_self(), g_mainThread)) + return NULL; + + void *threadSpecific = pthread_getspecific(g_threadSpecific.threadSpecific); + + // Is this a managed thread ? + if (threadSpecific == NULL) + Throw(Exception::UnmanagedThread); + + return static_cast(threadSpecific); +} + +void *Thread::StaticThreadEntry(void *param) +{ + LogPedantic("Entered static thread entry"); + + // Retrieve context + Thread *This = static_cast(param); + Assert(This != NULL); + + // Set thread specific + pthread_setspecific(g_threadSpecific.threadSpecific, This); + + // Enter thread proc + // Do not allow exceptions to hit pthread core + UNHANDLED_EXCEPTION_HANDLER_BEGIN + { + This->ThreadEntry(); + } + UNHANDLED_EXCEPTION_HANDLER_END + + // Critical section + { + // Leave running state + Mutex::ScopedLock lock(&This->m_stateMutex); + + This->m_running = false; + + // Abandon thread + if (This->m_abandon) + { + LogPedantic("Thread was abandoned"); + pthread_detach(This->m_thread); + } + else + { + LogPedantic("Thread is joinable"); + } + } + + return NULL; +} + +int Thread::ThreadEntry() +{ + LogPedantic("Entered default thread entry"); + return Exec(); +} + +void Thread::ProcessEvents() +{ + LogPedantic("Processing events"); + + // Steal current event list + InternalEventList stolenEvents; + + // Enter event list critical section + { + Mutex::ScopedLock lock(&m_eventMutex); + m_eventList.swap(stolenEvents); + m_eventInvoker.Reset(); + } + + // Process event list + LogPedantic("Stolen " << stolenEvents.size() << " internal events"); + + for (InternalEventList::iterator iterator = stolenEvents.begin(); iterator != stolenEvents.end(); ++iterator) + { + // Dispatch immediate event + iterator->eventDispatchProc(iterator->event, iterator->userParam); + + // Delete event + iterator->eventDeleteProc(iterator->event, iterator->userParam); + } +} + +void Thread::ProcessTimedEvents() +{ + // Critical section on timed events mutex + { + Mutex::ScopedLock lock(&m_timedEventMutex); + + // Get current time + unsigned long currentTimeMiliseconds = GetCurrentTimeMiliseconds(); + + // Info + LogPedantic("Processing timed events. Time now: " << currentTimeMiliseconds << " ms"); + + // All timed events are sorted chronologically + // Emit timed out events + while (!m_timedEventVector.empty() && + currentTimeMiliseconds >= m_timedEventVector.begin()->registerTimeMiliseconds + m_timedEventVector.begin()->dueTimeMiliseconds) + { + // Info + LogPedantic("Transforming timed event into immediate event. Absolute due time: " << + (m_timedEventVector.begin()->registerTimeMiliseconds + m_timedEventVector.begin()->dueTimeMiliseconds) << " ms"); + + // Emit immediate event + PushEvent(m_timedEventVector.begin()->event, + m_timedEventVector.begin()->eventDispatchProc, + m_timedEventVector.begin()->eventDeleteProc, + m_timedEventVector.begin()->userParam); + + // Remove timed eventand fix heap + std::pop_heap(m_timedEventVector.begin(), m_timedEventVector.end()); + m_timedEventVector.pop_back(); + } + } +} + +unsigned long Thread::GetCurrentTimeMiliseconds() const +{ + timeval tv; + gettimeofday(&tv, NULL); + return static_cast(tv.tv_sec) * 1000 + static_cast(tv.tv_usec) / 1000; +} + +int Thread::Exec() +{ + LogPedantic("Executing thread event processing"); + + const std::size_t MIN_HANDLE_LIST_SIZE = 4; + + // Start processing of events + WaitableHandleListEx handleList; + + // index 0: Quit waitable event handle + handleList.push_back(std::make_pair(m_quitEvent.GetHandle(), WaitMode::Read)); + + // index 1: Event occurred event handle + handleList.push_back(std::make_pair(m_eventInvoker.GetHandle(), WaitMode::Read)); + + // index 2: Timed event occurred event handle + handleList.push_back(std::make_pair(m_timedEventInvoker.GetHandle(), WaitMode::Read)); + + // index 3: Waitable handle watch support invoker + handleList.push_back(std::make_pair(WaitableHandleWatchSupport::WaitableInvokerHandle(), WaitMode::Read)); + + // + // Watch list might have been initialized before threaded started + // Need to fill waitable event watch list in this case + // + { + WaitableHandleListEx waitableHandleWatchHandles = WaitableHandleWatchSupport::WaitableWatcherHandles(); + std::copy(waitableHandleWatchHandles.begin(), waitableHandleWatchHandles.end(), std::back_inserter(handleList)); + } + + // Quit flag + bool quit = false; + + while (!quit) + { + // Retrieve minimum wait time, according to timed events list + unsigned long minimumWaitTime; + + // Critical section on timed events mutex + { + Mutex::ScopedLock lock(&m_timedEventMutex); + + if (!m_timedEventVector.empty()) + { + unsigned long currentTimeMiliseconds = GetCurrentTimeMiliseconds(); + unsigned long destinationTimeMiliseconds = m_timedEventVector.begin()->registerTimeMiliseconds + m_timedEventVector.begin()->dueTimeMiliseconds; + + // Are we already late with timed event ? + if (currentTimeMiliseconds > destinationTimeMiliseconds) + minimumWaitTime = 0; + else + minimumWaitTime = destinationTimeMiliseconds - currentTimeMiliseconds; + } + else + minimumWaitTime = 0xFFFFFFFF; // Infinity + } + + // Info + LogPedantic("Thread loop minimum wait time: " << minimumWaitTime << " ms"); + + // Do thread waiting + WaitableHandleIndexList waitableHandleIndexList = WaitForMultipleHandles(handleList, minimumWaitTime); + + if (waitableHandleIndexList.empty()) + { + // Timeout occurred. Process timed events. + LogPedantic("Timed event list elapsed invoker"); + ProcessTimedEvents(); + continue; + } + + // Go through each index + for (WaitableHandleIndexList::const_iterator waitableHandleIndexIterator = waitableHandleIndexList.begin(); + waitableHandleIndexIterator != waitableHandleIndexList.end(); + ++waitableHandleIndexIterator) + { + size_t index = *waitableHandleIndexIterator; + + LogPedantic("Event loop triggered with index: " << index); + + switch (index) + { + case 0: + // Quit waitable event handle + quit = true; + break; + + case 1: + // Event occurred event handle + ProcessEvents(); + + // Handle direct invoker + if (m_directInvoke) + { + m_directInvoke = false; + + LogPedantic("Handling direct invoker"); + + // Update list + while (handleList.size() > MIN_HANDLE_LIST_SIZE) + handleList.pop_back(); + + // Insert current waitable event handles instead + { + WaitableHandleListEx waitableHandleWatchHandles = WaitableHandleWatchSupport::WaitableWatcherHandles(); + std::copy(waitableHandleWatchHandles.begin(), waitableHandleWatchHandles.end(), std::back_inserter(handleList)); + } + } + + // Done + break; + + case 2: + // Timed event list changed + LogPedantic("Timed event list changed invoker"); + ProcessTimedEvents(); + + // Reset timed event invoker + m_timedEventInvoker.Reset(); + + // Done + break; + + case 3: + // Waitable handle watch support invoker + LogPedantic("Waitable handle watch invoker event occurred"); + + // First, remove all previous handles + while (handleList.size() > MIN_HANDLE_LIST_SIZE) + handleList.pop_back(); + + // Insert current waitable event handles instead + { + WaitableHandleListEx waitableHandleWatchHandles = WaitableHandleWatchSupport::WaitableWatcherHandles(); + std::copy(waitableHandleWatchHandles.begin(), waitableHandleWatchHandles.end(), std::back_inserter(handleList)); + } + + // Handle invoker in waitable watch support + WaitableHandleWatchSupport::InvokerFinished(); + + LogPedantic("Waitable handle watch invoker event handled"); + + // Done + break; + + default: + // Waitable event watch list + LogPedantic("Waitable handle watch event occurred"); + + // Handle event in waitable handle watch + { + std::pair handle = handleList[index]; + WaitableHandleWatchSupport::HandleWatcher(handle.first, handle.second); + } + + if (m_directInvoke) + { + m_directInvoke = false; + + LogPedantic("Handling direct invoker"); + + // Update list + while (handleList.size() > MIN_HANDLE_LIST_SIZE) + handleList.pop_back(); + + // Insert current waitable event handles instead + { + WaitableHandleListEx waitableHandleWatchHandles = + WaitableHandleWatchSupport:: + WaitableWatcherHandles(); + std::copy(waitableHandleWatchHandles.begin(), + waitableHandleWatchHandles.end(), + std::back_inserter(handleList)); + } + } + + LogPedantic("Waitable handle watch event handled"); + + // Done + break; + } + } + } + + LogPedantic("Leaving thread event processing"); + return 0; +} + +void Thread::Run() +{ + LogPedantic("Running thread"); + + // Critical section + { + Mutex::ScopedLock lock(&m_stateMutex); + + if (m_running) + return; + + // Try to create new thread + if (pthread_create(&m_thread, NULL, &StaticThreadEntry, this) != 0) + Throw(Exception::RunFailed); + + // At default, we abandon thread + m_abandon = true; + + // Enter running state + m_running = true; + } + + LogPedantic("Thread run"); +} + +void Thread::Quit() +{ + pthread_t joinableThread; + + // Critical section + { + Mutex::ScopedLock lock(&m_stateMutex); + + // Is thread running ? + if (!m_running) + return; + + LogPedantic("Quitting thread..."); + + // Do not abandon thread, we will join + m_abandon = false; + + // Singal quit waitable event + m_quitEvent.Signal(); + + // Copy joinable thread identifier, because + // we are leaving critical section + joinableThread = m_thread; + } + + // Wait for joinable thread + void *result; + + if (pthread_join(joinableThread, &result) != 0) + Throw(Exception::QuitFailed); + + LogPedantic("Thread quit"); +} + +void Thread::PushEvent(void *event, EventDispatchProc eventDispatchProc, EventDeleteProc eventDeleteProc, void *userParam) +{ + // Enter event list critical section + Mutex::ScopedLock lock(&m_eventMutex); + + // Push new event + m_eventList.push_back(InternalEvent(event, userParam, eventDispatchProc, eventDeleteProc)); + + // Trigger invoker + m_eventInvoker.Signal(); + + LogPedantic("Event pushed and invoker signaled"); +} + +void Thread::PushTimedEvent(void *event, double dueTimeSeconds, EventDispatchProc eventDispatchProc, EventDeleteProc eventDeleteProc, void *userParam) +{ + // Check for developer errors + Assert(dueTimeSeconds >= 0.0); + + // Enter timed event list critical section + Mutex::ScopedLock lock(&m_timedEventMutex); + + // Get current time + unsigned long currentTimeMiliseconds = GetCurrentTimeMiliseconds(); + + // Convert to miliseconds + unsigned long dueTimeMiliseconds = static_cast(1000.0 * dueTimeSeconds); + + // Push new timed event + m_timedEventVector.push_back(InternalTimedEvent(event, userParam, dueTimeMiliseconds, currentTimeMiliseconds, eventDispatchProc, eventDeleteProc)); + + // Heapify timed events + std::make_heap(m_timedEventVector.begin(), m_timedEventVector.end()); + + // Trigger invoker + m_timedEventInvoker.Signal(); + + LogPedantic("Timed event pushed and invoker signaled: due time: " << dueTimeMiliseconds << " ms, absolute due time: " << currentTimeMiliseconds + dueTimeMiliseconds << " ms"); +} + +Thread *Thread::GetInvokerThread() +{ + return this; +} + +void Thread::HandleDirectInvoker() +{ + // We must be in ProcessEvents call stack + // Mark that situation to handle direct invoker + m_directInvoke = true; +} + +void Thread::Sleep(uint64_t seconds) +{ + NanoSleep(seconds * NANOSECONDS_PER_SECOND); +} + +void Thread::MiliSleep(uint64_t miliseconds) +{ + NanoSleep(miliseconds * NANOSECONDS_PER_MILISECOND); +} + +void Thread::MicroSleep(uint64_t microseconds) +{ + NanoSleep(microseconds * NANOSECONDS_PER_MICROSECOND); +} + +void Thread::NanoSleep(uint64_t nanoseconds) +{ + timespec requestedTime = + { + static_cast( + nanoseconds / NANOSECONDS_PER_SECOND), + + static_cast( + nanoseconds % NANOSECONDS_PER_SECOND) + }; + + timespec remainingTime; + + for (;;) + { + if (nanosleep(&requestedTime, &remainingTime) == 0) + break; + + int error = errno; + Assert(error == EINTR); + + requestedTime = remainingTime; + } +} +} // namespace DPL diff --git a/modules/core/src/type_list.cpp b/modules/core/src/type_list.cpp new file mode 100644 index 0000000..8eeb106 --- /dev/null +++ b/modules/core/src/type_list.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file type_list.cpp + * @author Bartosz Janiak (b.janiak@samsung.com) + * @version 1.0 + * @brief Generic type list template + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/core/src/union_cast.cpp b/modules/core/src/union_cast.cpp new file mode 100644 index 0000000..b79b622 --- /dev/null +++ b/modules/core/src/union_cast.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file union_cast.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for union cast + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/core/src/waitable_event.cpp b/modules/core/src/waitable_event.cpp new file mode 100644 index 0000000..6bb021c --- /dev/null +++ b/modules/core/src/waitable_event.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file waitable_event.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of waitable event + */ +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +WaitableEvent::WaitableEvent() +{ + if (pipe(m_pipe) == -1) + Throw(Exception::CreateFailed); + + if (fcntl(m_pipe[0], F_SETFL, O_NONBLOCK | fcntl(m_pipe[0], F_GETFL)) == -1) + Throw(Exception::CreateFailed); +} + +WaitableEvent::~WaitableEvent() +{ + if (TEMP_FAILURE_RETRY(close(m_pipe[0])) == -1) + Throw(Exception::DestroyFailed); + + if (TEMP_FAILURE_RETRY(close(m_pipe[1])) == -1) + Throw(Exception::DestroyFailed); +} + +WaitableHandle WaitableEvent::GetHandle() const +{ + return m_pipe[0]; +} + +void WaitableEvent::Signal() const +{ + char data = 0; + + if (TEMP_FAILURE_RETRY(write(m_pipe[1], &data, 1)) != 1) + Throw(Exception::SignalFailed); +} + +void WaitableEvent::Reset() const +{ + char data; + + if (TEMP_FAILURE_RETRY(read(m_pipe[0], &data, 1)) != 1) + Throw(Exception::ResetFailed); +} +} // namespace DPL diff --git a/modules/core/src/waitable_handle.cpp b/modules/core/src/waitable_handle.cpp new file mode 100644 index 0000000..faaea42 --- /dev/null +++ b/modules/core/src/waitable_handle.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file waitable_handle.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of waitable handle + */ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace // anonymous +{ +void CheckWaitableHandle(WaitableHandle handle) +{ +#ifdef DPL_ENABLE_WAITABLE_HANDLE_BADF_CHECK + // Try to get descriptor flags + int result = fcntl(handle, F_GETFL); + + if (result == -1 && errno == EBADF) + Assert(0 && "CheckWaitableHandle: Invalid WaitableHandle! (EBADF)"); + + Assert(result != -1 && "CheckWaitableHandle: Invalid WaitableHandle!"); +#endif // DPL_ENABLE_WAITABLE_HANDLE_BADF_CHECK +} +} // namespace anonymous + +WaitableHandleIndexList WaitForSingleHandle(WaitableHandle handle, unsigned long miliseconds) +{ + WaitableHandleList waitHandles; + waitHandles.push_back(handle); + return WaitForMultipleHandles(waitHandles, miliseconds); +} + +WaitableHandleIndexList WaitForSingleHandle(WaitableHandle handle, WaitMode::Type mode, unsigned long miliseconds) +{ + WaitableHandleListEx waitHandles; + waitHandles.push_back(std::make_pair(handle, mode)); + return WaitForMultipleHandles(waitHandles, miliseconds); +} + +WaitableHandleIndexList WaitForMultipleHandles(const WaitableHandleList &waitableHandleList, unsigned long miliseconds) +{ + WaitableHandleListEx handleList; + + for (WaitableHandleList::const_iterator iterator = waitableHandleList.begin(); + iterator != waitableHandleList.end(); + ++iterator) + { + // Wait for multiple objects + handleList.push_back(std::make_pair(*iterator, WaitMode::Read)); + } + + // Do waiting + return WaitForMultipleHandles(handleList, miliseconds); +} + +WaitableHandleIndexList WaitForMultipleHandles(const WaitableHandleListEx &waitableHandleListEx, unsigned long miliseconds) +{ + fd_set readFds, writeFds, errorFds; + + // Fill sets + int maxFd = -1; + + FD_ZERO(&readFds); + FD_ZERO(&writeFds); + FD_ZERO(&errorFds); + + // Add read wait handles + for (WaitableHandleListEx::const_iterator iterator = waitableHandleListEx.begin(); + iterator != waitableHandleListEx.end(); + ++iterator) + { + if (iterator->first > maxFd) + maxFd = iterator->first; + + CheckWaitableHandle(iterator->first); + + // Handle errors along with read and write events + FD_SET(iterator->first, &errorFds); + + if (iterator->second == WaitMode::Read) + { + FD_SET(iterator->first, &readFds); + } + else if (iterator->second == WaitMode::Write) + { + FD_SET(iterator->first, &writeFds); + } + } + + // Do select + timeval timeout; + timeval *effectiveTimeout = NULL; + if (miliseconds != 0xFFFFFFFF) { + timeout.tv_sec = miliseconds / 1000; + timeout.tv_usec = (miliseconds % 1000) * 1000; + effectiveTimeout = &timeout; + } + + if (TEMP_FAILURE_RETRY(select(maxFd + 1, &readFds, &writeFds, &errorFds, effectiveTimeout)) == -1) + Throw(WaitFailed); + + // Check results + WaitableHandleIndexList indexes; + size_t index = 0; + + for (WaitableHandleListEx::const_iterator iterator = waitableHandleListEx.begin(); + iterator != waitableHandleListEx.end(); + ++iterator) + { + // Always return errors, no matter what type of listening is set + if (FD_ISSET(iterator->first, &errorFds)) { + indexes.push_back(index); + } + else if (iterator->second == WaitMode::Read) + { + if (FD_ISSET(iterator->first, &readFds)) + indexes.push_back(index); + } + else if (iterator->second == WaitMode::Write) + { + if (FD_ISSET(iterator->first, &writeFds)) + indexes.push_back(index); + } + ++index; + } + + // Successfuly awaited some events or timeout occurred + return indexes; +} +} // namespace DPL diff --git a/modules/core/src/waitable_handle_watch_support.cpp b/modules/core/src/waitable_handle_watch_support.cpp new file mode 100644 index 0000000..19c10e7 --- /dev/null +++ b/modules/core/src/waitable_handle_watch_support.cpp @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file waitable_handle_watch_support.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of waitable handle watch support + */ +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +WaitableHandleWatchSupport::WaitableHandleWatchSupport() +{ +} + +WaitableHandleWatchSupport::~WaitableHandleWatchSupport() +{ + // Developer assertions + if (!m_watchersMap.empty()) + { + LogPedantic("### Leaked watchers map dump ###"); + + for (WaitableHandleWatchersMap::const_iterator iterator = m_watchersMap.begin(); + iterator != m_watchersMap.end(); + ++iterator) + { + LogPedantic("### Waitable handle: " << iterator->first); + + LogPedantic("### Read listeners: " << iterator->second.readListenersCount); + LogPedantic("### Write listeners: " << iterator->second.writeListenersCount); + + for (WaitableHandleListenerList::const_iterator listenersIterator = iterator->second.listeners.begin(); + listenersIterator != iterator->second.listeners.end(); + ++listenersIterator) + { + LogPedantic("### Mode: " << listenersIterator->mode << ". Listener: 0x" << std::hex << listenersIterator->listener); + } + } + } + + Assert(m_watchersMap.empty() == true); +} + +WaitableHandle WaitableHandleWatchSupport::WaitableInvokerHandle() const +{ + return m_watchersInvoker.GetHandle(); +} + +WaitableHandleListEx WaitableHandleWatchSupport::WaitableWatcherHandles() const +{ + // Critical section + { + RecursiveMutex::ScopedLock lock(&m_watchersMutex); + + WaitableHandleListEx handleList; + + for (WaitableHandleWatchersMap::const_iterator iterator = m_watchersMap.begin(); + iterator != m_watchersMap.end(); + ++iterator) + { + // Register waitable event id for wait + // Check if there are any read listeners and write listeners + // and register for both if applicable + if (iterator->second.readListenersCount > 0) + handleList.push_back(std::make_pair(iterator->first, WaitMode::Read)); + + if (iterator->second.writeListenersCount > 0) + handleList.push_back(std::make_pair(iterator->first, WaitMode::Write)); + } + + return handleList; + } +} + +void WaitableHandleWatchSupport::InvokerFinished() +{ + LogPedantic("Invoker finished called"); + + // Reset invoker + m_watchersInvoker.Reset(); + + // Commit invoke + m_watchersInvokerCommit.Signal(); +} + +void WaitableHandleWatchSupport::HandleWatcher(WaitableHandle waitableHandle, WaitMode::Type mode) +{ + // + // Waitable event occurred + // Now call all listeners for that waitable event. It is possible + // that some of listeners early disappeared. This is not a problem. + // Warning: Listeners and/or watcher may also disappear during dispatching handlers! + // + LogPedantic("Waitable event occurred"); + + // Critical section for other threads + { + RecursiveMutex::ScopedLock lock(&m_watchersMutex); + + // Notice: We must carefully call watchers here as they may disappear (zero listeners) or be created during each of handler call + // All removed listeners are handled correctly. Adding additional listener to the same waitable handle + // during handler dispatch sequence is _not_ supported. + WaitableHandleWatchersMap trackedWatchers = m_watchersMap; + + for (WaitableHandleWatchersMap::const_iterator trackedWatchersIterator = trackedWatchers.begin(); + trackedWatchersIterator != trackedWatchers.end(); + ++trackedWatchersIterator) + { + // Check if this watcher still exists + // If not, go to next tracked watcher + if (m_watchersMap.find(trackedWatchersIterator->first) == m_watchersMap.end()) + { + LogPedantic("Watcher disappeared during watcher handler"); + continue; + } + + // Is this is a waitable handle that we are searching for ? + if (waitableHandle != trackedWatchersIterator->first) + continue; + + // Track watcher listeners list + WaitableHandleListenerList trackedListeners = trackedWatchersIterator->second.listeners; + + LogPedantic("Calling waitable event listeners (" << trackedListeners.size() << ")..."); + + // Notice: We must carefully call listeners here as they may disappear or be created during each of handler call + // All removed listeners are handled correctly. Adding additional listener to the same waitable handle + // during handler dispatch sequence is should be also handled, as an extremly case. + + // Call all waitable event listeners who listen for that event + for (WaitableHandleListenerList::const_iterator trackedListenersIterator = trackedListeners.begin(); + trackedListenersIterator != trackedListeners.end(); + ++trackedListenersIterator) + { + // Check if this watcher still exists + // If not, there cannot be another one. Must exit now (after break, we actually exit) + if (m_watchersMap.find(trackedWatchersIterator->first) == m_watchersMap.end()) + { + LogPedantic("Watcher disappeared during watcher handler"); + break; + } + + // Check if this watcher listener still exists + // If not, go to next tracked watcher listener + bool listenerStillExists = false; + + for (WaitableHandleListenerList::const_iterator searchListenerIterator = trackedWatchersIterator->second.listeners.begin(); + searchListenerIterator != trackedWatchersIterator->second.listeners.end(); + ++searchListenerIterator) + { + if (searchListenerIterator->listener == trackedListenersIterator->listener && + searchListenerIterator->mode == trackedListenersIterator->mode) + { + listenerStillExists = true; + break; + } + } + + if (!listenerStillExists) + { + LogPedantic("Watcher listener disappeared during watcher handler"); + break; + } + + // Is this is a listener mode that we are searching for ? + if (mode != trackedListenersIterator->mode) + continue; + + // Call waitable event watch listener + LogPedantic("Before tracker listener call..."); + trackedListenersIterator->listener->OnWaitableHandleEvent(trackedWatchersIterator->first, trackedListenersIterator->mode); + LogPedantic("After tracker listener call..."); + } + + // Now call all those listeners who registered during listener calls + // FIXME: Implement! Notice, that scenario may be recursive! + + LogPedantic("Waitable event listeners called"); + + // No more waitable events possible - consistency check + break; + } + } +} + +void WaitableHandleWatchSupport::AddWaitableHandleWatch(WaitableHandleListener* listener, WaitableHandle waitableHandle, WaitMode::Type mode) +{ + // Enter waitable event list critical section + RecursiveMutex::ScopedLock lock(&m_watchersMutex); + + // Find proper list to register into + WaitableHandleWatchersMap::iterator mapIterator = m_watchersMap.find(waitableHandle); + + if (mapIterator != m_watchersMap.end()) + { + // Assert if there is no such listener already that is listening in this mode + for (WaitableHandleListenerList::iterator listenersIterator = mapIterator->second.listeners.begin(); + listenersIterator != mapIterator->second.listeners.end(); + ++listenersIterator) + { + // Must not insert same listener-mode pair + Assert(listenersIterator->listener != listener || listenersIterator->mode != mode); + } + } + + LogPedantic("Adding waitable handle watch: " << waitableHandle); + + // Push new waitable event watch + if (mapIterator != m_watchersMap.end()) + mapIterator->second.listeners.push_back(WaitableHandleWatcher(listener, mode)); + else + m_watchersMap[waitableHandle].listeners.push_back(WaitableHandleWatcher(listener, mode)); + + // Update counters + switch (mode) + { + case WaitMode::Read: + m_watchersMap[waitableHandle].readListenersCount++; + break; + + case WaitMode::Write: + m_watchersMap[waitableHandle].writeListenersCount++; + break; + + default: + Assert(0); + } + + // Trigger waitable event invoker to commit changes + CommitInvoker(); + + LogPedantic("Waitable event watch added and invoker signaled"); +} + +void WaitableHandleWatchSupport::RemoveWaitableHandleWatch(WaitableHandleListener *listener, WaitableHandle waitableHandle, WaitMode::Type mode) +{ + // Enter waitable event list critical section + RecursiveMutex::ScopedLock lock(&m_watchersMutex); + + // Find proper list with listener + WaitableHandleWatchersMap::iterator mapIterator = m_watchersMap.find(waitableHandle); + + Assert(mapIterator != m_watchersMap.end()); + + // Assert if there is such listener and mode + WaitableHandleListenerList::iterator listIterator = mapIterator->second.listeners.end(); + + for (WaitableHandleListenerList::iterator listenersIterator = mapIterator->second.listeners.begin(); + listenersIterator != mapIterator->second.listeners.end(); + ++listenersIterator) + { + // Check same pair listener-mode + if (listenersIterator->listener == listener && listenersIterator->mode == mode) + { + listIterator = listenersIterator; + break; + } + } + + // Same pair listener-mode must exist + Assert(listIterator != mapIterator->second.listeners.end()); + + LogPedantic("Removing waitable handle watch: " << waitableHandle); + + // Remove waitable event watch + mapIterator->second.listeners.erase(listIterator); + + // Update counters + switch (mode) + { + case WaitMode::Read: + mapIterator->second.readListenersCount--; + break; + + case WaitMode::Write: + mapIterator->second.writeListenersCount--; + break; + + default: + Assert(0); + } + + // If list is empty, remove it too + if (mapIterator->second.listeners.empty()) + m_watchersMap.erase(mapIterator); + + // Trigger waitable event invoker to commit changes + CommitInvoker(); + + LogPedantic("Waitable event watch removed and invoker signaled"); +} + +void WaitableHandleWatchSupport::CommitInvoker() +{ + // Check calling context and execute invoker + if (Thread::GetCurrentThread() == GetInvokerThread()) + { + LogPedantic("Calling direct invoker"); + + // Direct invoker call + HandleDirectInvoker(); + } + else + { + LogPedantic("Calling indirect invoker"); + + // Indirect invoker call + m_watchersInvoker.Signal(); + + WaitableHandleList waitHandles; + waitHandles.push_back(m_watchersInvokerCommit.GetHandle()); + WaitForMultipleHandles(waitHandles); + + m_watchersInvokerCommit.Reset(); + } +} + +WaitableHandleWatchSupport *WaitableHandleWatchSupport::InheritedContext() +{ + // In threaded context, return thread waitable handle watch implementation + // In main loop, return main waitable handle watch implementation + if (Thread::GetCurrentThread() != NULL) + return Thread::GetCurrentThread(); + else + return &MainSingleton::Instance(); +} +} // namespace DPL diff --git a/modules/core/src/zip_input.cpp b/modules/core/src/zip_input.cpp new file mode 100644 index 0000000..d862c01 --- /dev/null +++ b/modules/core/src/zip_input.cpp @@ -0,0 +1,622 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file zip_input.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of zip input + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace // anonymous +{ +const size_t EXTRACT_BUFFER_SIZE = 4096; + +class ScopedUnzClose +{ +private: + unzFile m_file; + +public: + ScopedUnzClose(unzFile file) + : m_file(file) + { + } + + ~ScopedUnzClose() + { + if (!m_file) + return; + + if (unzClose(m_file) != UNZ_OK) + LogPedantic("Failed to close zip input file"); + } + + unzFile Release() + { + unzFile file = m_file; + + m_file = NULL; + + return file; + } +}; +} // namespace anonymous + +/* + * Seekable multiplexing device + * + * Explanation: + * Minizip library lacks serious support for multithreaded + * access to zip files. Thus, they cannot be easily extracted + * simulateously. Here is introduced seekable device which does + * have a context with seek index for each file. File is mapped to + * memory and because of that no real synchronization is needed. + * Memory addresses can be indexed. + * + * About generalization: + * To achieve the same results on abstract input device, there must be + * provided a mechanism to read data from random address without synchronization. + * In other words: stateless. As described above, stateless property can be + * achieved via memory mapping. + */ +class Device +{ +private: + DPL::ScopedPtr m_fileMapping; + + struct File + { + off64_t offset; + Device *device; + + File(Device *d) + : offset(0), + device(d) + { + } + }; + +public: + Device(const std::string &fileName) + { + Try + { + LogPedantic("Creating file mapping"); + m_fileMapping.Reset(new FileInputMapping(fileName)); + } + Catch (FileInputMapping::Exception::Base) + { + LogPedantic("Failed to create file mapping"); + + ReThrowMsg(ZipInput::Exception::OpenFailed, + "Failed to open zip file mapping"); + } + + LogPedantic("File mapping created"); + } + + // zlib_filefunc64_def interface: files + static voidpf ZCALLBACK open64_file(voidpf opaque, + const void* filename, + int mode) + { + (void)filename; + (void)mode; + + Device *device = static_cast(opaque); + + // Open file for master device + return new File(device); + } + + static uLong ZCALLBACK read_file(voidpf opaque, + voidpf pstream, + void* buf, + uLong size) + { + Device *device = static_cast(opaque); + File *deviceFile = static_cast(pstream); + + // Check if offset is out of bounds + if (deviceFile->offset >= device->m_fileMapping->GetSize()) + { + LogPedantic("Device: read offset out of bounds"); + return -1; + } + + off64_t bytesLeft = device->m_fileMapping->GetSize() - + deviceFile->offset; + + off64_t bytesToRead; + + // Calculate bytes to read + if (static_cast(size) > bytesLeft) + bytesToRead = bytesLeft; + else + bytesToRead = static_cast(size); + + // Do copy + memcpy(buf, + device->m_fileMapping->GetAddress() + deviceFile->offset, + static_cast(bytesToRead)); + + // Increment file offset + deviceFile->offset += bytesToRead; + + // Return bytes that were actually read + return static_cast(bytesToRead); + } + + static uLong ZCALLBACK write_file(voidpf opaque, + voidpf stream, + const void* buf, + uLong size) + { + (void)opaque; + (void)stream; + (void)buf; + (void)size; + + // Not supported by device + LogPedantic("Unsupported function called!"); + return -1; + } + + static int ZCALLBACK close_file(voidpf opaque, + voidpf stream) + { + (void)opaque; + File *deviceFile = static_cast(stream); + + // Delete file + delete deviceFile; + + // Always OK + return 0; + } + + static int ZCALLBACK testerror_file(voidpf opaque, + voidpf stream) + { + (void)opaque; + (void)stream; + + // No errors + return 0; + } + + static ZPOS64_T ZCALLBACK tell64_file(voidpf opaque, + voidpf stream) + { + (void)opaque; + File *deviceFile = static_cast(stream); + + return static_cast(deviceFile->offset); + } + + static long ZCALLBACK seek64_file(voidpf opaque, + voidpf stream, + ZPOS64_T offset, + int origin) + { + Device *device = static_cast(opaque); + File *deviceFile = static_cast(stream); + + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_SET: + deviceFile->offset = static_cast(offset); + + break; + + case ZLIB_FILEFUNC_SEEK_CUR: + deviceFile->offset += static_cast(offset); + + break; + + case ZLIB_FILEFUNC_SEEK_END: + deviceFile->offset = + device->m_fileMapping->GetSize() - + static_cast(offset); + + break; + + default: + return -1; + } + + return 0; + } +}; + +ZipInput::ZipInput(const std::string &fileName) + : m_device(NULL), + m_numberOfFiles(0), + m_globalComment(), + m_fileInfos() +{ + LogPedantic("Zip input file: " << fileName); + + // Create master device + LogPedantic("Creating master device"); + ScopedPtr device(new Device(fileName)); + + // Open master file + zlib_filefunc64_def interface; + interface.zopen64_file = &Device::open64_file; + interface.zread_file = &Device::read_file; + interface.zwrite_file = &Device::write_file; + interface.ztell64_file = &Device::tell64_file; + interface.zseek64_file = &Device::seek64_file; + interface.zclose_file = &Device::close_file; + interface.zerror_file = &Device::testerror_file; + interface.opaque = device.Get(); + + LogPedantic("Opening zip file"); + unzFile file = unzOpen2_64(NULL, &interface); + + if (file == NULL) + { + LogPedantic("Failed to open zip file"); + + // Some errror occured + ThrowMsg(Exception::OpenFailed, + "Failed to open zip file: " << fileName); + } + + // Begin scope + ScopedUnzClose scopedUnzClose(file); + + // Read contents + ReadGlobalInfo(file); + ReadGlobalComment(file); + ReadInfos(file); + + // Release scoped unz close + m_masterFile = scopedUnzClose.Release(); + m_device = device.Release(); + + LogPedantic("Zip file opened"); +} + +ZipInput::~ZipInput() +{ + // Close zip + if (unzClose(static_cast(m_masterFile)) != UNZ_OK) + LogPedantic("Failed to close zip input file"); + + // Close device + delete m_device; +} + +void ZipInput::ReadGlobalInfo(void *masterFile) +{ + // Read number of entries and global comment + unz_global_info globalInfo; + + if (unzGetGlobalInfo(static_cast(masterFile), + &globalInfo) != UNZ_OK) + { + LogPedantic("Failed to read zip global info"); + + ThrowMsg(Exception::ReadGlobalInfoFailed, + "Failed to read global info"); + } + + m_numberOfFiles = static_cast(globalInfo.number_entry); + m_globalCommentSize = static_cast(globalInfo.size_comment); + + LogPedantic("Number of files: " << m_numberOfFiles); + LogPedantic("Global comment size: " << m_globalCommentSize); +} + +void ZipInput::ReadGlobalComment(void *masterFile) +{ + ScopedArray comment(new char[m_globalCommentSize + 1]); + + if (unzGetGlobalComment(static_cast(masterFile), + comment.Get(), + m_globalCommentSize + 1) != UNZ_OK) + { + LogPedantic("Failed to read zip global comment"); + + ThrowMsg(Exception::ReadGlobalCommentFailed, + "Failed to read global comment"); + } + + m_globalComment = comment.Get(); + LogPedantic("Global comment: " << m_globalComment); +} + +void ZipInput::ReadInfos(void *masterFile) +{ + // Read infos + m_fileInfos.reserve(m_numberOfFiles); + + if (unzGoToFirstFile(static_cast(masterFile)) != UNZ_OK) + { + LogPedantic("Failed to go to first file"); + ThrowMsg(Exception::SeekFileFailed, "Failed to seek first file"); + } + + for (size_t i = 0; i < m_numberOfFiles; ++i) + { + unz_file_pos_s filePos; + + if (unzGetFilePos(static_cast(masterFile), + &filePos) != UNZ_OK) + { + LogPedantic("Failed to get file pos"); + ThrowMsg(Exception::FileInfoFailed, "Failed to get zip file info"); + } + + unz_file_info64 fileInfo; + + if (unzGetCurrentFileInfo64(static_cast(masterFile), + &fileInfo, + NULL, + 0, + NULL, + 0, + NULL, + 0) != UNZ_OK) + { + LogPedantic("Failed to get file pos"); + ThrowMsg(Exception::FileInfoFailed, "Failed to get zip file info"); + } + + ScopedArray fileName(new char[fileInfo.size_filename + 1]); + ScopedArray fileComment(new char[fileInfo.size_file_comment + 1]); + + if (unzGetCurrentFileInfo64(static_cast(masterFile), + &fileInfo, + fileName.Get(), + fileInfo.size_filename + 1, + NULL, + 0, + fileComment.Get(), + fileInfo.size_file_comment + 1) != UNZ_OK) + { + LogPedantic("Failed to get file pos"); + ThrowMsg(Exception::FileInfoFailed, "Failed to get zip file info"); + } + + m_fileInfos.push_back( + FileInfo( + FileHandle( + static_cast(filePos.pos_in_zip_directory), + static_cast(filePos.num_of_file) + ), + std::string(fileName.Get()), + std::string(fileComment.Get()), + static_cast(fileInfo.version), + static_cast(fileInfo.version_needed), + static_cast(fileInfo.flag), + static_cast(fileInfo.compression_method), + static_cast(fileInfo.dosDate), + static_cast(fileInfo.crc), + static_cast(fileInfo.compressed_size), + static_cast(fileInfo.uncompressed_size), + static_cast(fileInfo.disk_num_start), + static_cast(fileInfo.internal_fa), + static_cast(fileInfo.external_fa), + FileDateTime( + fileInfo.tmu_date.tm_sec, + fileInfo.tmu_date.tm_min, + fileInfo.tmu_date.tm_hour, + fileInfo.tmu_date.tm_mday, + fileInfo.tmu_date.tm_mon, + fileInfo.tmu_date.tm_year + ) + ) + ); + + // If this is not the last file, go to next one + if (i != m_numberOfFiles - 1) + { + if (unzGoToNextFile( + static_cast(masterFile))!= UNZ_OK) + { + LogPedantic("Failed to go to next file"); + + ThrowMsg(Exception::FileInfoFailed, + "Failed to go to next file"); + } + } + } +} + +ZipInput::const_iterator ZipInput::begin() const +{ + return m_fileInfos.begin(); +} + +ZipInput::const_iterator ZipInput::end() const +{ + return m_fileInfos.end(); +} + +ZipInput::const_reverse_iterator ZipInput::rbegin() const +{ + return m_fileInfos.rbegin(); +} + +ZipInput::const_reverse_iterator ZipInput::rend() const +{ + return m_fileInfos.rend(); +} + +ZipInput::size_type ZipInput::size() const +{ + return m_fileInfos.size(); +} + +ZipInput::File *ZipInput::OpenFile(FileHandle handle) +{ + return new File(m_device, handle); +} + +ZipInput::File *ZipInput::OpenFile(const std::string &fileName) +{ + FOREACH(iterator, m_fileInfos) + { + if (iterator->name == fileName) + { + return new File(m_device, iterator->handle); + } + } + + ThrowMsg(Exception::OpenFileFailed, + "Failed to open zip file: " << fileName); +} + +ZipInput::File::File(class Device *device, FileHandle handle) +{ + // Open file file + zlib_filefunc64_def interface; + interface.zopen64_file = &Device::open64_file; + interface.zread_file = &Device::read_file; + interface.zwrite_file = &Device::write_file; + interface.ztell64_file = &Device::tell64_file; + interface.zseek64_file = &Device::seek64_file; + interface.zclose_file = &Device::close_file; + interface.zerror_file = &Device::testerror_file; + interface.opaque = device; + + LogPedantic("Opening zip file"); + unzFile file = unzOpen2_64(NULL, &interface); + + if (file == NULL) + { + LogPedantic("Failed to open zip file"); + + // Some errror occured + ThrowMsg(ZipInput::Exception::OpenFileFailed, + "Failed to open zip file"); + } + + // Begin scope + ScopedUnzClose scopedUnzClose(file); + + // Look up file handle + unz64_file_pos filePos = + { + static_cast(handle.first), + static_cast(handle.second) + }; + + if (unzGoToFilePos64(file, &filePos) != UNZ_OK) + { + LogPedantic("Failed to seek to zip file"); + + // Some errror occured + ThrowMsg(ZipInput::Exception::OpenFileFailed, + "Failed to open zip file"); + } + + // Open current file for reading + if (unzOpenCurrentFile(file) != UNZ_OK) + { + LogPedantic("Failed to open current zip file"); + + // Some errror occured + ThrowMsg(ZipInput::Exception::OpenFileFailed, + "Failed to open current zip file"); + } + + // Release scoped unz close + m_file = scopedUnzClose.Release(); + + LogPedantic("Zip file opened"); +} + +ZipInput::File::~File() +{ + // Close current file for reading + if (unzCloseCurrentFile(static_cast(m_file)) != UNZ_OK) + LogPedantic("Failed to close current zip input file"); + + // Close zip file + if (unzClose(static_cast(m_file)) != UNZ_OK) + LogPedantic("Failed to close zip input file"); +} + +DPL::BinaryQueueAutoPtr ZipInput::File::Read(size_t size) +{ + // Do not even try to unzip if requested zero bytes + if (size == 0) + return DPL::BinaryQueueAutoPtr(new DPL::BinaryQueue()); + + // Calc data to read + size_t sizeToRead = size > EXTRACT_BUFFER_SIZE ? + EXTRACT_BUFFER_SIZE : + size; + + // Extract zip file data (one-copy) + ScopedFree rawBuffer(malloc(sizeToRead)); + + if (!rawBuffer) + throw std::bad_alloc(); + + // Do unpack + int bytes = unzReadCurrentFile(static_cast(m_file), + rawBuffer.Get(), + sizeToRead); + + // Internal unzipper error + if (bytes < 0) + { + LogPedantic("Extract failed. Error: " << bytes); + + ThrowMsg(ZipInput::Exception::ReadFileFailed, + "Failed to extract file with error: " << bytes); + } + + // Data was read (may be zero bytes) + DPL::BinaryQueueAutoPtr buffer(new DPL::BinaryQueue()); + + buffer->AppendUnmanaged(rawBuffer.Get(), + static_cast(bytes), + &DPL::BinaryQueue::BufferDeleterFree, + NULL); + + rawBuffer.Release(); + + return buffer; +} + +const std::string &ZipInput::GetGlobalComment() const +{ + return m_globalComment; +} + +bool ZipInput::empty() const +{ + return m_fileInfos.empty(); +} +} // namespace DPL diff --git a/modules/db/config.cmake b/modules/db/config.cmake new file mode 100644 index 0000000..96ccbb9 --- /dev/null +++ b/modules/db/config.cmake @@ -0,0 +1,45 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file config.cmake +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +SET(DPL_DB_SOURCES + ${PROJECT_SOURCE_DIR}/modules/db/src/naive_synchronization_object.cpp + ${PROJECT_SOURCE_DIR}/modules/db/src/orm.cpp + ${PROJECT_SOURCE_DIR}/modules/db/src/sql_connection.cpp + ${PROJECT_SOURCE_DIR}/modules/db/src/thread_database_support.cpp + PARENT_SCOPE +) + + +SET(DPL_DB_HEADERS + ${PROJECT_SOURCE_DIR}/modules/db/include/dpl/db/naive_synchronization_object.h + ${PROJECT_SOURCE_DIR}/modules/db/include/dpl/db/orm_generator.h + ${PROJECT_SOURCE_DIR}/modules/db/include/dpl/db/orm.h + ${PROJECT_SOURCE_DIR}/modules/db/include/dpl/db/orm_interface.h + ${PROJECT_SOURCE_DIR}/modules/db/include/dpl/db/orm_macros.h + ${PROJECT_SOURCE_DIR}/modules/db/include/dpl/db/sql_connection.h + ${PROJECT_SOURCE_DIR}/modules/db/include/dpl/db/thread_database_support.h + PARENT_SCOPE +) + +SET(DPL_DB_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/modules/db/include + PARENT_SCOPE +) diff --git a/modules/db/include/dpl/db/naive_synchronization_object.h b/modules/db/include/dpl/db/naive_synchronization_object.h new file mode 100644 index 0000000..37d268c --- /dev/null +++ b/modules/db/include/dpl/db/naive_synchronization_object.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file naive_synchronization_object.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of SQL naive synchronization object + */ +#ifndef DPL_NAIVE_SYNCHRONIZATION_OBJECT_H +#define DPL_NAIVE_SYNCHRONIZATION_OBJECT_H + +#include + +namespace DPL +{ +namespace DB +{ + +/** + * Naive synchronization object used to synchronize SQL connection + * to the same database across different threads and processes + */ +class NaiveSynchronizationObject + : public SqlConnection::SynchronizationObject +{ +public: + // [SqlConnection::SynchronizationObject] + virtual void Synchronize(); + virtual void NotifyAll(); +}; + +} // namespace DB +} // namespace DPL + +#endif // DPL_NAIVE_SYNCHRONIZATION_OBJECT_H diff --git a/modules/db/include/dpl/db/orm.h b/modules/db/include/dpl/db/orm.h new file mode 100644 index 0000000..f4df084 --- /dev/null +++ b/modules/db/include/dpl/db/orm.h @@ -0,0 +1,704 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file orm.h + * @author Bartosz Janiak (b.janiak@samsung.com) + * @version 1.0 + * @brief DPL-ORM: Object-relational mapping for sqlite database, written on top of DPL. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef DPL_ORM_H +#define DPL_ORM_H + +namespace DPL { +namespace DB { +namespace ORM { + +//TODO move to type utils +#define DPL_CHECK_TYPE_INSTANTIABILITY(type) \ + { \ + type _ignored_; \ + (void)_ignored_; \ + } + +typedef size_t ColumnIndex; +typedef size_t ArgumentIndex; +typedef DPL::Optional OptionalString; +typedef DPL::Optional OptionalInteger; +typedef DPL::DB::SqlConnection::DataCommand DataCommand; + +namespace RelationTypes { + extern const char Equal[]; + extern const char LessThan[]; + extern const char And[]; + extern const char Or[]; + extern const char Is[]; + //TODO define more relation types +} + +namespace DataCommandUtils { + //TODO move to DPL::DataCommand? + void BindArgument(DataCommand *command, ArgumentIndex index, int argument); + void BindArgument(DataCommand *command, ArgumentIndex index, const OptionalInteger& argument); + void BindArgument(DataCommand *command, ArgumentIndex index, const DPL::String& argument); + void BindArgument(DataCommand *command, ArgumentIndex index, const OptionalString& argument); +} +class Expression { +public: + virtual ~Expression() {} + virtual std::string GetString() const = 0; + virtual ArgumentIndex BindTo(DataCommand *command, ArgumentIndex index) = 0; +}; + +typedef DPL::SharedPtr ExpressionPtr; + +template +class BinaryExpression : public Expression { +protected: + LeftExpression m_leftExpression; + RightExpression m_rightExpression; + bool m_outerParenthesis; +public: + BinaryExpression(const LeftExpression& leftExpression, const RightExpression& rightExpression, bool outerParenthesis = true) : + m_leftExpression(leftExpression), + m_rightExpression(rightExpression), + m_outerParenthesis(outerParenthesis) + {} + + virtual std::string GetString() const + { + return (m_outerParenthesis ? "( " : " " ) + + m_leftExpression.GetString() + " " + Operator + " " + m_rightExpression.GetString() + + (m_outerParenthesis ? " )" : " " ) ; + } + + virtual ArgumentIndex BindTo(DataCommand *command, ArgumentIndex index) + { + index = m_leftExpression.BindTo(command, index); + return m_rightExpression.BindTo(command, index); + } + + template + struct ValidForTable { + typedef std::pair::Yes , + typename RightExpression::template ValidForTable::Yes > + Yes; + }; +}; + +template +BinaryExpression + And(const LeftExpression& leftExpression, const RightExpression& rightExpression) +{ + return BinaryExpression + (leftExpression, rightExpression); +} + +template +class ExpressionWithArgument : public Expression { +protected: + ArgumentType argument; + +public: + explicit ExpressionWithArgument(const ArgumentType& _argument) : argument(_argument) {} + + virtual ArgumentIndex BindTo(DataCommand *command, ArgumentIndex index) + { + DataCommandUtils::BindArgument(command, index, argument); + return index + 1; + } +}; + + +template +class Compare : public ExpressionWithArgument { +public: + explicit Compare(typename ColumnData::ColumnType column) : + ExpressionWithArgument(column) + {} + + virtual std::string GetString() const + { + std::string statement; + statement += ColumnData::GetColumnName(); + statement += " "; + statement += Relation; + statement += " ?"; + return statement; + } + + template + struct ValidForTable { + typedef typename TableDefinition::ColumnList::template Contains Yes; + }; +}; +#define ORM_DEFINE_COMPARE_EXPRESSION(name, relationType) \ + template \ + class name : public Compare { \ + public: \ + name(typename ColumnData::ColumnType column) : \ + Compare(column) \ + {} \ + }; + +ORM_DEFINE_COMPARE_EXPRESSION(Equals, Equal) +ORM_DEFINE_COMPARE_EXPRESSION(Is, Is) + +template +ColumnType GetColumnFromCommand(ColumnIndex columnIndex, DataCommand *command); + +template +class FillRowUtil { +public: + static void FillRow(Row& row, ColumnIndex columnIndex, DataCommand *command) + { + typename ColumnList::Head::ColumnType rowField; + rowField = GetColumnFromCommand(columnIndex, command); + ColumnList::Head::SetRowField(row, rowField); + FillRowUtil::FillRow(row, columnIndex + 1, command); + } +}; + +template +class FillRowUtil { +public: + static void FillRow(Row&, ColumnIndex, DataCommand *) + { /* do nothing, we're past the last element of column list */ } +}; + +class Exception { +public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, SelectReuseWithDifferentQuerySignature) + DECLARE_EXCEPTION_TYPE(Base, RowFieldNotInitialized) + DECLARE_EXCEPTION_TYPE(Base, EmptyUpdateStatement) +}; + +template +class Query +{ +protected: + explicit Query(IOrmInterface* interface) : + m_interface(interface), + m_command(NULL) + { + } + + virtual ~Query() + { + if (m_command == NULL) + return; + + TableDefinition::FreeTableDataCommand(m_command, m_interface); + } + + IOrmInterface* m_interface; + DataCommand *m_command; + std::string m_commandString; + ArgumentIndex m_bindArgumentIndex; +}; + +template +class QueryWithWhereClause : public Query +{ +protected: + ExpressionPtr m_whereExpression; + + void Prepare() + { + if ( !!m_whereExpression ) + { + this->m_commandString += " WHERE "; + this->m_commandString += m_whereExpression->GetString(); + } + } + + void Bind() + { + if ( !!m_whereExpression ) + { + this->m_bindArgumentIndex = m_whereExpression->BindTo( + this->m_command, this->m_bindArgumentIndex); + } + } + +public: + explicit QueryWithWhereClause(IOrmInterface* interface) : + Query(interface) + { + } + + template + void Where(const Expression& expression) + { + DPL_CHECK_TYPE_INSTANTIABILITY(typename Expression::template ValidForTable::Yes); + if ( !!m_whereExpression && ( typeid(Expression) != typeid(*m_whereExpression) ) ) + { + std::ostringstream str; + str << "Current ORM implementation doesn't allow to reuse Select" + " instance with different query signature (particularly " + "WHERE on different column).\n"; + str << "Query: "; + str << this->m_commandString; + ThrowMsg(Exception::SelectReuseWithDifferentQuerySignature, + str.str()); + } + //TODO maybe don't make a copy here but just generate the string part of the query. + m_whereExpression.Reset(new Expression(expression)); + } + +}; + +template +class Delete : public QueryWithWhereClause +{ +protected: + void Prepare() + { + if ( !this->m_command) + { + this->m_commandString = "DELETE FROM "; + this->m_commandString += TableDefinition::GetName(); + + QueryWithWhereClause::Prepare(); + + this->m_command = TableDefinition::AllocTableDataCommand( + this->m_commandString.c_str(), + Query::m_interface); + LogPedantic("Prepared SQL command " << this->m_commandString); + } + } + + void Bind() + { + this->m_bindArgumentIndex = 1; + QueryWithWhereClause::Bind(); + } + +public: + explicit Delete(IOrmInterface *interface = NULL) : + QueryWithWhereClause(interface) + { + } + + void Execute() + { + Prepare(); + Bind(); + this->m_command->Step(); + this->m_command->Reset(); + } +}; + +template +class Insert : public Query +{ +public: + typedef typename TableDefinition::Row Row; + typedef DPL::DB::SqlConnection::RowID RowID; + +protected: + DPL::Optional m_orClause; + Row m_row; + + class PrepareVisitor { + public: + std::string m_columnNames; + std::string m_values; + + template + void Visit(const char* name, const ColumnType&, bool isSet) + { + if ( isSet ) + { + if ( !m_columnNames.empty() ) + { + m_columnNames += ", "; + m_values += ", "; + } + m_columnNames += name; + m_values += "?"; + } + } + }; + + void Prepare() + { + if ( !this->m_command ) + { + this->m_commandString = "INSERT "; + if ( !!m_orClause ) + { + this->m_commandString += " OR " + *m_orClause + " "; + } + this->m_commandString += "INTO "; + this->m_commandString += TableDefinition::GetName(); + + PrepareVisitor visitor; + m_row.VisitColumns(visitor); + + this->m_commandString += " ( " + visitor.m_columnNames + " ) "; + this->m_commandString += "VALUES ( " + visitor.m_values + " )"; + + LogPedantic("Prepared SQL command " << this->m_commandString); + this->m_command = TableDefinition::AllocTableDataCommand( + this->m_commandString.c_str(), + Query::m_interface); + } + } + + class BindVisitor { + private: + DataCommand *m_command; + ArgumentIndex m_bindArgumentIndex; + public: + BindVisitor(DataCommand *command) : + m_command(command), + m_bindArgumentIndex(1) + {} + + template + void Visit(const char*, const ColumnType& value, bool isSet) + { + if ( isSet ) + { + DataCommandUtils::BindArgument(m_command, m_bindArgumentIndex, value); + m_bindArgumentIndex++; + } + } + }; + + void Bind() + { + BindVisitor visitor(this->m_command); + m_row.VisitColumns(visitor); + } + +public: + explicit Insert( + IOrmInterface* interface = NULL, + const DPL::Optional& orClause = DPL::Optional::Null) : + Query(interface), + m_orClause(orClause) + { + } + + void Values(const Row& row) + { + if ( this->m_command ) + { + if ( !row.IsSignatureMatching(m_row) ) + { + ThrowMsg(Exception::SelectReuseWithDifferentQuerySignature, + "Current ORM implementation doesn't allow to reuse Insert instance " + "with different query signature."); + } + } + m_row = row; + } + + RowID Execute() + { + Prepare(); + Bind(); + this->m_command->Step(); + + RowID result = TableDefinition::GetLastInsertRowID( + Query::m_interface); + + this->m_command->Reset(); + return result; + } +}; + +template +class Select : public QueryWithWhereClause +{ +public: + typedef typename TableDefinition::ColumnList ColumnList; + typedef typename TableDefinition::Row Row; + + typedef std::list RowList; +protected: + DPL::Optional m_orderBy; + bool m_distinctResults; + + void Prepare(const char* selectColumnName) + { + if ( !this->m_command ) + { + this->m_commandString = "SELECT "; + if (m_distinctResults) + this->m_commandString += "DISTINCT "; + this->m_commandString += selectColumnName; + this->m_commandString += " FROM "; + this->m_commandString += TableDefinition::GetName(); + + QueryWithWhereClause::Prepare(); + + if ( !m_orderBy.IsNull() ) + { + this->m_commandString += " ORDER BY " + *m_orderBy; + } + + this->m_command = TableDefinition::AllocTableDataCommand( + this->m_commandString.c_str(), + Query::m_interface); + + LogPedantic("Prepared SQL command " << this->m_commandString); + } + } + + void Bind() + { + this->m_bindArgumentIndex = 1; + QueryWithWhereClause::Bind(); + } + + template + ColumnType GetColumn(ColumnIndex columnIndex) + { + return GetColumnFromCommand(columnIndex, this->m_command); + } + + Row GetRow() + { + Row row; + FillRowUtil::FillRow(row, 0, this->m_command); + return row; + } + +public: + + explicit Select(IOrmInterface *interface = NULL) : + QueryWithWhereClause(interface), + m_distinctResults(false) + { + } + + void Distinct() + { + m_distinctResults = true; + } + + void OrderBy(const std::string& orderBy) + { + m_orderBy = orderBy; + } + + template + typename ColumnData::ColumnType GetSingleValue() + { + Prepare(ColumnData::GetColumnName()); + Bind(); + this->m_command->Step(); + + typename ColumnData::ColumnType result = + GetColumn(0); + + this->m_command->Reset(); + return result; + } + + //TODO return range - pair of custom iterators + template + std::list GetValueList() + { + Prepare(ColumnData::GetColumnName()); + Bind(); + + std::list resultList; + + while (this->m_command->Step()) + resultList.push_back(GetColumn(0)); + + this->m_command->Reset(); + return resultList; + } + + Row GetSingleRow() + { + Prepare("*"); + Bind(); + this->m_command->Step(); + + Row result = GetRow(); + + this->m_command->Reset(); + return result; + } + + //TODO return range - pair of custom iterators + RowList GetRowList() + { + Prepare("*"); + Bind(); + + RowList resultList; + + while (this->m_command->Step()) + resultList.push_back(GetRow()); + + this->m_command->Reset(); + return resultList; + } +}; + +template +class Update : public QueryWithWhereClause { +public: + typedef typename TableDefinition::Row Row; + +protected: + DPL::Optional m_orClause; + Row m_row; + + class PrepareVisitor { + public: + std::string m_setExpressions; + + template + void Visit(const char* name, const ColumnType&, bool isSet) + { + if ( isSet ) + { + if ( !m_setExpressions.empty() ) + { + m_setExpressions += ", "; + } + m_setExpressions += name; + m_setExpressions += " = "; + m_setExpressions += "?"; + } + } + }; + + void Prepare() + { + if ( !this->m_command ) + { + this->m_commandString = "UPDATE "; + if ( !!m_orClause ) + { + this->m_commandString += " OR " + *m_orClause + " "; + } + this->m_commandString += TableDefinition::GetName(); + this->m_commandString += " SET "; + + // got through row columns and values + PrepareVisitor visitor; + m_row.VisitColumns(visitor); + + if(visitor.m_setExpressions.empty()) + { + ThrowMsg(Exception::EmptyUpdateStatement, "No SET expressions in update statement"); + } + + this->m_commandString += visitor.m_setExpressions; + + // where + QueryWithWhereClause::Prepare(); + + this->m_command = TableDefinition::AllocTableDataCommand( + this->m_commandString.c_str(), + Query::m_interface); + LogPedantic("Prepared SQL command " << this->m_commandString); + } + } + + class BindVisitor { + private: + DataCommand *m_command; + + public: + ArgumentIndex m_bindArgumentIndex; + + BindVisitor(DataCommand *command) : + m_command(command), + m_bindArgumentIndex(1) + {} + + template + void Visit(const char*, const ColumnType& value, bool isSet) + { + if ( isSet ) + { + DataCommandUtils::BindArgument(m_command, m_bindArgumentIndex, value); + m_bindArgumentIndex++; + } + } + }; + + void Bind() + { + BindVisitor visitor(this->m_command); + m_row.VisitColumns(visitor); + + this->m_bindArgumentIndex = visitor.m_bindArgumentIndex; + QueryWithWhereClause::Bind(); + } + + +public: + explicit Update(IOrmInterface *interface = NULL, + const DPL::Optional& orClause = DPL::Optional::Null) : + QueryWithWhereClause(interface), + m_orClause(orClause) + { + } + + void Values(const Row& row) + { + if ( this->m_command ) + { + if ( !row.IsSignatureMatching(m_row) ) + { + ThrowMsg(Exception::SelectReuseWithDifferentQuerySignature, + "Current ORM implementation doesn't allow to reuse Update instance " + "with different query signature."); + } + } + m_row = row; + } + + void Execute() + { + Prepare(); + Bind(); + this->m_command->Step(); + this->m_command->Reset(); + } +}; + +} //namespace ORM +} //namespace DB +} //namespace DPL + +#endif // DPL_ORM_H diff --git a/modules/db/include/dpl/db/orm_generator.h b/modules/db/include/dpl/db/orm_generator.h new file mode 100644 index 0000000..61ff9ca --- /dev/null +++ b/modules/db/include/dpl/db/orm_generator.h @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file orm_generator.h + * @author Bartosz Janiak (b.janiak@samsung.com) + * @version 1.0 + * @brief Macro definitions for generating the DPL-ORM table definitions from database definitions. + */ + +#ifndef ORM_GENERATOR_DATABASE_NAME +#error You need to define database name in ORM_GENERATOR_DATABASE_NAME define before you include orm_generator.h file +#endif + +#include + +#define ORM_GENERATOR_DATABASE_NAME_LOCAL + +#ifdef DPL_ORM_GENERATOR_H +#warning orm_generator.h is included multiply times. Make sure it has different ORM_GENERATOR_DATABASE_NAME set. +#endif + +#define DPL_ORM_GENERATOR_H + + +#include +#include +#include +#include +#include +#include +#include + +/* + +This is true only when exactly one db is available. + +#if (defined DECLARE_COLUMN) || (defined INT) || (defined TINYINT) || \ + (defined INTEGER) || (defined BIGINT) || defined(VARCHAR) || defined(TEXT) || \ + (defined SQL) || (defined TABLE_CONSTRAINTS) || (defined OPTIONAL) || \ + (defined DATABASE_START) || (defined DATABASE_END) || (defined CREATE_TABLE) || \ + (defined COLUMN) || (defined COLUMN_NOT_NULL) || (defined CREATE_TABLE_END) + +#error This file temporarily defines many macros with generic names. To avoid name clash please include \ + this file as early as possible. If this is not possible please report this problem to DPL developers. + +#endif +*/ + +namespace DPL { +namespace DB { +namespace ORM { + +// Global macros + +#define STRINGIFY(s) _str(s) +#define _str(s) #s +#define DECLARE_COLUMN(FIELD, TYPE) \ + struct FIELD { \ + typedef TYPE ColumnType; \ + static const char* GetColumnName() { return STRINGIFY(FIELD); } \ + static void SetRowField(Row& row, const TYPE& value) { row.Set_##FIELD(value);} \ + }; + +#define INT int +#define TINYINT int +#define INTEGER int //TODO: should be long long? +#define BIGINT int //TODO: should be long long? +#define VARCHAR(x) DPL::String +#define TEXT DPL::String + +#define SQL(args...) +#define TABLE_CONSTRAINTS(args...) +#define OPTIONAL(type) DPL::Optional< type > +#define DATABASE_START(db_name) \ + namespace db_name \ + { \ + class ScopedTransaction \ + { \ + bool m_commited; \ + IOrmInterface *m_interface; \ + \ + public: \ + ScopedTransaction(IOrmInterface *interface) : \ + m_commited(false), \ + m_interface(interface) \ + { \ + Assert(interface != NULL); \ + m_interface->TransactionBegin(); \ + } \ + \ + ~ScopedTransaction() \ + { \ + if (!m_commited) \ + m_interface->TransactionRollback(); \ + } \ + \ + void Commit() \ + { \ + m_interface->TransactionCommit(); \ + m_commited = true; \ + } \ + }; + +#define DATABASE_END() } + +// RowBase ostream operator<< declaration + +#define CREATE_TABLE(name) \ + namespace name { \ + class RowBase; \ + inline std::ostream& operator<<(std::ostream& ostr, const RowBase& row); \ + } +#define COLUMN_NOT_NULL(name, type, args...) +#define COLUMN(name, type, args...) +#define CREATE_TABLE_END() + +#include ORM_GENERATOR_DATABASE_NAME_LOCAL + +#undef CREATE_TABLE +#undef COLUMN_NOT_NULL +#undef COLUMN +#undef CREATE_TABLE_END + +#undef DATABASE_START +#define DATABASE_START(db_name) namespace db_name { + +// RowBase class + +#define CREATE_TABLE(name) namespace name { class RowBase { \ + public: friend std::ostream& operator<<(std::ostream&, const RowBase&); +#define COLUMN_NOT_NULL(name, type, args...) \ + protected: type name; bool m_##name##_set; \ + public: void Set_##name(const type& value) { \ + m_##name##_set = true; \ + this->name = value; \ + } \ + public: type Get_##name() const { \ + if ( !m_##name##_set ) { \ + ThrowMsg(Exception::RowFieldNotInitialized, \ + "You tried to read a row field that hasn't been set yet."); \ + } \ + return name; \ + } + +#define COLUMN(name, type, args...) \ + protected: OPTIONAL(type) name; bool m_##name##_set; \ + public: void Set_##name(const OPTIONAL(type)& value) { \ + m_##name##_set = true; \ + this->name = value; \ + } \ + public: OPTIONAL(type) Get_##name() const { \ + if ( !m_##name##_set ) { \ + ThrowMsg(Exception::RowFieldNotInitialized, \ + "You tried to read a row field that hasn't been set yet."); \ + } \ + return name; \ + } +#define CREATE_TABLE_END() }; } + +#include ORM_GENERATOR_DATABASE_NAME_LOCAL + +#undef CREATE_TABLE +#undef COLUMN_NOT_NULL +#undef COLUMN +#undef CREATE_TABLE_END + +// RowBase ostream operator<< + +#define CREATE_TABLE(name) std::ostream& name::operator<<(std::ostream& ostr, const RowBase& row) { using ::operator<< ; ostr << STRINGIFY(name) << " ("; +#define COLUMN_NOT_NULL(name, type, args...) ostr << " '" << row.name << "'" ; +#define COLUMN(name, type, args...) ostr << " '" << row.name << "'" ; +#define CREATE_TABLE_END() ostr << " )" ; return ostr; } + +#include ORM_GENERATOR_DATABASE_NAME_LOCAL + +#undef CREATE_TABLE +#undef COLUMN_NOT_NULL +#undef COLUMN +#undef CREATE_TABLE_END + +// RowBase2 class (== RowBase + operator==) + +#define CREATE_TABLE(name) namespace name { class RowBase2 : public RowBase { \ + public: bool operator==(const RowBase2& row) const { return true +#define COLUMN_NOT_NULL(name, type, args...) && (this->name == row.name) +#define COLUMN(name, type, args...) && (this->name == row.name) +#define CREATE_TABLE_END() ; } }; } + +#include ORM_GENERATOR_DATABASE_NAME_LOCAL + +#undef CREATE_TABLE +#undef COLUMN_NOT_NULL +#undef COLUMN +#undef CREATE_TABLE_END + +// RowBase3 class (== RowBase2 + operator<) + +#define CREATE_TABLE(name) namespace name { class RowBase3 : public RowBase2 { \ + public: bool operator<(const RowBase3& row) const { +#define COLUMN_NOT_NULL(name, type, args...) if (this->name < row.name) { return true; } if (this->name > row.name) { return false; } +#define COLUMN(name, type, args...) if (this->name < row.name) { return true; } if (this->name > row.name) { return false; } +#define CREATE_TABLE_END() return false; } }; } + +#include ORM_GENERATOR_DATABASE_NAME_LOCAL + +#undef CREATE_TABLE +#undef COLUMN_NOT_NULL +#undef COLUMN +#undef CREATE_TABLE_END + +// RowBase4 class (== RowBase3 + IsSignatureMatching ) + +#define CREATE_TABLE(name) namespace name { class RowBase4 : public RowBase3 { \ + public: bool IsSignatureMatching(const RowBase4& row) const { return true +#define COLUMN_NOT_NULL(name, type, args...) && (this->m_##name##_set == row.m_##name##_set) +#define COLUMN(name, type, args...) && (this->m_##name##_set == row.m_##name##_set) +#define CREATE_TABLE_END() ; } }; } + +#include ORM_GENERATOR_DATABASE_NAME_LOCAL + +#undef CREATE_TABLE +#undef COLUMN_NOT_NULL +#undef COLUMN +#undef CREATE_TABLE_END + +// RowBase5 class (== RowBase4 + default constructor) + +#define CREATE_TABLE(name) namespace name { class RowBase5 : public RowBase4 { \ + public: RowBase5() { +#define COLUMN_NOT_NULL(name, type, args...) m_##name##_set = false; +#define COLUMN(name, type, args...) m_##name##_set = false; +#define CREATE_TABLE_END() } }; } + +#include ORM_GENERATOR_DATABASE_NAME_LOCAL + +#undef CREATE_TABLE +#undef COLUMN_NOT_NULL +#undef COLUMN +#undef CREATE_TABLE_END + +// Row class (== RowBase5 + ForEachColumn ) + +#define CREATE_TABLE(name) namespace name { class Row : public RowBase5 { \ + public: template \ + void VisitColumns(Visitor& visitor) const { +#define COLUMN_NOT_NULL(name, type, args...) visitor.Visit(STRINGIFY(name), this->name, this->m_##name##_set); +#define COLUMN(name, type, args...) visitor.Visit(STRINGIFY(name), this->name, this->m_##name##_set); +#define CREATE_TABLE_END() } }; } + +#include ORM_GENERATOR_DATABASE_NAME_LOCAL + +#undef CREATE_TABLE +#undef COLUMN_NOT_NULL +#undef COLUMN +#undef CREATE_TABLE_END + +// Field structure declarations + +#define CREATE_TABLE(name) namespace name { +#define COLUMN_NOT_NULL(name, type, args...) DECLARE_COLUMN(name, type) +#define COLUMN(name, type, args...) DECLARE_COLUMN(name, OPTIONAL(type)) +#define CREATE_TABLE_END() } + +#include ORM_GENERATOR_DATABASE_NAME_LOCAL + +#undef CREATE_TABLE +#undef COLUMN_NOT_NULL +#undef COLUMN +#undef CREATE_TABLE_END + +// ColumnList typedef + +#define CREATE_TABLE(name) namespace name { typedef DPL::TypeListDecl< +#define COLUMN_NOT_NULL(name, type, args...) name, +#define COLUMN(name, type, args...) name, +#define CREATE_TABLE_END() DPL::TypeListGuard>::Type ColumnList; } + +#include ORM_GENERATOR_DATABASE_NAME_LOCAL + +#undef CREATE_TABLE +#undef COLUMN_NOT_NULL +#undef COLUMN +#undef CREATE_TABLE_END + +// TableDefinition struct + +#define CREATE_TABLE(table_name) \ + namespace table_name { \ + struct TableDefinition { \ + typedef table_name::ColumnList ColumnList; \ + typedef table_name::Row Row; \ + static const char* GetName() { return STRINGIFY(table_name); } \ + static DPL::DB::SqlConnection::DataCommand *AllocTableDataCommand( \ + const std::string &statement, \ + IOrmInterface *interface) \ + { \ + Assert(interface != NULL); \ + return interface->AllocDataCommand(statement); \ + } \ + static void FreeTableDataCommand( \ + DPL::DB::SqlConnection::DataCommand *command, \ + IOrmInterface *interface) \ + { \ + Assert(interface != NULL); \ + interface->FreeDataCommand(command); \ + } \ + static DPL::DB::SqlConnection::RowID GetLastInsertRowID( \ + IOrmInterface *interface) \ + { \ + Assert(interface != NULL); \ + return interface->GetLastInsertRowID(); \ + } \ + }; \ + } + +#define COLUMN_NOT_NULL(name, type, args...) +#define COLUMN(name, type, args...) +#define CREATE_TABLE_END() + +#include ORM_GENERATOR_DATABASE_NAME_LOCAL + +#undef CREATE_TABLE +#undef COLUMN_NOT_NULL +#undef COLUMN +#undef CREATE_TABLE_END + +// Query typedefs + +#define CREATE_TABLE(name) \ + namespace name { \ + typedef Select Select; \ + typedef Insert Insert; \ + typedef Delete Delete; \ + typedef Update Update; \ + } +#define COLUMN_NOT_NULL(name, type, args...) +#define COLUMN(name, type, args...) +#define CREATE_TABLE_END() + +#include ORM_GENERATOR_DATABASE_NAME_LOCAL + +#undef CREATE_TABLE +#undef COLUMN_NOT_NULL +#undef COLUMN +#undef CREATE_TABLE_END + + +// Global undefs +#undef INT +#undef TINYINT +#undef INTEGER +#undef BIGINT +#undef VARCHAR +#undef TEXT + +#undef SQL +#undef TABLE_CONSTRAINTS +#undef OPTIONAL +#undef DATABASE_START +#undef DATABASE_END + +} //namespace ORM +} //namespace DB +} //namespace DPL + +#undef ORM_GENERATOR_DATABASE_NAME +#undef ORM_GENERATOR_DATABASE_NAME_LOCAL diff --git a/modules/db/include/dpl/db/orm_interface.h b/modules/db/include/dpl/db/orm_interface.h new file mode 100644 index 0000000..74908fd --- /dev/null +++ b/modules/db/include/dpl/db/orm_interface.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file orm_interface.h + * @author Lukasz Marek (l.marek@samsung.com) + * @version 1.0 + */ + +#include +#include + +#ifndef DPL_ORM_INTERFACE_H +#define DPL_ORM_INTERFACE_H + +namespace DPL +{ +namespace DB +{ +namespace ORM +{ + +class IOrmInterface +{ + public: + virtual ~IOrmInterface() {} + virtual DPL::DB::SqlConnection::DataCommand *AllocDataCommand(const std::string &statement) = 0; + virtual void FreeDataCommand(DPL::DB::SqlConnection::DataCommand *command) = 0; + virtual void TransactionBegin() = 0; + virtual void TransactionCommit() = 0; + virtual void TransactionRollback() = 0; + virtual DPL::DB::SqlConnection::RowID GetLastInsertRowID() = 0; +}; + +} +} +} + +#endif diff --git a/modules/db/include/dpl/db/orm_macros.h b/modules/db/include/dpl/db/orm_macros.h new file mode 100644 index 0000000..804f59a --- /dev/null +++ b/modules/db/include/dpl/db/orm_macros.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file orm_macros.h + * @author Bartosz Janiak (b.janiak@samsung.com) + * @version 1.0 + * @brief Macro definitions for generating the SQL input file from database definition. + */ + +//Do not include this file directly! It is used only for SQL code generation. + +#define CREATE_TABLE(name) CREATE TABLE name ( +#define COLUMN(name, type, args...) name type args , +#define COLUMN_NOT_NULL(name, type, args...) name type args not null, +#define SQL(args...) args +#define TABLE_CONSTRAINTS(args...) args , +#define CREATE_TABLE_END() CHECK(1) ); +#define DATABASE_START(db_name) +#define DATABASE_END() + diff --git a/modules/db/include/dpl/db/sql_connection.h b/modules/db/include/dpl/db/sql_connection.h new file mode 100644 index 0000000..1d032ee --- /dev/null +++ b/modules/db/include/dpl/db/sql_connection.h @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file sql_connection.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of SQL connection + */ +#ifndef DPL_SQL_CONNECTION_H +#define DPL_SQL_CONNECTION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace DB +{ + +/** + * SQL connection class + */ +class SqlConnection +{ +public: + /** + * SQL Exception classes + */ + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, SyntaxError) + DECLARE_EXCEPTION_TYPE(Base, ConnectionBroken) + DECLARE_EXCEPTION_TYPE(Base, InternalError) + DECLARE_EXCEPTION_TYPE(Base, InvalidColumn) + }; + + typedef int ColumnIndex; + typedef int ArgumentIndex; + + /* + * SQL processed data command + */ + class DataCommand + : private Noncopyable + { + private: + SqlConnection *m_masterConnection; + sqlite3_stmt *m_stmt; + + void CheckBindResult(int result); + void CheckColumnIndex(SqlConnection::ColumnIndex column); + + DataCommand(SqlConnection *connection, const char *buffer); + + friend class SqlConnection; + + public: + virtual ~DataCommand(); + + /** + * Bind null to the prepared statement argument + * + * @param position Index of argument to bind value to + */ + void BindNull(ArgumentIndex position); + + /** + * Bind int to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInteger(ArgumentIndex position, int value); + + /** + * Bind int8_t to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt8(ArgumentIndex position, int8_t value); + + /** + * Bind int16 to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt16(ArgumentIndex position, int16_t value); + + /** + * Bind int32 to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt32(ArgumentIndex position, int32_t value); + + /** + * Bind int64 to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt64(ArgumentIndex position, int64_t value); + + /** + * Bind float to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindFloat(ArgumentIndex position, float value); + + /** + * Bind double to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindDouble(ArgumentIndex position, double value); + + /** + * Bind string to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindString(ArgumentIndex position, const char *value); + + /** + * Bind string to the prepared statement argument + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindString(ArgumentIndex position, const String& value); + + /** + * Bind optional int to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInteger(ArgumentIndex position, const Optional &value); + + /** + * Bind optional int8 to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt8(ArgumentIndex position, const Optional &value); + + /** + * Bind optional int16 to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt16(ArgumentIndex position, const Optional &value); + + /** + * Bind optional int32 to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt32(ArgumentIndex position, const Optional &value); + + /** + * Bind optional int64 to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindInt64(ArgumentIndex position, const Optional &value); + + /** + * Bind optional float to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindFloat(ArgumentIndex position, const Optional &value); + + /** + * Bind optional double to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindDouble(ArgumentIndex position, const Optional &value); + + /** + * Bind optional string to the prepared statement argument. + * If optional is not set null will be bound + * + * @param position Index of argument to bind value to + * @param value Value to bind + */ + void BindString(ArgumentIndex position, const Optional &value); + + /** + * Execute the prepared statement and/or move + * to the next row of the result + * + * @return True when there was a row returned + */ + bool Step(); + + /** + * Reset prepared statement's arguments + * All parameters will become null + */ + void Reset(); + + /** + * Checks whether column value is null + * + * @throw Exception::InvalidColumn + */ + bool IsColumnNull(ColumnIndex column); + + /** + * Get integer value from column in current row. + * + * @throw Exception::InvalidColumn + */ + int GetColumnInteger(ColumnIndex column); + + /** + * Get int8 value from column in current row. + * + * @throw Exception::InvalidColumn + */ + int8_t GetColumnInt8(ColumnIndex column); + + /** + * Get int16 value from column in current row. + * + * @throw Exception::InvalidColumn + */ + int16_t GetColumnInt16(ColumnIndex column); + /** + * Get int32 value from column in current row. + * + * @throw Exception::InvalidColumn + */ + int32_t GetColumnInt32(ColumnIndex column); + + /** + * Get int64 value from column in current row. + * + * @throw Exception::InvalidColumn + */ + int64_t GetColumnInt64(ColumnIndex column); + + /** + * Get float value from column in current row. + * + * @throw Exception::InvalidColumn + */ + float GetColumnFloat(ColumnIndex column); + + /** + * Get double value from column in current row. + * + * @throw Exception::InvalidColumn + */ + double GetColumnDouble(ColumnIndex column); + + /** + * Get string value from column in current row. + * + * @throw Exception::InvalidColumn + */ + std::string GetColumnString(ColumnIndex column); + + /** + * Get optional integer value from column in current row. + * + * @throw Exception::InvalidColumn + */ + Optional GetColumnOptionalInteger(ColumnIndex column); + + /** + * Get optional int8 value from column in current row. + * + * @throw Exception::InvalidColumn + */ + Optional GetColumnOptionalInt8(ColumnIndex column); + + /** + * Get optional int16value from column in current row. + * + * @throw Exception::InvalidColumn + */ + Optional GetColumnOptionalInt16(ColumnIndex column); + + /** + * Get optional int32 value from column in current row. + * + * @throw Exception::InvalidColumn + */ + Optional GetColumnOptionalInt32(ColumnIndex column); + + /** + * Get optional int64 value from column in current row. + * + * @throw Exception::InvalidColumn + */ + Optional GetColumnOptionalInt64(ColumnIndex column); + + /** + * Get optional float value from column in current row. + * + * @throw Exception::InvalidColumn + */ + Optional GetColumnOptionalFloat(ColumnIndex column); + + /** + * Get optional double value from column in current row. + * + * @throw Exception::InvalidColumn + */ + Optional GetColumnOptionalDouble(ColumnIndex column); + + /** + * Get optional string value from column in current row. + * + * @throw Exception::InvalidColumn + */ + Optional GetColumnOptionalString(ColumnIndex column); + }; + + // Move on copy semantics + typedef std::auto_ptr DataCommandAutoPtr; + + // Open flags + class Flag + { + public: + enum Type + { + None = 1<<0, + UseLucene = 1<<1 + }; + }; + + // RowID + typedef sqlite3_int64 RowID; + + /** + * Synchronization object used to synchronize SQL connection + * to the same database across different threads and processes + */ + class SynchronizationObject + { + public: + virtual ~SynchronizationObject() {} + + /** + * Synchronizes SQL connection for multiple clients. + */ + virtual void Synchronize() = 0; + + /** + * Notify all waiting clients that the connection is no longer locked. + */ + virtual void NotifyAll() = 0; + }; + +protected: + sqlite3 *m_connection; + + // Options + bool m_usingLucene; + + // Stored data procedures + int m_dataCommandsCount; + + // Synchronization object + ScopedPtr m_synchronizationObject; + + virtual void Connect(const std::string &address, Flag::Type = Flag::None); + virtual void Disconnect(); + + void TurnOnForeignKeys(); + + static SynchronizationObject *AllocDefaultSynchronizationObject(); + +public: + /** + * Open SQL connection + * + * Synchronization is archieved by using provided asynchronization object. + * If synchronizationObject is set to NULL, so synchronization is performed. + * Ownership of the synchronization object is transfered to sql connection + * object. + * + * @param address Database file name + * @param flags Open flags + * @param synchronizationObject A synchronization object to use. + */ + explicit SqlConnection(const std::string &address = std::string(), + Flag::Type flags = Flag::None, + SynchronizationObject *synchronizationObject = + AllocDefaultSynchronizationObject()); + + /** + * Destructor + */ + virtual ~SqlConnection(); + + /** + * Execute SQL command without result + * + * @param format + * @param ... + */ + void ExecCommand(const char *format, ...); + + /** + * Prepare stored procedure + * + * @param format SQL statement + * @return Data command representing stored procedure + */ + DataCommandAutoPtr PrepareDataCommand(const char *format, ...); + + /** + * Check whether given table exists + * + * @param tableName Name of the table to check + * @return True if given table name exists + */ + bool CheckTableExist(const char *tableName); + + /** + * Get last insert operation new row id + * + * @return Row ID + */ + RowID GetLastInsertRowID() const; +}; + +} // namespace DB +} // namespace DPL + +#endif // DPL_SQL_CONNECTION_H diff --git a/modules/db/include/dpl/db/thread_database_support.h b/modules/db/include/dpl/db/thread_database_support.h new file mode 100644 index 0000000..2f45f0f --- /dev/null +++ b/modules/db/include/dpl/db/thread_database_support.h @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file thread_database_support.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk) + * @version 1.0 + * @brief This file contains the declaration of thread database support + */ + +#ifndef DPL_THREAD_DATABASE_SUPPORT_H +#define DPL_THREAD_DATABASE_SUPPORT_H + +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace DB +{ + +/** + * Thread database support + * + * Associate database connection with thread lifecycle + * + */ + +class ThreadDatabaseSupport : + public DPL::DB::ORM::IOrmInterface +{ + private: + typedef DPL::DB::SqlConnection *SqlConnectionPtr; + typedef DPL::ThreadLocalVariable TLVSqlConnectionPtr; + typedef DPL::ThreadLocalVariable TLVSizeT; + typedef DPL::ThreadLocalVariable TLVBool; + + TLVSqlConnectionPtr m_connection; + TLVBool m_linger; + TLVSizeT m_refCounter; + TLVSizeT m_transactionDepth; + TLVSizeT m_attachCount; + TLVBool m_transactionCancel; + std::string m_address; + DPL::DB::SqlConnection::Flag::Type m_flags; + + TLVSqlConnectionPtr &Connection() + { + return m_connection; + } + + TLVBool &Linger() + { + return m_linger; + } + + TLVSizeT &RefCounter() + { + return m_refCounter; + } + + TLVSizeT &TransactionDepth() + { + return m_transactionDepth; + } + + TLVSizeT &AttachCount() + { + return m_attachCount; + } + + TLVBool &TransactionCancel() + { + return m_transactionCancel; + } + + void CheckedConnectionDelete() + { + Assert(!Connection().IsNull()); + Assert(*Linger() == true); + + if (*RefCounter() > 0 || *AttachCount() > 0) { + return; + } + + // Destroy connection + LogInfo("Destroying thread database connection: " << m_address); + + delete *Connection(); + + // Blocking destroy + Connection().GuardValue(false); + Linger().GuardValue(false); + RefCounter().GuardValue(false); + TransactionCancel().GuardValue(false); + TransactionDepth().GuardValue(false); + AttachCount().GuardValue(false); + + Connection().Reset(); + Linger().Reset(); + RefCounter().Reset(); + TransactionCancel().Reset(); + TransactionDepth().Reset(); + AttachCount().Reset(); + } + + void TransactionUnref() + { + LogPedantic("Unref transaction"); + + if (--(*TransactionDepth()) == 0) { + LogPedantic("Transaction is finalized"); + + if (*TransactionCancel()) { + LogPedantic("Transaction will be rolled back"); + (*Connection())->ExecCommand("ROLLBACK;"); + } else { + LogPedantic("Transaction will be commited"); + (*Connection())->ExecCommand("COMMIT;"); + } + } + } + + public: + ThreadDatabaseSupport(const std::string &address, + DPL::DB::SqlConnection::Flag::Type flags) : + m_address(address), + m_flags(flags) + { + } + + virtual ~ThreadDatabaseSupport() + { + } + + void AttachToThread() + { + Linger() = false; + + if (!Connection().IsNull()) { + // Add reference + ++*AttachCount(); + return; + } + + // Initialize SQL connection described in traits + LogInfo("Attaching thread database connection: " << m_address); + + Connection() = new DPL::DB::SqlConnection(m_address.c_str(), m_flags); + + RefCounter() = 0; + + AttachCount() = 1; + + //Init Transaction related variables + TransactionDepth() = 0; + TransactionCancel() = false; + + // Blocking destroy + Connection().GuardValue(true); + Linger().GuardValue(true); + RefCounter().GuardValue(true); + TransactionDepth().GuardValue(true); + AttachCount().GuardValue(true); + TransactionCancel().GuardValue(true); + } + + void DetachFromThread() + { + // Calling thread must support thread database connections + Assert(!Connection().IsNull()); + + // Remove reference + --*AttachCount(); + + if (*AttachCount() > 0) { + return; + } + + // It must not be in linger state yet + Assert(*Linger() == false); + + LogInfo("Detaching thread database connection: " << m_address); + + // Enter linger state + *Linger() = true; + + // Checked delete + CheckedConnectionDelete(); + } + + bool IsAttached() + { + return !AttachCount().IsNull() && *AttachCount() > 0; + } + + DPL::DB::SqlConnection::DataCommand *AllocDataCommand( + const std::string &statement) + { + // Calling thread must support thread database connections + Assert(!Connection().IsNull()); + + // Calling thread must not be in linger state + Assert(*Linger() == false); + + // Add reference + ++*RefCounter(); + + // Create new unmanaged data command + return (*Connection())->PrepareDataCommand(statement.c_str()).release(); + } + + void FreeDataCommand(DPL::DB::SqlConnection::DataCommand *command) + { + // Calling thread must support thread database connections + Assert(!Connection().IsNull()); + + // Delete data command + delete command; + + // Unreference SQL connection + --*RefCounter(); + + // If it is linger state, connection may be destroyed + if (*Linger() == true) { + CheckedConnectionDelete(); + } + } + + void TransactionBegin() + { + // Calling thread must support thread database connections + Assert(!Connection().IsNull()); + + LogPedantic("Begin transaction"); + + // Addref transaction + if (++(*TransactionDepth()) == 1) { + LogPedantic("Transaction is initialized"); + + TransactionCancel() = false; + (*Connection())->ExecCommand("BEGIN;"); + } + } + + void TransactionCommit() + { + // Calling thread must support thread database connections + Assert(!Connection().IsNull()); + + LogPedantic("Commit transaction"); + + // Unref transation + TransactionUnref(); + } + + void TransactionRollback() + { + // Calling thread must support thread database connections + Assert(!Connection().IsNull()); + + // Cancel and unref transaction + TransactionCancel() = true; + TransactionUnref(); + } + + DPL::DB::SqlConnection::RowID GetLastInsertRowID() + { + // Calling thread must support thread database connections + Assert(!Connection().IsNull()); + + return (*Connection())->GetLastInsertRowID(); + } + + bool CheckTableExist(const char *name) + { + // Calling thread must support thread database connections + Assert(!Connection().IsNull()); + + return (*Connection())->CheckTableExist(name); + } +}; + +} +} + +#endif // DPL_THREAD_DATABASE_SUPPORT_H diff --git a/modules/db/src/naive_synchronization_object.cpp b/modules/db/src/naive_synchronization_object.cpp new file mode 100644 index 0000000..198a2b6 --- /dev/null +++ b/modules/db/src/naive_synchronization_object.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file naive_synchronization_object.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of SQL naive synchronization object + */ +#include +#include + +namespace DPL +{ +namespace DB +{ + +void NaiveSynchronizationObject::Synchronize() +{ + // Sleep for about 10ms - 30ms + Thread::MiliSleep(10 + rand() % 20); +} + +void NaiveSynchronizationObject::NotifyAll() +{ + // No need to inform about anything +} + +} // namespace DB +} // namespace DPL diff --git a/modules/db/src/orm.cpp b/modules/db/src/orm.cpp new file mode 100644 index 0000000..5c7c821 --- /dev/null +++ b/modules/db/src/orm.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file orm.cpp + * @author Bartosz Janiak (b.janiak@samsung.com) + * @version 1.0 + * @brief Static definitions and function template specialziations of DPL-ORM. + */ + +#include + +namespace DPL { +namespace DB { +namespace ORM { + +namespace RelationTypes { +const char Equal[] = "="; +const char LessThan[] = "<"; +const char And[] = "AND"; +const char Or[] = "OR"; +const char Is[] = "IS"; +} + +template<> +int GetColumnFromCommand(ColumnIndex columnIndex, + DataCommand *command) +{ + return command->GetColumnInteger(columnIndex); +} + +template<> +DPL::String GetColumnFromCommand(ColumnIndex columnIndex, + DataCommand *command) +{ + return DPL::FromUTF8String(command->GetColumnString(columnIndex)); +} + +template<> +OptionalInteger GetColumnFromCommand(ColumnIndex columnIndex, + DataCommand *command) +{ + return command->GetColumnOptionalInteger(columnIndex); +} + +template<> +OptionalString GetColumnFromCommand(ColumnIndex columnIndex, + DataCommand *command) +{ + return command->GetColumnOptionalString(columnIndex); +} + +template<> +double GetColumnFromCommand(ColumnIndex columnIndex, + DataCommand *command) +{ + return command->GetColumnDouble(columnIndex); +} + +void DataCommandUtils::BindArgument(DataCommand *command, + ArgumentIndex index, + int argument) +{ + command->BindInteger(index, argument); +} + +void DataCommandUtils::BindArgument(DataCommand *command, + ArgumentIndex index, + const OptionalInteger& argument) +{ + command->BindInteger(index, argument); +} + +void DataCommandUtils::BindArgument(DataCommand *command, + ArgumentIndex index, + const DPL::String& argument) +{ + command->BindString(index, argument); +} + +void DataCommandUtils::BindArgument(DataCommand *command, + ArgumentIndex index, + const OptionalString& argument) +{ + command->BindString(index, argument); +} + +} +} +} \ No newline at end of file diff --git a/modules/db/src/sql_connection.cpp b/modules/db/src/sql_connection.cpp new file mode 100644 index 0000000..5a7fd4d --- /dev/null +++ b/modules/db/src/sql_connection.cpp @@ -0,0 +1,862 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file sql_connection.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of SQL connection + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace DB +{ + +namespace // anonymous +{ +class ScopedNotifyAll + : public Noncopyable +{ +private: + SqlConnection::SynchronizationObject *m_synchronizationObject; + +public: + explicit ScopedNotifyAll( + SqlConnection::SynchronizationObject *synchronizationObject) + : m_synchronizationObject(synchronizationObject) + { + } + + ~ScopedNotifyAll() + { + if (!m_synchronizationObject) + return; + + LogPedantic("Notifying after successful synchronize"); + m_synchronizationObject->NotifyAll(); + } +}; +} // namespace anonymous + +SqlConnection::DataCommand::DataCommand(SqlConnection *connection, + const char *buffer) + : m_masterConnection(connection), + m_stmt(NULL) +{ + Assert(connection != NULL); + + // Notify all after potentially synchronized database connection access + ScopedNotifyAll notifyAll(connection->m_synchronizationObject.Get()); + + for (;;) + { + int ret = sqlite3_prepare_v2(connection->m_connection, + buffer, strlen(buffer), + &m_stmt, NULL); + + if (ret == SQLITE_OK) + { + LogPedantic("Data command prepared successfuly"); + break; + } + else if (ret == SQLITE_BUSY) + { + LogPedantic("Collision occurred while preparing SQL command"); + + // Synchronize if synchronization object is available + if (connection->m_synchronizationObject) + { + LogPedantic("Performing synchronization"); + connection->m_synchronizationObject->Synchronize(); + continue; + } + + // No synchronization object defined. Fail. + } + + // Fatal error + const char *error = sqlite3_errmsg(m_masterConnection->m_connection); + + LogPedantic("SQL prepare data command failed"); + LogPedantic(" Statement: " << buffer); + LogPedantic(" Error: " << error); + + ThrowMsg(Exception::SyntaxError, error); + } + + LogPedantic("Prepared data command: " << buffer); + + // Increment stored data command count + ++m_masterConnection->m_dataCommandsCount; +} + +SqlConnection::DataCommand::~DataCommand() +{ + LogPedantic("SQL data command finalizing"); + + if (sqlite3_finalize(m_stmt) != SQLITE_OK) + LogPedantic("Failed to finalize data command"); + + // Decrement stored data command count + --m_masterConnection->m_dataCommandsCount; +} + +void SqlConnection::DataCommand::CheckBindResult(int result) +{ + if (result != SQLITE_OK) + { + const char *error = sqlite3_errmsg( + m_masterConnection->m_connection); + + LogPedantic("Failed to bind SQL statement parameter"); + LogPedantic(" Error: " << error); + + ThrowMsg(Exception::SyntaxError, error); + } +} + +void SqlConnection::DataCommand::BindNull( + SqlConnection::ArgumentIndex position) +{ + CheckBindResult(sqlite3_bind_null(m_stmt, position)); + LogPedantic("SQL data command bind null: [" + << position << "]"); +} + +void SqlConnection::DataCommand::BindInteger( + SqlConnection::ArgumentIndex position, + int value) +{ + CheckBindResult(sqlite3_bind_int(m_stmt, position, value)); + LogPedantic("SQL data command bind integer: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindInt8( + SqlConnection::ArgumentIndex position, + int8_t value) +{ + CheckBindResult(sqlite3_bind_int(m_stmt, position, + static_cast(value))); + LogPedantic("SQL data command bind int8: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindInt16( + SqlConnection::ArgumentIndex position, + int16_t value) +{ + CheckBindResult(sqlite3_bind_int(m_stmt, position, + static_cast(value))); + LogPedantic("SQL data command bind int16: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindInt32( + SqlConnection::ArgumentIndex position, + int32_t value) +{ + CheckBindResult(sqlite3_bind_int(m_stmt, position, + static_cast(value))); + LogPedantic("SQL data command bind int32: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindInt64( + SqlConnection::ArgumentIndex position, + int64_t value) +{ + CheckBindResult(sqlite3_bind_int64(m_stmt, position, + static_cast(value))); + LogPedantic("SQL data command bind int64: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindFloat( + SqlConnection::ArgumentIndex position, + float value) +{ + CheckBindResult(sqlite3_bind_double(m_stmt, position, + static_cast(value))); + LogPedantic("SQL data command bind float: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindDouble( + SqlConnection::ArgumentIndex position, + double value) +{ + CheckBindResult(sqlite3_bind_double(m_stmt, position, value)); + LogPedantic("SQL data command bind double: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindString( + SqlConnection::ArgumentIndex position, + const char *value) +{ + if (!value) + { + BindNull(position); + return; + } + + // Assume that text may disappear + CheckBindResult(sqlite3_bind_text(m_stmt, position, + value, strlen(value), + SQLITE_TRANSIENT)); + + LogPedantic("SQL data command bind string: [" + << position << "] -> " << value); +} + +void SqlConnection::DataCommand::BindString( + SqlConnection::ArgumentIndex position, + const String &value) +{ + BindString(position, ToUTF8String(value).c_str()); +} + +void SqlConnection::DataCommand::BindInteger( + SqlConnection::ArgumentIndex position, + const Optional &value) +{ + if (value.IsNull()) + BindNull(position); + else + BindInteger(position, *value); +} + +void SqlConnection::DataCommand::BindInt8( + SqlConnection::ArgumentIndex position, + const Optional &value) +{ + if (value.IsNull()) + BindNull(position); + else + BindInt8(position, *value); +} + +void SqlConnection::DataCommand::BindInt16( + SqlConnection::ArgumentIndex position, + const Optional &value) +{ + if (value.IsNull()) + BindNull(position); + else + BindInt16(position, *value); +} + +void SqlConnection::DataCommand::BindInt32( + SqlConnection::ArgumentIndex position, + const Optional &value) +{ + if (value.IsNull()) + BindNull(position); + else + BindInt32(position, *value); +} + +void SqlConnection::DataCommand::BindInt64( + SqlConnection::ArgumentIndex position, + const Optional &value) +{ + if (value.IsNull()) + BindNull(position); + else + BindInt64(position, *value); +} + +void SqlConnection::DataCommand::BindFloat( + SqlConnection::ArgumentIndex position, + const Optional &value) +{ + if (value.IsNull()) + BindNull(position); + else + BindFloat(position, *value); +} + +void SqlConnection::DataCommand::BindDouble( + SqlConnection::ArgumentIndex position, + const Optional &value) +{ + if (value.IsNull()) + BindNull(position); + else + BindDouble(position, *value); +} + +void SqlConnection::DataCommand::BindString( + SqlConnection::ArgumentIndex position, + const Optional &value) +{ + if (!!value) + BindString(position, ToUTF8String(*value).c_str()); + else + BindNull(position); +} + +bool SqlConnection::DataCommand::Step() +{ + // Notify all after potentially synchronized database connection access + ScopedNotifyAll notifyAll( + m_masterConnection->m_synchronizationObject.Get()); + + for (;;) + { + int ret = sqlite3_step(m_stmt); + + if (ret == SQLITE_ROW) + { + LogPedantic("SQL data command step ROW"); + return true; + } + else if (ret == SQLITE_DONE) + { + LogPedantic("SQL data command step DONE"); + return false; + } + else if (ret == SQLITE_BUSY) + { + LogPedantic("Collision occurred while executing SQL command"); + + // Synchronize if synchronization object is available + if (m_masterConnection->m_synchronizationObject) + { + LogPedantic("Performing synchronization"); + + m_masterConnection-> + m_synchronizationObject->Synchronize(); + + continue; + } + + // No synchronization object defined. Fail. + } + + // Fatal error + const char *error = sqlite3_errmsg(m_masterConnection->m_connection); + + LogPedantic("SQL step data command failed"); + LogPedantic(" Error: " << error); + + ThrowMsg(Exception::InternalError, error); + } +} + +void SqlConnection::DataCommand::Reset() +{ + /* + * According to: + * http://www.sqlite.org/c3ref/stmt.html + * + * if last sqlite3_step command on this stmt returned an error, + * then sqlite3_reset will return that error, althought it is not an error. + * So sqlite3_reset allways succedes. + */ + sqlite3_reset(m_stmt); + + LogPedantic("SQL data command reset"); +} + +void SqlConnection::DataCommand::CheckColumnIndex( + SqlConnection::ColumnIndex column) +{ + if (column < 0 || column >= sqlite3_column_count(m_stmt)) + ThrowMsg(Exception::InvalidColumn, "Column index is out of bounds"); +} + +bool SqlConnection::DataCommand::IsColumnNull( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column type: [" << column << "]"); + CheckColumnIndex(column); + return sqlite3_column_type(m_stmt, column) == SQLITE_NULL; +} + +int SqlConnection::DataCommand::GetColumnInteger( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column integer: [" << column << "]"); + CheckColumnIndex(column); + int value = sqlite3_column_int(m_stmt, column); + LogPedantic(" Value: " << value); + return value; +} + +int8_t SqlConnection::DataCommand::GetColumnInt8( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column int8: [" << column << "]"); + CheckColumnIndex(column); + int8_t value = static_cast(sqlite3_column_int(m_stmt, column)); + LogPedantic(" Value: " << value); + return value; +} + +int16_t SqlConnection::DataCommand::GetColumnInt16( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column int16: [" << column << "]"); + CheckColumnIndex(column); + int16_t value = static_cast(sqlite3_column_int(m_stmt, column)); + LogPedantic(" Value: " << value); + return value; +} + +int32_t SqlConnection::DataCommand::GetColumnInt32( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column int32: [" << column << "]"); + CheckColumnIndex(column); + int32_t value = static_cast(sqlite3_column_int(m_stmt, column)); + LogPedantic(" Value: " << value); + return value; +} + +int64_t SqlConnection::DataCommand::GetColumnInt64( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column int64: [" << column << "]"); + CheckColumnIndex(column); + int64_t value = static_cast(sqlite3_column_int64(m_stmt, column)); + LogPedantic(" Value: " << value); + return value; +} + +float SqlConnection::DataCommand::GetColumnFloat( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column float: [" << column << "]"); + CheckColumnIndex(column); + float value = static_cast(sqlite3_column_double(m_stmt, column)); + LogPedantic(" Value: " << value); + return value; +} + +double SqlConnection::DataCommand::GetColumnDouble( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column double: [" << column << "]"); + CheckColumnIndex(column); + double value = sqlite3_column_double(m_stmt, column); + LogPedantic(" Value: " << value); + return value; +} + +std::string SqlConnection::DataCommand::GetColumnString( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column string: [" << column << "]"); + CheckColumnIndex(column); + + const char *value = reinterpret_cast( + sqlite3_column_text(m_stmt, column)); + + LogPedantic("Value: " << (value ? value : "NULL")); + + if (value == NULL) + return std::string(); + + return std::string(value); +} + +Optional SqlConnection::DataCommand::GetColumnOptionalInteger( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional integer: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) + return Optional::Null; + int value = sqlite3_column_int(m_stmt, column); + LogPedantic(" Value: " << value); + return Optional(value); +} + +Optional SqlConnection::DataCommand::GetColumnOptionalInt8( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional int8: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) + return Optional::Null; + int8_t value = static_cast(sqlite3_column_int(m_stmt, column)); + LogPedantic(" Value: " << value); + return Optional(value); +} + +Optional SqlConnection::DataCommand::GetColumnOptionalInt16( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional int16: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) + return Optional::Null; + int16_t value = static_cast(sqlite3_column_int(m_stmt, column)); + LogPedantic(" Value: " << value); + return Optional(value); +} + +Optional SqlConnection::DataCommand::GetColumnOptionalInt32( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional int32: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) + return Optional::Null; + int32_t value = static_cast(sqlite3_column_int(m_stmt, column)); + LogPedantic(" Value: " << value); + return Optional(value); +} + +Optional SqlConnection::DataCommand::GetColumnOptionalInt64( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional int64: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) + return Optional::Null; + int64_t value = static_cast(sqlite3_column_int64(m_stmt, column)); + LogPedantic(" Value: " << value); + return Optional(value); +} + +Optional SqlConnection::DataCommand::GetColumnOptionalFloat( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional float: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) + return Optional::Null; + float value = static_cast(sqlite3_column_double(m_stmt, column)); + LogPedantic(" Value: " << value); + return Optional(value); +} + +Optional SqlConnection::DataCommand::GetColumnOptionalDouble( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional double: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) + return Optional::Null; + double value = sqlite3_column_double(m_stmt, column); + LogPedantic(" Value: " << value); + return Optional(value); +} + +Optional SqlConnection::DataCommand::GetColumnOptionalString( + SqlConnection::ColumnIndex column) +{ + LogPedantic("SQL data command get column optional string: [" + << column << "]"); + CheckColumnIndex(column); + if (sqlite3_column_type(m_stmt, column) == SQLITE_NULL) + return Optional::Null; + const char *value = reinterpret_cast( + sqlite3_column_text(m_stmt, column)); + LogPedantic("Value: " << value); + String s = FromUTF8String(value); + return Optional(s); +} + +void SqlConnection::Connect(const std::string &address, Flag::Type flag) +{ + if (m_connection != NULL) + { + LogPedantic("Already connected."); + return; + } + + LogPedantic("Connecting to DB: " << address << "..."); + + // Connect to database + int result; + + if (flag & Flag::UseLucene) + { + result = db_util_open_with_options( + address.c_str(), + &m_connection, + SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, + NULL); + + m_usingLucene = true; + LogPedantic("Lucene index enabled"); + } + else + { + result = sqlite3_open_v2( + address.c_str(), + &m_connection, + SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, + NULL); + + m_usingLucene = false; + LogPedantic("Lucene index disabled"); + } + + if (result == SQLITE_OK) + { + LogPedantic("Connected to DB"); + } + else + { + LogPedantic("Failed to connect to DB!"); + ThrowMsg(Exception::ConnectionBroken, address); + } + + // Enable foreign keys + TurnOnForeignKeys(); +} + +void SqlConnection::Disconnect() +{ + if (m_connection == NULL) + { + LogPedantic("Already disconnected."); + return; + } + + LogPedantic("Disconnecting from DB..."); + + // All stored data commands must be deleted before disconnect + Assert(m_dataCommandsCount == 0 && + "All stored procedures must be deleted" + " before disconnecting SqlConnection"); + + int result; + + if (m_usingLucene) + { + result = db_util_close(m_connection); + } + else + { + result = sqlite3_close(m_connection); + } + + if (result != SQLITE_OK) + { + const char *error = sqlite3_errmsg(m_connection); + LogPedantic("SQL close failed"); + LogPedantic(" Error: " << error); + Throw(Exception::InternalError); + } + + m_connection = NULL; + + LogPedantic("Disconnected from DB"); +} + +bool SqlConnection::CheckTableExist(const char *tableName) +{ + if (m_connection == NULL) + { + LogPedantic("Cannot execute command. Not connected to DB!"); + return false; + } + + DataCommandAutoPtr command = + PrepareDataCommand("select tbl_name from sqlite_master where name=?;"); + + command->BindString(1, tableName); + + if (!command->Step()) + { + LogPedantic("No matching records in table"); + return false; + } + + return command->GetColumnString(0) == tableName; +} + +SqlConnection::SqlConnection(const std::string &address, + Flag::Type flag, + SynchronizationObject *synchronizationObject) + : m_connection(NULL), + m_usingLucene(false), + m_dataCommandsCount(0), + m_synchronizationObject(synchronizationObject) +{ + LogPedantic("Opening database connection to: " << address); + + // Connect to DB + SqlConnection::Connect(address, flag); + + if (!m_synchronizationObject) + { + LogPedantic("No synchronization object defined"); + } +} + +SqlConnection::~SqlConnection() +{ + LogPedantic("Closing database connection"); + + // Disconnect from DB + Try + { + SqlConnection::Disconnect(); + } + Catch (Exception::Base) + { + LogPedantic("Failed to disconnect from database"); + } +} + +void SqlConnection::ExecCommand(const char *format, ...) +{ + if (m_connection == NULL) + { + LogPedantic("Cannot execute command. Not connected to DB!"); + return; + } + + char *rawBuffer; + + va_list args; + va_start(args, format); + + if (vasprintf(&rawBuffer, format, args) == -1) + rawBuffer = NULL; + + va_end(args); + + ScopedFree buffer(rawBuffer); + + if (!buffer) + { + LogPedantic("Failed to allocate statement string"); + return; + } + + LogPedantic("Executing SQL command: " << buffer.Get()); + + // Notify all after potentially synchronized database connection access + ScopedNotifyAll notifyAll(m_synchronizationObject.Get()); + + for (;;) + { + char *errorBuffer; + + int ret = sqlite3_exec(m_connection, + buffer.Get(), + NULL, + NULL, + &errorBuffer); + + std::string errorMsg; + + // Take allocated error buffer + if (errorBuffer != NULL) + { + errorMsg = errorBuffer; + sqlite3_free(errorBuffer); + } + + if (ret == SQLITE_OK) + return; + + if (ret == SQLITE_BUSY) + { + LogPedantic("Collision occurred while executing SQL command"); + + // Synchronize if synchronization object is available + if (m_synchronizationObject) + { + LogPedantic("Performing synchronization"); + m_synchronizationObject->Synchronize(); + continue; + } + + // No synchronization object defined. Fail. + } + + // Fatal error + LogPedantic("Failed to execute SQL command. Error: " << errorMsg); + ThrowMsg(Exception::SyntaxError, errorMsg); + } +} + +SqlConnection::DataCommandAutoPtr SqlConnection::PrepareDataCommand( + const char *format, + ...) +{ + if (m_connection == NULL) + { + LogPedantic("Cannot execute data command. Not connected to DB!"); + return DataCommandAutoPtr(); + } + + char *rawBuffer; + + va_list args; + va_start(args, format); + + if (vasprintf(&rawBuffer, format, args) == -1) + rawBuffer = NULL; + + va_end(args); + + ScopedFree buffer(rawBuffer); + + if (!buffer) + { + LogPedantic("Failed to allocate statement string"); + return DataCommandAutoPtr(); + } + + LogPedantic("Executing SQL data command: " << buffer.Get()); + + return DataCommandAutoPtr(new DataCommand(this, buffer.Get())); +} + +SqlConnection::RowID SqlConnection::GetLastInsertRowID() const +{ + return static_cast(sqlite3_last_insert_rowid(m_connection)); +} + +void SqlConnection::TurnOnForeignKeys() +{ + ExecCommand("PRAGMA foreign_keys = ON;"); +} + +SqlConnection::SynchronizationObject * + SqlConnection::AllocDefaultSynchronizationObject() +{ + return new NaiveSynchronizationObject(); +} + +} // namespace DB +} // namespace DPL diff --git a/modules/db/src/thread_database_support.cpp b/modules/db/src/thread_database_support.cpp new file mode 100644 index 0000000..5b7865c --- /dev/null +++ b/modules/db/src/thread_database_support.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file thread_database_support.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk) + * @version 1.0 + * @brief This file contains the definition of thread database support + */ + +#include \ No newline at end of file diff --git a/modules/dbus/config.cmake b/modules/dbus/config.cmake new file mode 100644 index 0000000..995c071 --- /dev/null +++ b/modules/dbus/config.cmake @@ -0,0 +1,55 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file config.cmake +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +SET(DPL_DBUS_SOURCES + ${PROJECT_SOURCE_DIR}/modules/dbus/src/connection.cpp + ${PROJECT_SOURCE_DIR}/modules/dbus/src/dispatcher.cpp + ${PROJECT_SOURCE_DIR}/modules/dbus/src/interface.cpp + ${PROJECT_SOURCE_DIR}/modules/dbus/src/object.cpp + ${PROJECT_SOURCE_DIR}/modules/dbus/src/object_proxy.cpp + ${PROJECT_SOURCE_DIR}/modules/dbus/src/server.cpp + PARENT_SCOPE +) + + +SET(DPL_DBUS_HEADERS + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/connection.h + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/dbus_client.h + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/dbus_deserialization.h + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/dbus_interface_dispatcher.h + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/dbus_serialization.h + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/dbus_server_deserialization.h + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/dbus_server_serialization.h + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/dbus_signature.h + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/dispatcher.h + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/exception.h + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/interface.h + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/method_proxy.h + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/object.h + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/object_proxy.h + ${PROJECT_SOURCE_DIR}/modules/dbus/include/dpl/dbus/server.h + PARENT_SCOPE +) + +SET(DPL_DBUS_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/modules/dbus/include + PARENT_SCOPE +) diff --git a/modules/dbus/include/dpl/dbus/connection.h b/modules/dbus/include/dpl/dbus/connection.h new file mode 100644 index 0000000..886038c --- /dev/null +++ b/modules/dbus/include/dpl/dbus/connection.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file connection.h + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef DPL_DBUS_CONNECTION_H +#define DPL_DBUS_CONNECTION_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL { +namespace DBus { + +namespace ConnectionEvents +{ +/** + * Emitted when service name is acquired. + * + * Arg0 Acquired name. + */ +DECLARE_GENERIC_EVENT_1(ServiceNameAcquiredEvent, std::string) + +/** + * Emitted when service name is lost. + * + * Arg0 Lost name. + */ +DECLARE_GENERIC_EVENT_1(ServiceNameLostEvent, std::string) + +/** + * Emitted when remote host closes connection. + * + * Arg0 Low-level error message. + */ +DECLARE_GENERIC_EVENT_1(ConnectionBrokenEvent, std::string) + +/** + * Emitted when invalid or malformed data appear on connection. + * + * Arg0 Low-level error message. + */ +DECLARE_GENERIC_EVENT_1(ConnectionInvalidEvent, std::string) +} + +class Server; + +class Connection; +typedef std::shared_ptr ConnectionPtr; + +typedef std::shared_ptr ObjectProxyPtr; + +class Connection : + public DPL::Event::EventSupport, + public DPL::Event::EventSupport, + public DPL::Event::EventSupport, + public DPL::Event::EventSupport +{ +public: + /** + * Acquires connection to session bus. + * + * @return Session bus connection. + * @throw DBus::Exception If unable to connect to session bus. + */ + static ConnectionPtr sessionBus(); + + /** + * Acquires connection to system bus. + * + * @return System bus connection. + * @throw DBus::Exception If unable to connect to system bus. + */ + static ConnectionPtr systemBus(); + + /** + * Acquires connection to specified bus. + * + * @return Bus connection. + * @throw DBus::Exception If unable to connect to a bus. + */ + static ConnectionPtr connectTo(GBusType busType); + + /** + * Acquires connection to for specified address. + * + * @return Connection. + * @throw DBus::Exception If unable to connect. + * + * @remarks Address should be in DBus format (@see DBus documentation). + */ + static ConnectionPtr connectTo(const std::string& address); + + ~Connection(); + + /** + * Sets up a service on the connection. + * + * @param serviceName Service to register. + * @throw DBus::Exception If registration failed. + * + * @remarks Add objects before services to prevent notifications about new + * interfaces being added. + */ + void registerService(const std::string& serviceName); + + /** + * Unregisters a service from the connection. + * + * @param serviceName Service to unregister. + * @throw DBus::Exception If service not registered. + */ + void unregisterService(const std::string& serviceName); + + /** + * Adds object to the connection. + * + * @param object Object to register. + * @throw DBus::Exception If registration failed. + * + * @remarks Add objects before services to prevent notifications about new + * interfaces being added. + */ + void registerObject(const ObjectPtr& object); + + /** + * Removed object from the connection. + * + * @param objectPath Path of the object to unregister. + * @throw DBus::Exception If object not registered. + */ + void unregisterObject(const std::string& objectPath); + + /** + * Creates proxy to remote objects. + * + * @param serviceName Name of the DBus service. + * @param objectPath DBus path to the object. + * @return Object proxy. + * @throw DBus::ConnectionClosedException If connection is closed. + */ + ObjectProxyPtr createObjectProxy(const std::string& serviceName, + const std::string& objectPath); + +private: + friend class Server; + + typedef std::map RegisteredServices; + + struct ObjectRegistration + { + ObjectRegistration(guint registrationId, const ObjectPtr& object) + : registrationId(registrationId), + object(object) + { + } + + guint registrationId; + ObjectPtr object; + }; + typedef std::map RegisteredObjects; + + static void onServiceNameAcquired(GDBusConnection* connection, + const gchar* serviceName, + gpointer data); + + static void onServiceNameLost(GDBusConnection* connection, + const gchar* serviceName, + gpointer data); + + static void onConnectionClosed(GDBusConnection* connection, + gboolean peerVanished, + GError* error, + gpointer data); + + explicit Connection(GDBusConnection* connection); + + GDBusConnection* m_connection; + + RegisteredServices m_registeredServices; + + RegisteredObjects m_registeredObjects; +}; + +} +} + +#endif // DPL_DBUS_CONNECTION_H diff --git a/modules/dbus/include/dpl/dbus/dbus_client.h b/modules/dbus/include/dpl/dbus/dbus_client.h new file mode 100644 index 0000000..6b86c57 --- /dev/null +++ b/modules/dbus/include/dpl/dbus/dbus_client.h @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file dbus_client.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 1.0 + * @brief Header file for DBus generic client support + */ + +#ifndef DPL_DBUS_DBUS_CLIENT_H_ +#define DPL_DBUS_DBUS_CLIENT_H_ + +#include +#include +#include +#include +#include +#include + +namespace DPL { +namespace DBus { + +/* + * DBus::Client class is intended to act as simple DBus client. To call a method + * on remote service "Service", on remote object "Object", interface + * "Interface",use it like this: + * + * + * DBus::Client client("Object", "Service", "Interface"); + * (...) // variables declarations + * client.call("Method name", arg1, arg2, arg2, ... argN, + * &outArg1, &outArg2, &outArg3, ..., &outArgN); + * + * + * As You can see, input parameters of the call are passed with reference, + * output ones are passed as pointers - parameters MUST be passed this way. + * + * To call a void function (no out params), just pass in arguments to Call(). + * + * Currently client supports serialization and deserialization of simple types + * (int, char, float, unsigned), strings (std::string and char*) and + * some STL containers (std::vector, std::list, std::map, std::set, std::pair). + * Structures and classes are not (yet) supported. + */ + +class Client +{ + + public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, DBusClientException) + }; + + Client(std::string serverPath, + std::string serviceName, + std::string interfaceName) : + m_serviceName(serviceName), + m_serverPath(serverPath), + m_interfaceName(interfaceName) + { + DBusError error; + + dbus_error_init(&error); + m_connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + if (NULL == m_connection) { + LogPedantic("Couldn't get DBUS connection. Error: " << + error.message); + dbus_error_free(&error); + ThrowMsg(Exception::DBusClientException, + "Couldn't get DBUS connection." ); + } + } + + template + void call(const char* methodName, const Args&... args) + { + DBusMessage* message = dbus_message_new_method_call( + m_serviceName.c_str(), + m_serverPath.c_str(), + m_interfaceName.c_str(), + methodName); + DBusMessageIter argsIterator; + dbus_message_iter_init_append(message, &argsIterator); + call(message, &argsIterator, args...); + dbus_message_unref(message); + } + + template + void call(std::string methodName, const Args&... args) + { + call(methodName.c_str(), args...); + } + + ~Client() + { + dbus_connection_unref(m_connection); + } + + private: + + DBusMessage* makeCall( + DBusMessage* message) + { + DBusError error; + dbus_error_init(&error); + DBusMessage* ret = dbus_connection_send_with_reply_and_block( + m_connection, + message, + -1, + &error); + if (NULL == ret) { + LogPedantic("Error sending DBUS message: " << + error.message); + dbus_error_free(&error); + ThrowMsg(Exception::DBusClientException, + "Error sending DBUS message." ); + } + return ret; + } + + void call(DBusMessage* message, DBusMessageIter* /*argsIterator*/) + { + DBusMessage* ret = makeCall(message); + if (ret != NULL) { + dbus_message_unref(ret); + } else { + LogPedantic("Error getting DBUS response."); + ThrowMsg(Exception::DBusClientException, + "Error getting DBUS response." ); + } + } + + template + void call( + DBusMessage* message, + DBusMessageIter* argsIterator, + const T& invalue, + const Args&... args) + { + if (!Serialization::serialize(argsIterator, invalue)){ + LogPedantic("Error in serialization."); + ThrowMsg(Exception::DBusClientException, + "Error in serialization." ); + } + call(message, argsIterator, args...); + } + + template + void call( + DBusMessage* message, + DBusMessageIter* argsIterator, + const T* invalue, + const Args&... args) + { + if (!Serialization::serialize(argsIterator, invalue)){ + LogPedantic("Error in serialization."); + ThrowMsg(Exception::DBusClientException, + "Error in serialization." ); + } + call(message, argsIterator, args...); + } + + template + void call( + DBusMessage* message, + DBusMessageIter* argsIterator, + const T* invalue) + { + if (!Serialization::serialize(argsIterator, invalue)){ + LogPedantic("Error in serialization."); + ThrowMsg(Exception::DBusClientException, + "Error in serialization." ); + } + call(message, argsIterator); + } + + template + void call( + DBusMessage* message, + DBusMessageIter* /*argsIterator*/, + T* out, + const Args&... args) + { + DBusMessage* ret = makeCall(message); + if (ret != NULL) { + DBusMessageIter responseIterator; + dbus_message_iter_init(ret, &responseIterator); + returnFromCall(&responseIterator, out, args...); + dbus_message_unref(ret); + } + } + + template + void returnFromCall( + DBusMessageIter* responseIterator, + T* out, + const Args&... args) + { + if (!Deserialization::deserialize(responseIterator, out)){ + LogPedantic("Error in deserialization."); + ThrowMsg(Exception::DBusClientException, + "Error in deserialization." ); + } + returnFromCall(responseIterator, args...); + } + + template + void returnFromCall(DBusMessageIter* responseIterator, T* out) + { + if (!Deserialization::deserialize(responseIterator, out)){ + LogPedantic("Error in deserialization."); + ThrowMsg(Exception::DBusClientException, + "Error in deserialization." ); + } + } + + std::string m_serviceName, m_serverPath, m_interfaceName; + DBusConnection* m_connection; +}; + +} // namespace DBus +} // namespace DPL + +#endif // DPL_DBUS_DBUS_CLIENT_H_ diff --git a/modules/dbus/include/dpl/dbus/dbus_deserialization.h b/modules/dbus/include/dpl/dbus/dbus_deserialization.h new file mode 100644 index 0000000..9f50f91 --- /dev/null +++ b/modules/dbus/include/dpl/dbus/dbus_deserialization.h @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file dbus_deserialization.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 1.0 + * @brief Header file for DBus data deserialization + */ + +#ifndef DPL_DBUS_DBUS_DESERIALIZATION_H_ +#define DPL_DBUS_DBUS_DESERIALIZATION_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace DPL { +namespace DBus { + +struct Deserialization +{ + + static bool deserializePrimitive( + DBusMessageIter* responseIterator, + void* arg, + int type) + { + if (dbus_message_iter_get_arg_type(responseIterator) != type) { + return false; + } + dbus_message_iter_get_basic(responseIterator, arg); + return true; + } + + // char* and all integer types + doubles + template + static bool deserialize(DBusMessageIter* responseIterator, T* arg) + { + if (dbus_message_iter_get_arg_type(responseIterator) + != SimpleType::value) { + return false; + } + dbus_message_iter_get_basic(responseIterator, arg); + return true; + } + + // float case - read as double + static bool deserialize(DBusMessageIter* responseIterator, float* arg) + { + double d; + if (!deserialize(responseIterator, &d)){ + return false; + } + *arg = static_cast(d); + return true; + } + + // std::string + static bool deserialize( + DBusMessageIter* responseIterator, + std::string* arg) + { + char* str = NULL; + if (!deserialize(responseIterator, &str)){ + return false; + } + *arg = std::string(str); + return true; + } + + // dbus array deserialization + template + static bool deserializeContainer( + DBusMessageIter* responseIterator, + T* arg) + { + if (dbus_message_iter_get_arg_type(responseIterator) + != DBUS_TYPE_ARRAY) { + return false; + } + DBusMessageIter subIterator; + dbus_message_iter_recurse(responseIterator, &subIterator); + while (dbus_message_iter_get_arg_type(&subIterator) + != DBUS_TYPE_INVALID) { + arg->push_back(typename T::value_type()); + if (!deserialize(&subIterator, &arg->back())){ + return false; + } + dbus_message_iter_next(&subIterator); + } + return true; + } + + // std::vector + template + static bool deserialize( + DBusMessageIter* responseIterator, + std::vector* arg) + { + return deserializeContainer(responseIterator, arg); + } + + // std::list + template + static bool deserialize( + DBusMessageIter* responseIterator, + std::list* arg) + { + return deserializeContainer(responseIterator, arg); + } + + // std::set + template + static bool deserialize( + DBusMessageIter* responseIterator, + std::set* arg) + { + if (dbus_message_iter_get_arg_type(responseIterator) + != DBUS_TYPE_ARRAY) { + return false; + } + DBusMessageIter subIterator; + dbus_message_iter_recurse(responseIterator, &subIterator); + while (dbus_message_iter_get_arg_type(&subIterator) + != DBUS_TYPE_INVALID) { + typename std::set::value_type element; + if (!deserialize(&subIterator, &element)){ + return false; + } + arg->insert(element); + dbus_message_iter_next(&subIterator); + } + return true; + } + + // std::pair + template + static bool deserialize( + DBusMessageIter* argsIterator, + const std::pair* arg) + { + if (dbus_message_iter_get_arg_type(argsIterator) + != DBUS_TYPE_DICT_ENTRY) { + return false; + } + DBusMessageIter dictEntryIterator; + dbus_message_iter_recurse(argsIterator, &dictEntryIterator); + if (!deserialize(dictEntryIterator, &arg->first)){ + return false; + } + dbus_message_iter_next(&dictEntryIterator); + if (!deserialize(dictEntryIterator, &arg->second)){ + return false; + } + return true; + } + + // std::map + template + static bool deserialize( + DBusMessageIter* responseIterator, + const std::map* arg) + { + if (dbus_message_iter_get_arg_type(responseIterator) + != DBUS_TYPE_ARRAY) { + return false; + } + DBusMessageIter subIterator; + dbus_message_iter_recurse(responseIterator, &subIterator); + while (dbus_message_iter_get_arg_type(&subIterator) + != DBUS_TYPE_INVALID) { + typename std::pair element; + if (!deserialize(&subIterator, &element)){ + return false; + } + arg->insert(element); + dbus_message_iter_next(&subIterator); + } + return true; + } + +}; + +template<> +inline bool Deserialization::deserialize( + DBusMessageIter* responseIterator, + bool* arg) +{ + unsigned int value; + if (dbus_message_iter_get_arg_type(responseIterator) + != SimpleType::value) { + return false; + } + dbus_message_iter_get_basic(responseIterator, &value); + *arg = static_cast(value); + return true; +} + +} // namespace DBus +} // namespace DPL + +#endif // DPL_DBUS_DBUS_DESERIALIZATION_H_ diff --git a/modules/dbus/include/dpl/dbus/dbus_interface_dispatcher.h b/modules/dbus/include/dpl/dbus/dbus_interface_dispatcher.h new file mode 100644 index 0000000..d5e3591 --- /dev/null +++ b/modules/dbus/include/dpl/dbus/dbus_interface_dispatcher.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file dbus_interface_dispatcher.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 1.0 + * @brief This file contains definitions of DBus::InterfaceDispatcher + * class. + */ + +#ifndef DPL_DBUS_DBUS_INTERFACE_DISPATCHER_H_ +#define DPL_DBUS_DBUS_INTERFACE_DISPATCHER_H_ + +#include +#include +#include + +namespace DPL { +namespace DBus { + +class InterfaceDispatcher : public DBus::Dispatcher +{ +public: + explicit InterfaceDispatcher(const std::string& interfaceName): + m_interfaceName(interfaceName) + { + } + + virtual ~InterfaceDispatcher() + {} + + // Implement it in specific interface with method handling + virtual void onMethodCall(const gchar* /*methodName*/, + GVariant* /*parameters*/, + GDBusMethodInvocation* /*invocation*/) = 0; + + virtual std::string getName() const + { + return m_interfaceName; + } + + virtual std::string getXmlSignature() const + { + return m_xml; + } + virtual void setXmlSignature(const std::string& newSignature) + { + m_xml = newSignature; + } + + virtual void onMethodCall(GDBusConnection* /*connection*/, + const gchar* /*sender*/, + const gchar* /*objectPath*/, + const gchar* interfaceName, + const gchar* methodName, + GVariant* parameters, + GDBusMethodInvocation* invocation) + { + if (g_strcmp0(interfaceName, m_interfaceName.c_str()) == 0){ + onMethodCall(methodName, parameters, invocation); + } else { + LogPedantic("Called invalid interface: " << interfaceName << + " instead of: " << m_interfaceName); + } + } + + virtual GVariant* onPropertyGet(GDBusConnection* /*connection*/, + const gchar* /*sender*/, + const gchar* /*objectPath*/, + const gchar* /*interfaceName*/, + const gchar* propertyName) + { + LogInfo("InterfaceDispatcher onPropertyGet: " << propertyName); + return NULL; + } + + virtual gboolean onPropertySet(GDBusConnection* /*connection*/, + const gchar* /*sender*/, + const gchar* /*objectPath*/, + const gchar* /*interfaceName*/, + const gchar* propertyName, + GVariant* /*value*/) + { + LogInfo("InterfaceDispatcher onPropertySet: " << propertyName); + return false; + } +private: + std::string m_interfaceName, m_xml; +}; + +} // namespace DBus +} // namespace DPL + +#endif // DPL_DBUS_DBUS_INTERFACE_DISPATCHER_H_ diff --git a/modules/dbus/include/dpl/dbus/dbus_serialization.h b/modules/dbus/include/dpl/dbus/dbus_serialization.h new file mode 100644 index 0000000..7bfa65b --- /dev/null +++ b/modules/dbus/include/dpl/dbus/dbus_serialization.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file dbus_serialization.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 1.0 + * @brief Header file for DBus data derialization + */ + +#ifndef DPL_DBUS_DBUS_SERIALIZATION_H_ +#define DPL_DBUS_DBUS_SERIALIZATION_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL { +namespace DBus { + +struct Serialization +{ + + // std::string + static bool serialize( + DBusMessageIter* argsIterator, + const std::string& str) + { + return serialize(argsIterator, str.c_str()); + } + + // float case - send as double + static bool serialize(DBusMessageIter* argsIterator, const float& arg) + { + const double d = static_cast(arg); + return serialize(argsIterator, d); + } + + // char* and all integer types + doubles + template + static bool serialize(DBusMessageIter* argsIterator, const T& arg) + { + return dbus_message_iter_append_basic(argsIterator, + SimpleType::value, + &arg); + } + + // dbus array serialization + template + static bool serializeContainer( + DBusMessageIter* argsIterator, + const T& arg) + { + typename T::const_iterator containerIt; + DBusMessageIter subIterator; + if (!dbus_message_iter_open_container(argsIterator, DBUS_TYPE_ARRAY, + Signature::value(), &subIterator)) { + return false; + } + FOREACH(containerIt,arg) { + if (!serialize(&subIterator, *containerIt)){ + return false; + } + } + return dbus_message_iter_close_container(argsIterator, &subIterator); + } + + // std::vector + template + static bool serialize( + DBusMessageIter* argsIterator, + const std::vector &arg) + { + return serializeContainer(argsIterator, arg); + } + + // std::list + template + static bool serialize( + DBusMessageIter* argsIterator, + const std::list &arg) + { + return serializeContainer(argsIterator, arg); + } + + // std::set + template + static bool serialize( + DBusMessageIter* argsIterator, + const std::set &arg) + { + return serializeContainer(argsIterator, arg); + } + + // std::pair + template + static bool serialize( + DBusMessageIter* argsIterator, + const std::pair &arg) + { + DBusMessageIter dictEntryIterator; + if (!dbus_message_iter_open_container(argsIterator, + DBUS_TYPE_DICT_ENTRY, NULL, &dictEntryIterator)) { + return false; + } + if (!serialize(dictEntryIterator, arg.first)){ + return false; + } + if (!serialize(dictEntryIterator, arg.second)){ + return false; + } + return dbus_message_iter_close_container(argsIterator, + &dictEntryIterator); + } + + // std::map + template + static bool serialize( + DBusMessageIter* argsIterator, + const std::map &arg) + { + return serializeContainer(argsIterator, arg); + } + +}; + +// char* and all integer types + doubles +template<> +inline bool Serialization::serialize(DBusMessageIter* argsIterator, + const bool& arg) +{ + unsigned int value = static_cast(arg); + return dbus_message_iter_append_basic(argsIterator, + SimpleType::value, + &value); +} + +} // namespace DBus +} // namespace DPL + +#endif // DPL_DBUS_DBUS_SERIALIZATION_H_ diff --git a/modules/dbus/include/dpl/dbus/dbus_server_deserialization.h b/modules/dbus/include/dpl/dbus/dbus_server_deserialization.h new file mode 100644 index 0000000..5f3b28b --- /dev/null +++ b/modules/dbus/include/dpl/dbus/dbus_server_deserialization.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file dbus_server_deserialization.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 1.0 + * @brief Header file for DBus data deserialization from GVariant + */ + +#ifndef DPL_DBUS_DBUS_SERVER_DESERIALIZATION_H_ +#define DPL_DBUS_DBUS_SERVER_DESERIALIZATION_H_ + +#include +#include +#include +#include +#include +#include + +namespace DPL { +namespace DBus { + +struct ServerDeserialization { + + template + static bool deserialize(GVariant* g, T* arg1, Args... args) + { + Assert(NULL != g); + Assert(NULL != arg1); + GVariantIter* iterator = g_variant_iter_new(g); + if (NULL == iterator) { + return false; + } + if (!deserializeIterator(iterator, arg1)) { + g_variant_iter_free(iterator); + return false; + } + if (!deserializeIterator(iterator, args...)) { + g_variant_iter_free(iterator); + return false; + } + g_variant_iter_free(iterator); + return true; + } + + template + static bool deserialize(GVariant* g, T* arg1) + { + Assert(NULL != g); + Assert(NULL != arg1); + GVariantIter* iterator = g_variant_iter_new(g); + if (NULL == iterator) { + return false; + } + if (!deserializeIterator(iterator, arg1)) { + g_variant_iter_free(iterator); + return false; + } + g_variant_iter_free(iterator); + return true; + } + + // deserialization from GVariant tuple iterator + template + static bool deserializeIterator(GVariantIter* g, T* arg1, Args... args) + { + Assert(NULL != g); + Assert(NULL != arg1); + GVariant* elem = g_variant_iter_next_value(g); + if (NULL == elem) { + return false; + } + if (!deserializeElem(elem, arg1)) { + return false; + } + if (!deserializeIterator(g, args...)) { + return false; + } + return true; + } + + template + static bool deserializeIterator(GVariantIter* g, T* arg1) + { + Assert(NULL != g); + Assert(NULL != arg1); + GVariant* elem = g_variant_iter_next_value(g); + if (NULL == elem) { + return false; + } + if (!deserializeElem(elem, arg1)) { + return false; + } + g_variant_unref(elem); + return true; + } + + // type specialization + static bool deserializeElem(GVariant* parameters, std::string* outStr) + { + const gchar* arg = g_variant_get_string(parameters, NULL); + *outStr = std::string(arg); + return true; + } + + static bool deserializeElem(GVariant* parameters, int* outInt) + { + gint32 arg = g_variant_get_int32(parameters); + *outInt = arg; + return true; + } + + static bool deserializeElem(GVariant* parameters, unsigned* outInt) + { + guint32 arg = g_variant_get_uint32(parameters); + *outInt = arg; + return true; + } + + static bool deserializeElem(GVariant* parameters, bool* outInt) + { + gboolean arg = g_variant_get_boolean(parameters); + *outInt = arg; + return true; + } + + static bool deserializeElem(GVariant* parameters, float* outInt) + { + gdouble arg = g_variant_get_double(parameters); + *outInt = static_cast(arg); + return true; + } + + static bool deserializeElem(GVariant* parameters, + std::vector* outArray) + { + unsigned int i = 0; + gsize length = 0; + const gchar** args = g_variant_get_strv( + parameters, + &length); + for (i = 0; i < length; ++i) { + outArray->push_back(std::string(args[i])); + } + g_free(args); + return true; + } + + static bool deserializeElem(GVariant* parameters, + std::list* outArray) + { + unsigned int i = 0; + gsize length = 0; + const gchar** args = g_variant_get_strv( + parameters, + &length); + for (i = 0; i < length; ++i) { + outArray->push_back(std::string(args[i])); + } + g_free(args); + return true; + } +}; + +} // namespace DBus +} // namespace DPL + +#endif // DPL_DBUS_DBUS_SERVER_DESERIALIZATION_H_ diff --git a/modules/dbus/include/dpl/dbus/dbus_server_serialization.h b/modules/dbus/include/dpl/dbus/dbus_server_serialization.h new file mode 100644 index 0000000..8c299ef --- /dev/null +++ b/modules/dbus/include/dpl/dbus/dbus_server_serialization.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file dbus_server_serialization.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 1.0 + * @brief Header file for DBus data serialization to GVariant + */ + +#ifndef DPL_DBUS_DBUS_SERVER_SERIALIZATION_H_ +#define DPL_DBUS_DBUS_SERVER_SERIALIZATION_H_ + +#include +#include + +namespace DPL { +namespace DBus { + +struct ServerSerialization { + + template + static GVariant* serialize(Args... args) + { + GVariantBuilder* builder = g_variant_builder_new(G_VARIANT_TYPE_TUPLE); + if (NULL == builder) { + return NULL; + } + serializeBuilder(builder, args ...); + return g_variant_builder_end(builder); + } + + // serialization on GVariantBuilder + template + static void serializeBuilder(GVariantBuilder* builder, + const T& arg, + Args... args) + { + serializeElem(builder, arg); + serializeBuilder(builder, args ...); + } + + template + static void serializeBuilder(GVariantBuilder* builder, const T& arg) + { + serializeElem(builder, arg); + } + + // type specialization + static void serializeElem(GVariantBuilder* builder, int arg) + { + g_variant_builder_add_value(builder, g_variant_new_int32(arg)); + } + + static void serializeElem(GVariantBuilder* builder, unsigned arg) + { + g_variant_builder_add_value(builder, g_variant_new_uint32(arg)); + } + + static void serializeElem(GVariantBuilder* builder, bool arg) + { + g_variant_builder_add_value(builder, g_variant_new_boolean(arg)); + } + + static void serializeElem(GVariantBuilder* builder, float arg) + { + gdouble d = static_cast(arg); + g_variant_builder_add_value(builder, g_variant_new_double(d)); + } + + static void serializeElem(GVariantBuilder* builder, const char* arg) + { + g_variant_builder_add_value(builder, g_variant_new_string(arg)); + } + + static void serializeElem(GVariantBuilder* builder, + const std::string& arg) + { + g_variant_builder_add_value(builder, g_variant_new_string(arg.c_str())); + } +}; + +} // namespace DBus +} // namespace DPL + +#endif // DPL_DBUS_DBUS_SERVER_SERIALIZATION_H_ diff --git a/modules/dbus/include/dpl/dbus/dbus_signature.h b/modules/dbus/include/dpl/dbus/dbus_signature.h new file mode 100644 index 0000000..af0c2ef --- /dev/null +++ b/modules/dbus/include/dpl/dbus/dbus_signature.h @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file dbus_deserialization.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 1.0 + * @brief Header file for DBus data signatures + */ + +#ifndef DPL_DBUS_SIGNATURE_H +#define DPL_DBUS_SIGNATURE_H + +#include +#include + +namespace DPL { +namespace DBus { + +template +struct SimpleType; + +template +struct Signature +{ + static inline const char* value() + { + static const char signature[] = + { (char) SimpleType::value, 0 }; + return signature; + } +}; + +// signed integer types +template +struct __SignedIntegerType; + +template<> +struct __SignedIntegerType<1> +{ + static const int value = DBUS_TYPE_INT16; +}; + +template<> +struct __SignedIntegerType<2> +{ + static const int value = DBUS_TYPE_INT16; +}; + +template<> +struct __SignedIntegerType<4> +{ + static const int value = DBUS_TYPE_INT32; +}; + +template<> +struct __SignedIntegerType<8> +{ + static const int value = DBUS_TYPE_INT64; +}; + +// unsigned integer types +template +struct __UnsignedIntegerType; + +template<> +struct __UnsignedIntegerType<1> +{ + static const int value = DBUS_TYPE_BYTE; +}; +template<> +struct __UnsignedIntegerType<2> +{ + static const int value = DBUS_TYPE_UINT16; +}; +template<> +struct __UnsignedIntegerType<4> +{ + static const int value = DBUS_TYPE_UINT32; +}; +template<> +struct __UnsignedIntegerType<8> +{ + static const int value = DBUS_TYPE_UINT64; +}; + +// basic types +template<> +struct SimpleType +{ + static const int value = DBUS_TYPE_BOOLEAN; +}; + +template<> +struct SimpleType +{ + static const int value = __UnsignedIntegerType::value; +}; + +template<> +struct SimpleType +{ + static const int value = __SignedIntegerType::value; +}; + +template<> +struct SimpleType +{ + static const int value = + __UnsignedIntegerType::value; +}; + +template<> +struct SimpleType +{ + static const int value = __SignedIntegerType::value; +}; + +template<> +struct SimpleType +{ + static const int value = + __UnsignedIntegerType::value; +}; + +template<> +struct SimpleType +{ + static const int value = __SignedIntegerType::value; +}; + +template<> +struct SimpleType +{ + static const int value = + __UnsignedIntegerType::value; +}; + +template<> +struct SimpleType +{ + static const int value = __SignedIntegerType::value; +}; + +template<> +struct SimpleType +{ + static const int value = + __UnsignedIntegerType::value; +}; + +template<> +struct SimpleType +{ + static const int value = __SignedIntegerType::value; +}; + +template<> +struct SimpleType +{ + static const int value = __UnsignedIntegerType< + sizeof(unsigned long long)>::value; +}; + +template<> +struct SimpleType +{ + static const int value = DBUS_TYPE_DOUBLE; +}; + +template<> +struct SimpleType +{ + static const int value = DBUS_TYPE_DOUBLE; +}; + +template<> +struct SimpleType +{ + static const int value = DBUS_TYPE_STRING; +}; + +template<> +struct SimpleType +{ + static const int value = DBUS_TYPE_STRING; +}; + +template<> +struct SimpleType +{ + static const int value = DBUS_TYPE_STRING; +}; + +// STL containers signatures + +// generic array +template +struct ArraySignature +{ + static inline const char* value() + { + static const std::string signature = std::string( + DBUS_TYPE_ARRAY_AS_STRING) + Signature::value(); + return signature.c_str(); + } +}; + +// std::vector +template +struct Signature > : public ArraySignature +{ +}; + +// std::list +template +struct Signature > : public ArraySignature +{ +}; + +// std::set +template +struct Signature > : public ArraySignature +{ +}; + +// std::pair +template +struct Signature > +{ + static inline const char* value() + { + static const std::string signature = std::string( + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING) + + Signature::value() + Signature::value() + + DBUS_DICT_ENTRY_END_CHAR_AS_STRING; + return signature.c_str(); + } +}; + +// std::map +template +struct Signature > : public ArraySignature > +{ +}; + +} // namespace DBus +} // namespace DPL + +#endif // DPL_DBUS_SIGNATURE_H diff --git a/modules/dbus/include/dpl/dbus/dispatcher.h b/modules/dbus/include/dpl/dbus/dispatcher.h new file mode 100644 index 0000000..68e4397 --- /dev/null +++ b/modules/dbus/include/dpl/dbus/dispatcher.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file dispatcher.h + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef DPL_DBUS_DISPATCHER_H +#define DPL_DBUS_DISPATCHER_H + +#include + +namespace DPL { +namespace DBus { + +class Dispatcher +{ +public: + virtual ~Dispatcher() =0; + + /** + * Called on method invocation. + * + * @param connection + * @param sender + * @param objectPath + * @param interfaceName + * @param methodName + * @param parameters + * @param invocation + * + * @see GLib DBus documentation. + */ + virtual void onMethodCall(GDBusConnection* connection, + const gchar* sender, + const gchar* objectPath, + const gchar* interfaceName, + const gchar* methodName, + GVariant* parameters, + GDBusMethodInvocation* invocation) =0; + + /** + * Called on property get. + * + * @param connection + * @param sender + * @param objectPath + * @param interfaceName + * @param propertyName + * @return Porperty value. + * + * @see GLib DBus documentation. + */ + virtual GVariant* onPropertyGet(GDBusConnection* connection, + const gchar* sender, + const gchar* objectPath, + const gchar* interfaceName, + const gchar* propertyName, + GError** error); + + /** + * Called on property set. + * + * @param connection + * @param sender + * @param objectPath + * @param interfaceName + * @param propertyName + * @param value + * @return TRUE if successfully set, FALSE otherwise. + * + * @see GLib DBus documentation. + */ + virtual gboolean onPropertySet(GDBusConnection* connection, + const gchar* sender, + const gchar* objectPath, + const gchar* interfaceName, + const gchar* propertyName, + GVariant* value, + GError** error); +}; + +} +} + +#endif // DPL_DBUS_DISPATCHER_H diff --git a/modules/dbus/include/dpl/dbus/exception.h b/modules/dbus/include/dpl/dbus/exception.h new file mode 100644 index 0000000..fb21bf0 --- /dev/null +++ b/modules/dbus/include/dpl/dbus/exception.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file exception.h + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef DPL_DBUS_EXCEPTION_H +#define DPL_DBUS_EXCEPTION_H + +#include + +namespace DPL { +namespace DBus { + +/** + * Thrown when none of the following, more specific exception fit. + */ +DECLARE_EXCEPTION_TYPE(DPL::Exception, Exception) + +/** + * Thrown when trying to perform an operation on a closed connection. + */ +DECLARE_EXCEPTION_TYPE(DBus::Exception, ConnectionClosedException) + +/** + * Thrown when passing invalid argument(s). + */ +DECLARE_EXCEPTION_TYPE(DBus::Exception, InvalidArgumentException) + +} +} + +#endif diff --git a/modules/dbus/include/dpl/dbus/interface.h b/modules/dbus/include/dpl/dbus/interface.h new file mode 100644 index 0000000..1bf1148 --- /dev/null +++ b/modules/dbus/include/dpl/dbus/interface.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file interface.h + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef DPL_DBUS_INTERFACE_H +#define DPL_DBUS_INTERFACE_H + +#include +#include +#include +#include +#include + +namespace DPL { +namespace DBus { + +class Interface; +typedef std::shared_ptr InterfacePtr; + +class Interface : private DPL::Noncopyable +{ +public: + /** + * Parses supplied XML string to produce DBus interface descriptions. + * + * @param xmlString XML string to parse. + * @return Interfaces. + * @throw DPL::DBus::Exception If error while parsing occurs. + */ + static std::vector fromXMLString( + const std::string& xmlString); + +public: + ~Interface(); + + /** + * Gets pointers to functions called on method call or property get/set + * request. + * + * @return Pointers to functions. + */ + const GDBusInterfaceVTable* getVTable() const; + + /** + * Gets interface description. + * + * @return Interface description. + */ + GDBusInterfaceInfo* getInfo() const; + + /** + * Sets method/property dispatcher for the interface. + * + * @param dispatcher Method call and property get/set dispatcher. + */ + void setDispatcher(Dispatcher* dispatcher); + +private: + static void onMethodCallFunc(GDBusConnection *connection, + const gchar *sender, + const gchar *objectPath, + const gchar *interfaceName, + const gchar *methodName, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer data); + + static GVariant* onPropertyGetFunc(GDBusConnection *connection, + const gchar *sender, + const gchar *objectPath, + const gchar *interfaceName, + const gchar *propertyName, + GError **error, + gpointer data); + + static gboolean onPropertySetFunc(GDBusConnection *connection, + const gchar *sender, + const gchar *objectPath, + const gchar *interfaceName, + const gchar *propertyName, + GVariant *value, + GError **error, + gpointer data); + + explicit Interface(GDBusInterfaceInfo* info); + + static const GDBusInterfaceVTable m_vTable; + + GDBusInterfaceInfo* m_info; + + Dispatcher* m_dispatcher; +}; + +} +} + +#endif // DPL_DBUS_INTERFACE_H diff --git a/modules/dbus/include/dpl/dbus/method_proxy.h b/modules/dbus/include/dpl/dbus/method_proxy.h new file mode 100644 index 0000000..3d553cf --- /dev/null +++ b/modules/dbus/include/dpl/dbus/method_proxy.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file method_proxy.h + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef DPL_DBUS_METHOD_PROXY_H +#define DPL_DBUS_METHOD_PROXY_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL { +namespace DBus { + +class ObjectProxy; + +/** + * Represents a remote method. + */ +template +class MethodProxy +{ +public: + ~MethodProxy() + { + g_object_unref(m_connection); + } + + /** + * Invokes remote method. + * + * @param args Input arguments for remote method. + * @return Value returned by remote method. + * @throw DBus::InvalidArgumentException If invalid argument(s) supplied. + * @throw DBus::ConnectionClosedException If connection is closed. + * @throw DBus::Exception If some other error occurs. + */ + Result operator()(const Args&... args) + { + return invoke(args...); + } + +private: + friend class ObjectProxy; + + MethodProxy(GDBusConnection* connection, + const std::string& serviceName, + const std::string& objectPath, + const std::string& interfaceName, + const std::string& methodName) + : m_connection(connection), + m_serviceName(serviceName), + m_objectPath(objectPath), + m_interfaceName(interfaceName), + m_methodName(methodName) + { + Assert(m_connection && "Connection is not set."); + + g_object_ref(m_connection); + } + + /** + * @remarks Making it a template with parameter set by default to class + * template parameter to overload on return type by utilizing + * the SFINAE concept. + */ + template + typename std::enable_if::value, R>::type + invoke(const Args&... args) + { + GVariant* parameters = serialize(args...); + + GVariant* invokeResult = invokeSync(parameters); + + R result; + + ServerDeserialization::deserialize(invokeResult, &result); + + g_variant_unref(invokeResult); + + return result; + } + + /** + * @remarks Void return type overload. + */ + template + typename std::enable_if::value>::type + invoke(const Args&... args) + { + GVariant* parameters = serialize(args...); + + GVariant* invokeResult = invokeSync(parameters); + + g_variant_unref(invokeResult); + } + + /** + * @remarks ArgsM... are the same as Args...; it has been made a template + * to make overloading/specialization possible. + */ + template + GVariant* serialize(ArgsM&&... args) + { + return ServerSerialization::serialize(std::forward(args)...); + } + + /** + * @remarks Specialization for zero-argument functions. + */ + GVariant* serialize() + { + return NULL; + } + + /** + * Calls remote method over DBus. + * + * @param parameters Input parameters for the remote method. + * @return Result returned by the remote method. + * @throw DBus::InvalidArgumentException If invalid argument(s) supplied. + * @throw DBus::ConnectionClosedException If connection is closed. + * @throw DBus::Exception If some other error occurs. + */ + GVariant* invokeSync(GVariant* parameters) + { + GError* error = NULL; + + LogPedantic("Invoking method: " << m_interfaceName << "." << m_methodName); + GVariant* result = g_dbus_connection_call_sync(m_connection, + m_serviceName.c_str(), + m_objectPath.c_str(), + m_interfaceName.c_str(), + m_methodName.c_str(), + parameters, + G_VARIANT_TYPE_TUPLE, + G_DBUS_CALL_FLAGS_NONE, + DBUS_SYNC_CALL_TIMEOUT, + NULL, + &error); + if (NULL == result) + { + std::ostringstream oss; + oss << "Error while invoking: " + << m_interfaceName << "." << m_methodName + << " <" << error->message << ">"; + std::string message = oss.str(); + + gint code = error->code; + + g_error_free(error); + + switch (code) + { + case G_IO_ERROR_INVALID_ARGUMENT: + ThrowMsg(DBus::InvalidArgumentException, message); + case G_IO_ERROR_CLOSED: + ThrowMsg(DBus::ConnectionClosedException, message); + default: + ThrowMsg(DBus::Exception, message); + } + } + + return result; + } + + /** + * Default timeout for synchronous method call. + * + * @see GIO::GDBusConnection::g_dbus_connection_call_sync() for details. + */ + static const gint DBUS_SYNC_CALL_TIMEOUT = -1; + + GDBusConnection* m_connection; + std::string m_serviceName; + std::string m_objectPath; + std::string m_interfaceName; + std::string m_methodName; +}; + +/** + * Smart pointer for MethodProxy objects. + */ +template +class MethodProxyPtr +{ +public: + explicit MethodProxyPtr(MethodProxy* method = NULL) + : m_method(method) + { + } + + Result operator()(const Args&... args) const + { + Assert(NULL != m_method.get() && "Method not set."); + + return (*m_method)(args...); + } + +private: + std::shared_ptr > m_method; +}; + +} +} + +#endif diff --git a/modules/dbus/include/dpl/dbus/object.h b/modules/dbus/include/dpl/dbus/object.h new file mode 100644 index 0000000..7731dfa --- /dev/null +++ b/modules/dbus/include/dpl/dbus/object.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file object.h + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef DPL_DBUS_OBJECT_H +#define DPL_DBUS_OBJECT_H + +#include +#include +#include +#include + +namespace DPL { +namespace DBus { + +class Object; +typedef std::shared_ptr ObjectPtr; + +class Object +{ +public: + /** + * Creates an object. + * + * @param path Object's path. + * @param interface Interface the object supports. + * @return Object shared pointer. + */ + static ObjectPtr create(const std::string& path, + const InterfacePtr& interface); + + /** + * Gets object's path. + * + * @return Object's path. + */ + std::string getPath() const; + + /** + * Gets object's interface. + * + * @return Object's interface. + */ + InterfacePtr getInterface() const; + +private: + Object(const std::string& path, const InterfacePtr& interface); + + std::string m_path; + InterfacePtr m_interface; +}; + +} +} + +#endif // WRT_SRC_DBUS_OBJECT_H diff --git a/modules/dbus/include/dpl/dbus/object_proxy.h b/modules/dbus/include/dpl/dbus/object_proxy.h new file mode 100644 index 0000000..3d727c0 --- /dev/null +++ b/modules/dbus/include/dpl/dbus/object_proxy.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file object_proxy.h + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef DPL_DBUS_OBJECT_PROXY_H +#define DPL_DBUS_OBJECT_PROXY_H + +#include +#include +#include +#include + +namespace DPL { +namespace DBus { + +class Connection; + +/** + * Represents a remote object attached to a DBus service. + */ +class ObjectProxy +{ +public: + ~ObjectProxy(); + + /** + * Creates method proxy object. + * + * The object is used to call remote methods. + * + * @param interface Name of the DBus interface. + * @param name Name of the method to call. + * @return Proxy to remote method. + * @throw DBus::ConnectionClosedException If connection is closed. + */ + template + MethodProxyPtr createMethodProxy( + const std::string& interface, + const std::string& name) + { + if (g_dbus_connection_is_closed(m_connection)) + { + ThrowMsg(DBus::ConnectionClosedException, "Connection closed."); + } + + return MethodProxyPtr( + new MethodProxy(m_connection, + m_serviceName, + m_objectPath, + interface, + name)); + } + +private: + friend class Connection; + + ObjectProxy(GDBusConnection* connection, + const std::string& serviceName, + const std::string& objectPath); + + GDBusConnection* m_connection; + std::string m_serviceName; + std::string m_objectPath; +}; + +} +} + +#endif diff --git a/modules/dbus/include/dpl/dbus/server.h b/modules/dbus/include/dpl/dbus/server.h new file mode 100644 index 0000000..39220ae --- /dev/null +++ b/modules/dbus/include/dpl/dbus/server.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file server.h + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef DPL_DBUS_SERVER_H +#define DPL_DBUS_SERVER_H + +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace DBus +{ + +namespace ServerEvents +{ +/** + * Emitted when new connection is accepted. + * + * Arg0 Accepted connection. + * + * @remarks If this event is processed on separate thread than that thread + * should run GLib main loop if one wants to e.g. register DBus + * objects during this event processing. + */ +DECLARE_GENERIC_EVENT_1(NewConnectionEvent, ConnectionPtr) +} + +class Server; +typedef std::shared_ptr ServerPtr; + +/** + * Class acting as a server for peer to peer connections over DBus. + */ +class Server : public DPL::Event::EventSupport +{ +public: + /** + * Creates server. + * + * @param address Address the server should listen on. + * @return Server. + */ + static ServerPtr create(const std::string& address); + + ~Server(); + + /** + * Starts the server. + */ + void start(); + + /** + * Stops the server. + */ + void stop(); + +protected: + explicit Server(GDBusServer* server); + +private: + static gboolean onNewConnection(GDBusServer* server, + GDBusConnection* connection, + gpointer data); + + GDBusServer* m_server; +}; + +} +} + +#endif // DPL_DBUS_SERVER_H diff --git a/modules/dbus/src/connection.cpp b/modules/dbus/src/connection.cpp new file mode 100644 index 0000000..e50faa8 --- /dev/null +++ b/modules/dbus/src/connection.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file connection.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief + */ + +#include +#include +#include +#include + +namespace DPL +{ +namespace DBus +{ + +ConnectionPtr Connection::sessionBus() +{ + return connectTo(G_BUS_TYPE_SESSION); +} + +ConnectionPtr Connection::systemBus() +{ + return connectTo(G_BUS_TYPE_SYSTEM); +} + +ConnectionPtr Connection::connectTo(GBusType busType) +{ + GError* error = NULL; + + GDBusConnection* connection = g_bus_get_sync(busType, + NULL, + &error); + if (NULL == connection) + { + std::string message; + if (NULL != error) + { + message = error->message; + g_error_free(error); + } + ThrowMsg(DBus::Exception, + "Couldn't connect to bus: " << message); + } + + g_dbus_connection_set_exit_on_close(connection, FALSE); + + return ConnectionPtr(new Connection(connection)); +} + +ConnectionPtr Connection::connectTo(const std::string& address) +{ + GError* error = NULL; + + GDBusConnection* connection = g_dbus_connection_new_for_address_sync( + address.c_str(), + G_DBUS_CONNECTION_FLAGS_NONE, + NULL, + NULL, + &error); + if (NULL == connection) + { + std::string message; + if (NULL != error) + { + message = error->message; + g_error_free(error); + } + ThrowMsg(DBus::Exception, + "Couldn't connect to " << address << ": " << message); + } + + return ConnectionPtr(new Connection(connection)); +} + +Connection::Connection(GDBusConnection* connection) + : m_connection(connection) +{ + g_signal_connect(m_connection, + "closed", + G_CALLBACK(onConnectionClosed), + this); +} + +Connection::~Connection() +{ + std::for_each(m_registeredServices.begin(), + m_registeredServices.end(), + [] (const RegisteredServices::value_type& value) + { + g_bus_unown_name(value.second); + }); + + std::for_each(m_registeredObjects.begin(), + m_registeredObjects.end(), + [m_connection] (const RegisteredObjects::value_type& value) + { + g_dbus_connection_unregister_object( + m_connection, + value.second.registrationId); + }); + + if (!g_dbus_connection_is_closed(m_connection)) + { + GError* error = NULL; + + if (FALSE == g_dbus_connection_flush_sync(m_connection, NULL, &error)) + { + LogPedantic("Could not flush the connection" + << " <" << error->message << ">"); + g_error_free(error); + } + } + + g_object_unref(m_connection); +} + +void Connection::registerService(const std::string& serviceName) +{ + guint regId = g_bus_own_name_on_connection(m_connection, + serviceName.c_str(), + G_BUS_NAME_OWNER_FLAGS_NONE, + onServiceNameAcquired, + onServiceNameLost, + this, + NULL); + if (0 >= regId) + { + ThrowMsg(DBus::Exception, "Error while registering service."); + } + + m_registeredServices.insert(RegisteredServices::value_type(serviceName, + regId)); +} + +void Connection::unregisterService(const std::string& serviceName) +{ + auto it = m_registeredServices.find(serviceName); + if (m_registeredServices.end() == it) + { + ThrowMsg(DBus::Exception, "Service not registered."); + } + + g_bus_unown_name(it->second); + + m_registeredServices.erase(it); +} + +void Connection::registerObject(const ObjectPtr& object) +{ + GError* error = NULL; + + guint regId = g_dbus_connection_register_object( + m_connection, + object->getPath().c_str(), + object->getInterface()->getInfo(), + object->getInterface()->getVTable(), + // TODO This is ugly, fix this! + object->getInterface().get(), + NULL, + &error); + if (0 == regId) + { + std::string message; + if (NULL != error) + { + message = error->message; + LogPedantic(error->message << " " << error->code); + g_error_free(error); + } + ThrowMsg(DBus::Exception, "Error while registering an object: " + << message); + } + + m_registeredObjects.insert(RegisteredObjects::value_type( + object->getPath(), + ObjectRegistration(regId, object))); +} + +void Connection::unregisterObject(const std::string& objectPath) +{ + auto it = m_registeredObjects.find(objectPath); + if (m_registeredObjects.end() == it) + { + ThrowMsg(DBus::Exception, "Object not registered."); + } + + gboolean result = g_dbus_connection_unregister_object( + m_connection, + it->second.registrationId); + if (FALSE == result) + { + ThrowMsg(DBus::Exception, "Unregistering object failed."); + } + m_registeredObjects.erase(it); +} + +ObjectProxyPtr Connection::createObjectProxy(const std::string& serviceName, + const std::string& objectPath) +{ + if (g_dbus_connection_is_closed(m_connection)) + { + ThrowMsg(DBus::ConnectionClosedException, "Connection closed."); + } + + return ObjectProxyPtr( + new ObjectProxy(m_connection, serviceName, objectPath)); +} + +void Connection::onServiceNameAcquired(GDBusConnection* /*connection*/, + const gchar* serviceName, + gpointer data) +{ + Assert(data && "Connection should not be NULL"); + + Connection* self = static_cast(data); + + LogPedantic("Emitting service name acquired event: " << serviceName); + + ConnectionEvents::ServiceNameAcquiredEvent event(serviceName); + self->DPL::Event::EventSupport:: + EmitEvent(event, DPL::Event::EmitMode::Queued); +} + +void Connection::onServiceNameLost(GDBusConnection* /*connection*/, + const gchar* serviceName, + gpointer data) +{ + Assert(data && "Connection should not be NULL"); + + Connection* self = static_cast(data); + + LogPedantic("Emitting service name lost event: " << serviceName); + + ConnectionEvents::ServiceNameLostEvent event(serviceName); + self->DPL::Event::EventSupport:: + EmitEvent(event, DPL::Event::EmitMode::Queued); +} + +void Connection::onConnectionClosed(GDBusConnection* /*connection*/, + gboolean peerVanished, + GError* error, + gpointer data) +{ + Assert(NULL != data && "Connection cannot be NULL"); + + Connection* self = static_cast(data); + + if ((NULL == error) && (FALSE == peerVanished)) + { + // Connection closed by this. + } + else if (NULL != error) + { + std::string message = error->message; + + g_error_free(error); + + if (TRUE == peerVanished) + { + // Connection closed by remote host. + ConnectionEvents::ConnectionBrokenEvent event(message); + self->DPL::Event::EventSupport:: + EmitEvent(event, DPL::Event::EmitMode::Queued); + } + else + { + // Invalid or malformed data on connection. + ConnectionEvents::ConnectionInvalidEvent event(message); + self->DPL::Event::EventSupport:: + EmitEvent(event, DPL::Event::EmitMode::Queued); + } + } +} + +} +} diff --git a/modules/dbus/src/dispatcher.cpp b/modules/dbus/src/dispatcher.cpp new file mode 100644 index 0000000..ca1bbab --- /dev/null +++ b/modules/dbus/src/dispatcher.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file dispatcher.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief + */ + +#include + +namespace DPL +{ +namespace DBus +{ + +Dispatcher::~Dispatcher() { } + +GVariant* Dispatcher::onPropertyGet(GDBusConnection* /*connection*/, + const gchar* /*sender*/, + const gchar* /*objectPath*/, + const gchar* /*interfaceName*/, + const gchar* /*propertyName*/, + GError** /*error*/) +{ + return NULL; +} + +gboolean Dispatcher::onPropertySet(GDBusConnection* /*connection*/, + const gchar* /*sender*/, + const gchar* /*objectPath*/, + const gchar* /*interfaceName*/, + const gchar* /*propertyName*/, + GVariant* /*value*/, + GError** /*error*/) +{ + return false; +} + +} +} diff --git a/modules/dbus/src/interface.cpp b/modules/dbus/src/interface.cpp new file mode 100644 index 0000000..248ac6d --- /dev/null +++ b/modules/dbus/src/interface.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file interface.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief + */ +#include +#include +#include +#include +#include + +namespace DPL { +namespace DBus { + +const GDBusInterfaceVTable Interface::m_vTable = +{ + Interface::onMethodCallFunc, + Interface::onPropertyGetFunc, + Interface::onPropertySetFunc, + {0, 0, 0, 0, 0, 0, 0, 0} +}; + +std::vector Interface::fromXMLString(const std::string& xmlString) +{ + GError* error = NULL; + + GDBusNodeInfo* nodeInfo = g_dbus_node_info_new_for_xml(xmlString.c_str(), + &error); + if (NULL == nodeInfo) + { + std::string message; + if (NULL != error) + { + message = error->message; + g_error_free(error); + } + ThrowMsg(DPL::DBus::Exception, + "Error parsing node info <" << message << ">"); + } + + std::vector result; + + GDBusInterfaceInfo** interface = nodeInfo->interfaces; + while (NULL != *interface) + { + result.push_back(InterfacePtr(new Interface(*interface))); + ++interface; + } + + g_dbus_node_info_unref(nodeInfo); + + return result; +} + +Interface::Interface(GDBusInterfaceInfo* info) + : m_info(info) +{ + g_dbus_interface_info_ref(m_info); +} + +Interface::~Interface() +{ + g_dbus_interface_info_unref(m_info); +} + +const GDBusInterfaceVTable* Interface::getVTable() const +{ + return &m_vTable; +} + +GDBusInterfaceInfo* Interface::getInfo() const +{ + return m_info; +} + +void Interface::setDispatcher(Dispatcher* dispatcher) +{ + m_dispatcher = dispatcher; +} + +void Interface::onMethodCallFunc(GDBusConnection *connection, + const gchar *sender, + const gchar *objectPath, + const gchar *interfaceName, + const gchar *methodName, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer data) +{ + Assert(NULL != data && "Interface cannot be NULL."); + Interface* self = static_cast(data); + + // TODO Verify interface name. + + if (NULL != self->m_dispatcher) + { + try + { + self->m_dispatcher->onMethodCall(connection, + sender, + objectPath, + interfaceName, + methodName, + parameters, + invocation); + } + catch (const DPL::Exception& /*ex*/) + { + // TODO Support for errors. + } + } +} + +GVariant* Interface::onPropertyGetFunc(GDBusConnection *connection, + const gchar *sender, + const gchar *objectPath, + const gchar *interfaceName, + const gchar *propertyName, + GError **error, + gpointer data) +{ + Assert(NULL != data && "Interface cannot be NULL."); + Interface* self = static_cast(data); + + // TODO Verify interface name. + + if (NULL != self->m_dispatcher) + { + try + { + // TODO Check if NULL is returned, if so set error variable. + return self->m_dispatcher->onPropertyGet(connection, + sender, + objectPath, + interfaceName, + propertyName, + error); + } + catch (const DPL::Exception& /*ex*/) + { + // TODO Support for errors. + } + } + + // TODO Set error. + + return NULL; +} + +gboolean Interface::onPropertySetFunc(GDBusConnection *connection, + const gchar *sender, + const gchar *objectPath, + const gchar *interfaceName, + const gchar *propertyName, + GVariant *value, + GError **error, + gpointer data) +{ + Assert(NULL != data && "Interface cannot be NULL."); + Interface* self = static_cast(data); + + // TODO Verify interface name. + + if (NULL != self->m_dispatcher) + { + try + { + return self->m_dispatcher->onPropertySet(connection, + sender, + objectPath, + interfaceName, + propertyName, + value, + error); + } + catch (const DPL::Exception& /*ex*/) + { + // TODO Support for errors. + } + } + + // TODO Set error. + + return false; +} + +} +} diff --git a/modules/dbus/src/object.cpp b/modules/dbus/src/object.cpp new file mode 100644 index 0000000..1c46018 --- /dev/null +++ b/modules/dbus/src/object.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file object.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief + */ + +#include + +namespace DPL { +namespace DBus { + +ObjectPtr Object::create(const std::string& path, const InterfacePtr& interface) +{ + return ObjectPtr(new Object(path, interface)); +} + +std::string Object::getPath() const +{ + return m_path; +} + +InterfacePtr Object::getInterface() const +{ + return m_interface; +} + +Object::Object(const std::string& path, const InterfacePtr& interface) + : m_path(path), + m_interface(interface) +{ +} + +} +} \ No newline at end of file diff --git a/modules/dbus/src/object_proxy.cpp b/modules/dbus/src/object_proxy.cpp new file mode 100644 index 0000000..22275a1 --- /dev/null +++ b/modules/dbus/src/object_proxy.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file object_proxy.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief + */ + +#include + +namespace DPL { +namespace DBus { + +ObjectProxy::ObjectProxy(GDBusConnection* connection, + const std::string& serviceName, + const std::string& objectPath) + : m_connection(connection), + m_serviceName(serviceName), + m_objectPath(objectPath) +{ + g_object_ref(m_connection); +} + +ObjectProxy::~ObjectProxy() +{ + g_object_unref(m_connection); +} + +} +} \ No newline at end of file diff --git a/modules/dbus/src/server.cpp b/modules/dbus/src/server.cpp new file mode 100644 index 0000000..08292f5 --- /dev/null +++ b/modules/dbus/src/server.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file server.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief + */ + +#include +#include +#include + +namespace DPL { +namespace DBus { + +ServerPtr Server::create(const std::string& address) +{ + GError* error = NULL; + + int flags = G_DBUS_SERVER_FLAGS_NONE | + G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; + + gchar* serverId = g_dbus_generate_guid(); + + GDBusServer* server = g_dbus_server_new_sync( + address.c_str(), + static_cast(flags), + serverId, + NULL, + NULL, + &error); + g_free(serverId); + + if (NULL == server) + { + std::string message; + if (NULL != error) + { + message = error->message; + g_error_free(error); + } + + ThrowMsg(DPL::Exception, "Error on server creation: " << message); + } + + return ServerPtr(new Server(server)); +} + +Server::Server(GDBusServer* server) + : m_server(server) +{ +} + +Server::~Server() +{ + if (g_dbus_server_is_active(m_server)) + { + stop(); + } + g_object_unref(m_server); +} + +void Server::start() +{ + Assert(!g_dbus_server_is_active(m_server) && "Server already started."); + + g_dbus_server_start(m_server); + + g_signal_connect(m_server, + "new-connection", + G_CALLBACK(onNewConnection), + this); + + LogInfo("Server started at: " + << g_dbus_server_get_client_address(m_server)); +} + +void Server::stop() +{ + Assert(g_dbus_server_is_active(m_server) && "Server not started."); + + g_dbus_server_stop(m_server); +} + +gboolean Server::onNewConnection(GDBusServer* /*server*/, + GDBusConnection* connection, + gpointer data) +{ + Assert(NULL != data && "User data cannot be NULL."); + + Server* self = static_cast(data); + + ServerEvents::NewConnectionEvent event( + ConnectionPtr(new Connection(connection))); + + LogInfo("Emitting new connection event"); + // TODO Blocking to allow object registration before any DBus messages are + // processed. + self->DPL::Event::EventSupport:: + EmitEvent(event, DPL::Event::EmitMode::Blocking); + + return TRUE; +} + +} +} \ No newline at end of file diff --git a/modules/event/config.cmake b/modules/event/config.cmake new file mode 100644 index 0000000..07831f0 --- /dev/null +++ b/modules/event/config.cmake @@ -0,0 +1,63 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file config.cmake +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +SET(DPL_EVENT_SOURCES + ${PROJECT_SOURCE_DIR}/modules/event/src/abstract_event_call.cpp + ${PROJECT_SOURCE_DIR}/modules/event/src/abstract_event_dispatcher.cpp + ${PROJECT_SOURCE_DIR}/modules/event/src/controller.cpp + ${PROJECT_SOURCE_DIR}/modules/event/src/event_delivery.cpp + ${PROJECT_SOURCE_DIR}/modules/event/src/event_delivery_detail.cpp + ${PROJECT_SOURCE_DIR}/modules/event/src/event_listener.cpp + ${PROJECT_SOURCE_DIR}/modules/event/src/event_support.cpp + ${PROJECT_SOURCE_DIR}/modules/event/src/generic_event_call.cpp + ${PROJECT_SOURCE_DIR}/modules/event/src/main_event_dispatcher.cpp + ${PROJECT_SOURCE_DIR}/modules/event/src/thread_event_dispatcher.cpp + ${PROJECT_SOURCE_DIR}/modules/event/src/inter_context_delegate.cpp + ${PROJECT_SOURCE_DIR}/modules/event/src/nested_loop.cpp + ${PROJECT_SOURCE_DIR}/modules/event/src/model.cpp + PARENT_SCOPE +) + +SET(DPL_EVENT_HEADERS + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/abstract_event_call.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/abstract_event_dispatcher.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/controller.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/event_delivery_detail.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/event_delivery.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/event_delivery_injector.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/event_delivery_messages.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/event_listener.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/event_support.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/generic_event_call.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/main_event_dispatcher.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/thread_event_dispatcher.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/nested_loop.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/inter_context_delegate.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/model.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/property.h + ${PROJECT_SOURCE_DIR}/modules/event/include/dpl/event/model_bind_to_dao.h + PARENT_SCOPE +) + +SET(DPL_EVENT_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/modules/event/include/ + PARENT_SCOPE +) diff --git a/modules/event/include/dpl/event/abstract_event_call.h b/modules/event/include/dpl/event/abstract_event_call.h new file mode 100644 index 0000000..e199897 --- /dev/null +++ b/modules/event/include/dpl/event/abstract_event_call.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_event_call.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of abstract event call + */ +#ifndef DPL_ABSTRACT_EVENT_CALL_H +#define DPL_ABSTRACT_EVENT_CALL_H + +#include + +namespace DPL +{ +namespace Event +{ + + +class AbstractEventCall + : private Noncopyable +{ +public: + /** + * Constructor + */ + explicit AbstractEventCall(); + + /** + * Destructor + */ + virtual ~AbstractEventCall(); + + /** + * Call abstract event call + */ + virtual void Call() = 0; +}; + +} +} // namespace DPL + +#endif // DPL_ABSTRACT_EVENT_CALL_H diff --git a/modules/event/include/dpl/event/abstract_event_dispatcher.h b/modules/event/include/dpl/event/abstract_event_dispatcher.h new file mode 100644 index 0000000..ddd76ad --- /dev/null +++ b/modules/event/include/dpl/event/abstract_event_dispatcher.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_event_dispatcher.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of abstract event dispatcher + */ +#ifndef DPL_ABSTRACT_EVENT_DISPATCHER_H +#define DPL_ABSTRACT_EVENT_DISPATCHER_H + +#include +#include + +namespace DPL +{ +namespace Event +{ + +class AbstractEventDispatcher + : private Noncopyable +{ +public: + /** + * Constructor + */ + explicit AbstractEventDispatcher(); + + /** + * Destructor + */ + virtual ~AbstractEventDispatcher(); + + /** + * Add abstract event call to abstract event dispatcher + * + * @param[in] abstractEventCall Pointer to abstract event call to add + * @return none + */ + virtual void AddEventCall(AbstractEventCall *abstractEventCall) = 0; + + /** + * Add abstract timed event call to abstract event dispatcher + * + * @param[in] abstractEventCall Pointer to abstract event call to add + * @param[in] dueTime Due time for timed event in seconds + * @return none + */ + virtual void AddTimedEventCall(AbstractEventCall *abstractEventCall, double dueTime) = 0; +}; + +} +} // namespace DPL + +#endif // DPL_ABSTRACT_EVENT_DISPATCHER_H diff --git a/modules/event/include/dpl/event/controller.h b/modules/event/include/dpl/event/controller.h new file mode 100644 index 0000000..1c5c75c --- /dev/null +++ b/modules/event/include/dpl/event/controller.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file controller.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of MVC controller + */ +#ifndef DPL_CONTROLLER_H +#define DPL_CONTROLLER_H + +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Event +{ + +template +class ControllerEventHandler + : public EventListener, + private EventSupport +{ +private: + bool m_touched; + +public: + ControllerEventHandler() + : m_touched(false) + { + EventSupport::AddListener(this); + } + + virtual ~ControllerEventHandler() + { + EventSupport::RemoveListener(this); + } + + void PostEvent(const EventType &event) + { + Assert(m_touched && "Default context not inherited. Call Touch() to inherit one."); + EventSupport::EmitEvent(event, EmitMode::Queued); + } + + void PostTimedEvent(const EventType &event, double dueTime) + { + Assert(m_touched && "Default context not inherited. Call Touch() to inherit one."); + EventSupport::EmitEvent(event, EmitMode::Deffered, dueTime); + } + + void PostSyncEvent(const EventType &event) + { + Assert(m_touched && "Default context not inherited. Call Touch() to inherit one."); + + // Check calling context + EventSupport::EmitEvent(event, EmitMode::Blocking); + } + + void SwitchToThread(Thread *thread) + { + Assert(m_touched && "Default context not inherited. Call Touch() to inherit one."); + EventSupport::SwitchListenerToThread(this, thread); + } + + void Touch() + { + m_touched = true; + EventSupport::SwitchListenerToThread(this, Thread::GetCurrentThread()); + } +}; + +template +class Controller + : public Controller, + public ControllerEventHandler +{ +public: + typedef typename EventTypeList::Head EventType; + +public: + Controller() + { + } + + virtual ~Controller() + { + } + + virtual void SwitchToThread(Thread *thread) + { + ControllerEventHandler::SwitchToThread(thread); + Controller::SwitchToThread(thread); + } + + virtual void Touch() + { + ControllerEventHandler::Touch(); + Controller::Touch(); + } +}; + +template<> +class Controller::Type> +{ +public: + Controller() + { + } + + virtual ~Controller() + { + } + + virtual void SwitchToThread(Thread *thread) + { + (void)thread; + } + + virtual void Touch() + { + } +}; + +} +} // namespace DPL + +// Utilities +#define CONTROLLER_POST_EVENT(Name, EventArg) Name##Singleton::Instance().DPL::Event::ControllerEventHandler<__typeof__ EventArg>::PostEvent(EventArg) +#define CONTROLLER_POST_TIMED_EVENT(Name, EventArg, DueTime) Name##Singleton::Instance().DPL::Event::ControllerEventHandler<__typeof__ EventArg>::PostTimedEvent(EventArg, DueTime) +#define CONTROLLER_POST_SYNC_EVENT(Name, EventArg) Name##Singleton::Instance().DPL::Event::ControllerEventHandler<__typeof__ EventArg>::PostSyncEvent(EventArg) + +#endif // DPL_CONTROLLER_H diff --git a/modules/event/include/dpl/event/event_delivery.h b/modules/event/include/dpl/event/event_delivery.h new file mode 100644 index 0000000..f4c893c --- /dev/null +++ b/modules/event/include/dpl/event/event_delivery.h @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file event_delivery.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of event delivery class + */ +#ifndef DPL_EVENT_DELIVERY_H +#define DPL_EVENT_DELIVERY_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Event delivery system + * + * Sample usages: + * + * I. Listening for standard predefined notifications + * + * class MyClass + * : public EventListener + * { + * void OnEventReceived(const EventMessages::RoamingChanged& event) + * { + * std::cout << "Roaming is now " << event.GetRoamingEnabled() ? "ENABLED" : "DISABLED" << std::endl; + * } + * + * public: + * MyClass() + * { + * EventDeliverySystem::AddListener(this); + * } + * + * virtual ~MyClass() + * { + * EventDeliverySystem::RemoveListener(this); + * } + * + * void SendSampleSignal() + * { + * EventMessages::RoamingChanged roamingMessage(true); + * EventDeliverySystem::Publish(roamingMessage); + * } + * } + * + * II. Creation of custom generic events, and listening for them + * + * EVENT_DELIVERY_DECLARE_EVENT_MESSAGE_2(MyEvent, int, float); + * + * class MyClass + * : public EventListener + * { + * void OnEventReceived(const MyEvent &event) + * { + * std::cout << "Event contents: " << event.GetArg0() << ", " << event.GetArg1() << std::endl; + * } + * + * public: + * MyClass() + * { + * EventDeliverySystem::AddListener(this); + * } + * + * virtual ~MyClass() + * { + * EventDeliverySystem::RemoveListener(this); + * } + * + * void SendSampleSignal() + * { + * MyEvent myEvent(5, 3.14f); + * EventDeliverySystem::Publish(myEvent); + * } + * } + * + * In source file, one must add implementation of event delivery message: + * + * EVENT_DELIVERY_IMPLEMENT_EVENT_MESSAGE(MyEvent) + * + */ +namespace DPL +{ +namespace Event +{ + +class AbstractEventDeliverySystemPublisher +{ +public: + virtual ~AbstractEventDeliverySystemPublisher() = 0; +}; + +template +class EventDeliverySystemPublisher + : private EventSupport + , public AbstractEventDeliverySystemPublisher +{ +public: + typedef typename EventSupport::EventListenerType EventListenerType; + +private: + friend class EventDeliverySystem; + + EventDeliverySystemPublisher(EventListenerType *eventListener) + : m_eventListener(eventListener) + { + EventSupport::AddListener(m_eventListener); + } + + EventListenerType *m_eventListener; + +public: + ~EventDeliverySystemPublisher() + { + EventSupport::RemoveListener(m_eventListener); + } + + void PublishEvent(const EventType &event) + { + EmitEvent(event, EmitMode::Queued); + } + + EventListenerType *GetListener() const + { + return m_eventListener; + } +}; + +template +class EventDeliverySystemTraits +{ +public: + typedef Type EventType; + typedef EventDeliverySystemPublisher PublisherType; + typedef typename PublisherType::EventListenerType EventListenerType; + typedef SharedPtr PublisherPtrType; + typedef std::list PublisherPtrContainerType; +}; + +class EventDeliverySystem + : private Noncopyable +{ +private: + typedef SharedPtr AbstractPublisherPtrType; + typedef std::list AbstractPublisherPtrContainerType; + typedef AbstractPublisherPtrContainerType::const_iterator AbstractPublisherPtrContainerTypeConstIterator; + typedef AbstractPublisherPtrContainerType::iterator AbstractPublisherPtrContainerTypeIterator; + typedef std::map AbstractPublisherPtrContainerMapType; + + template + class CrtDeleteCheck + { + private: + const AbstractPublisherPtrContainerType &m_publisherContainer; + + public: + CrtDeleteCheck(const AbstractPublisherPtrContainerType &publisherContainer) + : m_publisherContainer(publisherContainer) + { + } + + virtual ~CrtDeleteCheck() + { + Assert(m_publisherContainer.empty() && "All event delivery listeners must be removed before exit!"); + } + }; + + // Map containing publishers for all registered events + static AbstractPublisherPtrContainerMapType m_publishers; + + // Detail implementation: lazy initialized + static EventDeliverySystemDetail m_detailSystem; + + EventDeliverySystem() + { + } + + template + static AbstractPublisherPtrContainerType &GetPublisherContainerRef() + { + std::string typeId = typeid(EventType).name(); + + //FIXME: problem with linking per compilation unit won't hurt this check? + static CrtDeleteCheck crtDeleteCheck(m_publishers[typeId]); + (void)crtDeleteCheck; + + return m_publishers[typeId]; + } + + template + static typename EventDeliverySystemTraits::PublisherPtrContainerType GetPublisherContainer() + { + std::string typeId = typeid(EventType).name(); + typedef typename EventDeliverySystemTraits::PublisherPtrContainerType PublisherContainerType; + typedef typename EventDeliverySystemTraits::PublisherType PublisherType; + typedef typename EventDeliverySystemTraits::PublisherPtrType PublisherPtrType; + + AbstractPublisherPtrContainerType eventPublishers = m_publishers[typeId]; + PublisherContainerType publisherContainer; + + FOREACH(iterator, eventPublishers) + { + PublisherPtrType publisher = + StaticPointerCast(*iterator); + publisherContainer.push_back(publisher); + } + + return publisherContainer; + } + + // Detail access + template + static void Inject(const EventType &event) + { + typedef typename EventDeliverySystemTraits::PublisherPtrContainerType PublisherContainerType; + typedef typename EventDeliverySystemTraits::PublisherPtrType PublisherPtrType; + + PublisherContainerType publisherContainer = GetPublisherContainer(); + typename PublisherContainerType::iterator iterator; + + for (iterator = publisherContainer.begin(); iterator != publisherContainer.end(); ++iterator) + { + PublisherPtrType &publisher = *iterator; + publisher->PublishEvent(event); + } + } + + template + class PublisherPtrContainerListenerPredicate + { + public: + typedef EventListener EventListenerType; + + private: + EventListenerType *m_listener; + + public: + PublisherPtrContainerListenerPredicate(EventListenerType *listener) + : m_listener(listener) + { + } + + bool operator()(const AbstractPublisherPtrType &publisher) const + { + return StaticPointerCast::PublisherType>(publisher)->GetListener() == m_listener; + } + }; + + friend class EventDeliverySystemInjector; + +public: + virtual ~EventDeliverySystem() + { + } + + template + static void AddListener(EventListener *listener) + { + typedef typename EventDeliverySystemTraits::PublisherType PublisherType; + typedef typename EventDeliverySystemTraits::PublisherPtrType PublisherPtrType; + typedef typename EventDeliverySystemTraits::PublisherPtrContainerType PublisherPtrContainerType; + + typedef PublisherPtrContainerListenerPredicate PublisherPtrContainerListenerPredicateType; + + AbstractPublisherPtrContainerType &publisherContainer = GetPublisherContainerRef(); + AbstractPublisherPtrContainerTypeConstIterator iterator = + std::find_if(publisherContainer.begin(), publisherContainer.end(), PublisherPtrContainerListenerPredicateType(listener)); + + Assert(iterator == publisherContainer.end() && "Listener already exists"); + + // Add new publisher + publisherContainer.push_back(AbstractPublisherPtrType(new PublisherType(listener))); + + // When first listener is inserted, start to listen for events + if (publisherContainer.size() == 1) { + m_detailSystem.Listen(EventType()); + } + } + + template + static void RemoveListener(EventListener *listener) + { + typedef typename EventDeliverySystemTraits::PublisherType PublisherType; + typedef typename EventDeliverySystemTraits::PublisherPtrType PublisherPtrType; + typedef typename EventDeliverySystemTraits::PublisherPtrContainerType PublisherPtrContainerType; + + typedef PublisherPtrContainerListenerPredicate PublisherPtrContainerListenerPredicateType; + + AbstractPublisherPtrContainerType &publisherContainer = GetPublisherContainerRef(); + + AbstractPublisherPtrContainerTypeIterator iterator = + std::find_if(publisherContainer.begin(), publisherContainer.end(), PublisherPtrContainerListenerPredicateType(listener)); + + Assert(iterator != publisherContainer.end() && "Listener does not exist"); + + // Remove publisher + publisherContainer.erase(iterator); + + // When last listener was removed, stop to listen for events + if (publisherContainer.empty()) + m_detailSystem.Unlisten(EventType()); + } + + template + static void Publish(const EventType &event) + { + m_detailSystem.Publish(event); + } +}; + +} +} // namespace DPL + +#endif // DPL_EVENT_DELIVERY_H diff --git a/modules/event/include/dpl/event/event_delivery_detail.h b/modules/event/include/dpl/event/event_delivery_detail.h new file mode 100644 index 0000000..b5907d0 --- /dev/null +++ b/modules/event/include/dpl/event/event_delivery_detail.h @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file event_delivery_detail.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of event delivery EFL detail class + */ +#ifndef DPL_EVENT_DELIVERY_DETAIL_EFL_H +#define DPL_EVENT_DELIVERY_DETAIL_EFL_H + +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Event +{ + +class EventDeliverySystemDetail + : private Noncopyable +{ +private: + // Generic noti event prefixes + static const char *NOTI_EVENT_ID_GENERIC_0; + static const char *NOTI_EVENT_ID_GENERIC_1; + static const char *NOTI_EVENT_ID_GENERIC_2; + static const char *NOTI_EVENT_ID_GENERIC_3; + static const char *NOTI_EVENT_ID_GENERIC_4; + + // Other noti events + static const char *NOTI_EVENT_ID_NULL; + + // Main noti handle + int m_notiHandle; + + // Noti signaling class + class NotiSignal + { + private: + EventDeliverySystemDetail *m_receiver; + const char *m_gcid; + int m_sysnoti; + std::string m_eventName; + + public: + NotiSignal(EventDeliverySystemDetail *receiver, const char *gcid, const std::string &eventName) + : m_receiver(receiver), + m_gcid(gcid), + m_sysnoti(-1), + m_eventName(eventName) + { + Assert(receiver != NULL); + } + + NotiSignal(EventDeliverySystemDetail *receiver, int sysnoti) + : m_receiver(receiver), + m_gcid(NULL), + m_sysnoti(sysnoti) + { + Assert(receiver != NULL); + } + + const char *GetGcid() const + { + return m_gcid; + } + + int GetSysNoti() const + { + return m_sysnoti; + } + + std::string GetEventName() const + { + return m_eventName; + } + + EventDeliverySystemDetail *GetReceiver() const + { + return m_receiver; + } + }; + + // Registered noti singal list + typedef std::list NotiSignalList; + NotiSignalList m_notiSignalList; + + // setting library callbacks + class SettingSignal + { + private: + EventDeliverySystemDetail *m_receiver; + std::string m_key; + + public: + SettingSignal(EventDeliverySystemDetail *receiver, const std::string &key) + : m_receiver(receiver), + m_key(key) + { + } + + std::string GetKey() const + { + return m_key; + } + + EventDeliverySystemDetail *GetReceiver() const + { + return m_receiver; + } + }; + + typedef std::list SettingSignalList; + SettingSignalList m_settingSignalList; + + // Global noti callbacks + class GlobalNotiSignal + { + private: + EventDeliverySystemDetail *m_receiver; + std::string m_key; + + public: + GlobalNotiSignal(EventDeliverySystemDetail *receiver, const std::string &key) + : m_receiver(receiver), + m_key(key) + { + } + + std::string GetKey() const + { + return m_key; + } + + EventDeliverySystemDetail *GetReceiver() const + { + return m_receiver; + } + }; + + typedef std::list GlobalNotiSignalList; + GlobalNotiSignalList m_globalNotiSignalList; + + // Utilities + std::string ReadFile(const std::string &fileName) const; + std::string GetGenericNotiFilePath(const std::string &eventName) const; + + // Noti callback registration/unregistration + void RegisterFileNotiCallback(const char *gcid, const std::string &eventName); + void UnregisterFileNotiCallback(const std::string &eventName); + + void RegisterSettingCallback(const std::string &key); + void UnregisterSettingCallback(const std::string &key); + + void RegisterGlobalNotiCallback(const std::string &key); + void UnregisterGlobalNotiCallback(const std::string &key); + + // Emit noti signal + void SignalGenericNoti(const std::string &eventName, const std::string &contents) const; + + // Signals + static void OnNotiGlobalSignal(void *notiData); + static void OnSettingSignal(keynode_t *keyNode, void *userParam); + static void OnGlobalNotiSignal(void *globalNotiData); + + void OnNotiSignal(NotiSignal *notiSignal); + +public: + EventDeliverySystemDetail(); + virtual ~EventDeliverySystemDetail(); + + void Listen(const EventMessages::RoamingChanged &event); + void Listen(const EventMessages::NetworkTypeChanged &event); + void Listen(const EventMessages::HibernationEnter &event); + void Listen(const EventMessages::HibernationLeave &event); + + void Unlisten(const EventMessages::RoamingChanged &event); + void Unlisten(const EventMessages::NetworkTypeChanged &event); + void Unlisten(const EventMessages::HibernationEnter &event); + void Unlisten(const EventMessages::HibernationLeave &event); + + void Publish(const EventMessages::Generic0 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_0) + event.GetGcid(); + SignalGenericNoti(eventName, event.SerializeType()); + } + + template + void Publish(const EventMessages::Generic1 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_1) + event.GetGcid(); + SignalGenericNoti(eventName, event.SerializeType()); + } + + template + void Publish(const EventMessages::Generic2 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_2) + event.GetGcid(); + SignalGenericNoti(eventName, event.SerializeType()); + } + + template + void Publish(const EventMessages::Generic3 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_3) + event.GetGcid(); + SignalGenericNoti(eventName, event.SerializeType()); + } + + template + void Publish(const EventMessages::Generic4 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_4) + event.GetGcid(); + SignalGenericNoti(eventName, event.SerializeType()); + } + + void Listen(const EventMessages::Generic0 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_0) + event.GetGcid(); + RegisterFileNotiCallback(event.GetGcid(), eventName); + } + + template + void Listen(const EventMessages::Generic1 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_1) + event.GetGcid(); + RegisterFileNotiCallback(event.GetGcid(), eventName); + } + + template + void Listen(const EventMessages::Generic2 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_2) + event.GetGcid(); + RegisterFileNotiCallback(event.GetGcid(), eventName); + } + + template + void Listen(const EventMessages::Generic3 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_3) + event.GetGcid(); + RegisterFileNotiCallback(event.GetGcid(), eventName); + } + + template + void Listen(const EventMessages::Generic4 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_4) + event.GetGcid(); + RegisterFileNotiCallback(event.GetGcid(), eventName); + } + + void Unlisten(const EventMessages::Generic0 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_0) + event.GetGcid(); + UnregisterFileNotiCallback(eventName); + } + + template + void Unlisten(const EventMessages::Generic1 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_1) + event.GetGcid(); + UnregisterFileNotiCallback(eventName); + } + + template + void Unlisten(const EventMessages::Generic2 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_2) + event.GetGcid(); + UnregisterFileNotiCallback(eventName); + } + + template + void Unlisten(const EventMessages::Generic3 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_3) + event.GetGcid(); + UnregisterFileNotiCallback(eventName); + } + + template + void Unlisten(const EventMessages::Generic4 &event) + { + std::string eventName = std::string(NOTI_EVENT_ID_GENERIC_4) + event.GetGcid(); + UnregisterFileNotiCallback(eventName); + } +}; + +} +} // namespace DPL + +#endif // DPL_EVENT_DELIVERY_DETAIL_EFL_H diff --git a/modules/event/include/dpl/event/event_delivery_injector.h b/modules/event/include/dpl/event/event_delivery_injector.h new file mode 100644 index 0000000..546b4c3 --- /dev/null +++ b/modules/event/include/dpl/event/event_delivery_injector.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file event_delivery_injector.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of event delivery injector + */ +#ifndef DPL_EVENT_DELIVERY_INJECTOR_H +#define DPL_EVENT_DELIVERY_INJECTOR_H + +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Event +{ + +class EventDeliverySystemInjector + : private Noncopyable +{ +private: + EventDeliverySystemInjector() + { + } + + typedef void (*MetaCall)(const std::string &metaData); + typedef std::map MetaCallContainer; + MetaCallContainer m_metaCalls; + +public: + virtual ~EventDeliverySystemInjector() + { + } + + static EventDeliverySystemInjector &Instance() + { + static EventDeliverySystemInjector instance; + return instance; + } + + void PublishMetaType(const char *metaType, const std::string &metaData) + { + // Unwrap meta type (indirect Publish meta call) + m_metaCalls[metaType](metaData); + } + + template + void Publish(const EventMessageType &message) + { + EventDeliverySystem::Inject(message); + } + + void RegisterMetaCall(const char *metaType, MetaCall metaCall) + { + m_metaCalls.insert(std::make_pair(metaType, metaCall)); + } +}; + +} +} // namespace DPL + +#endif // DPL_EVENT_DELIVERY_INJECTOR_H diff --git a/modules/event/include/dpl/event/event_delivery_messages.h b/modules/event/include/dpl/event/event_delivery_messages.h new file mode 100644 index 0000000..046738d --- /dev/null +++ b/modules/event/include/dpl/event/event_delivery_messages.h @@ -0,0 +1,923 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file event_delivery_messages.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of event delivery messages + */ +#ifndef DPL_EVENT_DELIVERY_MESSAGES_H +#define DPL_EVENT_DELIVERY_MESSAGES_H + +#include +#include +#include +#include + +namespace DPL +{ +namespace Event +{ + +namespace EventMessages +{ +/** + * Generic event message base + * + * Custom events are derived from these templates + * Only template GenericN members are serialized + * + * Assumptions: + * + * 1. Arg* must be default constructible + * 2. For Arg* there must be defined << and >> operators + */ +class GenericBase +{ +private: + const char *m_gcid; ///< Global class ID + static const char SEPARATOR = '\254'; ///< Unique serialized data separator + static const size_t DESERIALIZE_STRING_FIELD_CHUNK_SIZE = 128; ///< Chunk size while parsing serialized event data + +protected: + template + void SerializeField(std::ostream &stream, const Type &type) const + { + stream << type; + } + + void SerializeSeparator(std::ostream &stream) const + { + stream << SEPARATOR; + } + + std::vector SplitStream(const std::string &stream) const + { + char separator[2] = {}; + separator[0] = SEPARATOR; + std::vector splitVector; + + char *splitTab = new char[stream.size() + 1]; + strcpy(splitTab, stream.c_str()); + + // Split serialized stream + char *savePtr; + char *splitPtr = strtok_r(splitTab, separator, &savePtr); + + while (splitPtr != NULL) + { + splitVector.push_back(std::string(splitPtr)); + splitPtr = strtok_r(NULL, separator, &savePtr); + } + + delete [] splitTab; + + return splitVector; + } + + template + Type DeserializeField(std::istream &stream, const Type &) const + { + Type type; + stream >> type; + return type; + } + + std::string DeserializeField(std::istream &stream, const std::string &) const + { + std::string result; + char buffer[DESERIALIZE_STRING_FIELD_CHUNK_SIZE]; + + for (;;) + { + stream.get(buffer, DESERIALIZE_STRING_FIELD_CHUNK_SIZE); + + if (stream.gcount() < 1) + break; + + result += std::string(buffer, buffer + stream.gcount()); + } + + return result; + } + +public: + /** + * Init constructor + */ + explicit GenericBase(const char *gcid) + : m_gcid(gcid) + { + } + + /** + * Destructor + */ + virtual ~GenericBase() + { + } + + const char *GetGcid() const { return m_gcid; } + + /** + * Serialize event n-tuple + * + * @return Ouput serialized stream + * @warning Internal use only + */ + virtual std::string SerializeType() const = 0; + + /** + * Deserialize event n-tuple + * + * @param[in] stream Input stream to deserialize from + * @warning Internal use only + */ + virtual void DeserializeType(const std::string &stream) = 0; +}; + +class Generic0 + : public GenericBase +{ +public: + /** + * Init constructor + */ + explicit Generic0(const char *Gcid) + : GenericBase(Gcid) + { + } + + /** + * Destructor + */ + virtual ~Generic0() + { + } + + /** + * Serialize event 0-tuple + * + * @return Ouput serialized stream + * @warning Internal use only + */ + virtual std::string SerializeType() const + { + return std::string(); + } + + /** + * Deserialize event 0-tuple + * + * @param[in] stream Input stream to deserialize from + * @warning Internal use only + */ + virtual void DeserializeType(const std::string &stream) + { + (void)stream; + } +}; + +template +class Generic1 + : public GenericBase +{ +public: + typedef Arg0Type Arg0; + +private: + Arg0 m_arg0; + +public: + /** + * Init constructor + */ + explicit Generic1(const char *Gcid) + : GenericBase(Gcid), + m_arg0() + { + } + + /** + * Full init constructor + */ + explicit Generic1(const char *Gcid, const Arg0 &arg0) + : GenericBase(Gcid), + m_arg0(arg0) + { + } + + /** + * Destructor + */ + virtual ~Generic1() + { + } + + /** + * Retrieve value of argument 0 from tuple + * + * @return Argument value + */ + const Arg0 &GetArg0() const + { + return m_arg0; + } + + /** + * Set value of argument 0 in tuple + * + * @param[in] value Argument value + */ + void SetArg0(const Arg0 &value) + { + m_arg0 = value; + } + + /** + * Serialize event 1-tuple + * + * @return Ouput serialized stream + * @warning Internal use only + */ + virtual std::string SerializeType() const + { + std::ostringstream s; + SerializeField(s, m_arg0); + return s.str(); + } + + /** + * Deserialize event 1-tuple + * + * @param[in] stream Input stream to deserialize from + * @warning Internal use only + */ + virtual void DeserializeType(const std::string &stream) + { + std::vector splitVector = SplitStream(stream); + if (splitVector.size() != 1) + return; + std::istringstream s0(splitVector[0]); + m_arg0 = DeserializeField(s0, Arg0()); + } +}; + +template +class Generic2 + : public GenericBase +{ +public: + typedef Arg0Type Arg0; + typedef Arg1Type Arg1; + +private: + Arg0 m_arg0; + Arg1 m_arg1; + +public: + /** + * Init constructor + */ + explicit Generic2(const char *Gcid) + : GenericBase(Gcid), + m_arg0(), + m_arg1() + { + } + + /** + * Full init constructor + */ + explicit Generic2(const char *Gcid, const Arg0 &arg0, const Arg1 &arg1) + : GenericBase(Gcid), + m_arg0(arg0), + m_arg1(arg1) + { + } + + /** + * Destructor + */ + virtual ~Generic2() + { + } + + /** + * Retrieve value of argument 0 from tuple + * + * @return Argument value + */ + const Arg0 &GetArg0() const + { + return m_arg0; + } + + /** + * Set value of argument 0 in tuple + * + * @param[in] value Argument value + */ + void SetArg0(const Arg0 &value) + { + m_arg0 = value; + } + + /** + * Retrieve value of argument 1 from tuple + * + * @return Argument value + */ + const Arg1 &GetArg1() const + { + return m_arg1; + } + + /** + * Set value of argument 1 in tuple + * + * @param[in] value Argument value + */ + void SetArg1(const Arg1 &value) + { + m_arg1 = value; + } + + /** + * Serialize event 2-tuple + * + * @return Ouput serialized stream + * @warning Internal use only + */ + virtual std::string SerializeType() const + { + std::ostringstream s; + SerializeField(s, m_arg0); + SerializeSeparator(s); + SerializeField(s, m_arg1); + return s.str(); + } + + /** + * Deserialize event 2-tuple + * + * @param[in] stream Input stream to deserialize from + * @warning Internal use only + */ + virtual void DeserializeType(const std::string &stream) + { + std::vector splitVector = SplitStream(stream); + if (splitVector.size() != 2) + return; + std::istringstream s0(splitVector[0]); + std::istringstream s1(splitVector[1]); + m_arg0 = DeserializeField(s0, Arg0()); + m_arg1 = DeserializeField(s1, Arg1()); + } +}; + +template +class Generic3 + : public GenericBase +{ +public: + typedef Arg0Type Arg0; + typedef Arg1Type Arg1; + typedef Arg2Type Arg2; + +private: + Arg0 m_arg0; + Arg1 m_arg1; + Arg2 m_arg2; + +public: + /** + * Init constructor + */ + explicit Generic3(const char *Gcid) + : GenericBase(Gcid), + m_arg0(), + m_arg1(), + m_arg2() + { + } + + /** + * Full init constructor + */ + explicit Generic3(const char *Gcid, const Arg0 &arg0, const Arg1 &arg1, const Arg2 &arg2) + : GenericBase(Gcid), + m_arg0(arg0), + m_arg1(arg1), + m_arg2(arg2) + { + } + + /** + * Destructor + */ + virtual ~Generic3() + { + } + + /** + * Retrieve value of argument 0 from tuple + * + * @return Argument value + */ + const Arg0 &GetArg0() const + { + return m_arg0; + } + + /** + * Set value of argument 0 in tuple + * + * @param[in] value Argument value + */ + void SetArg0(const Arg0 &value) + { + m_arg0 = value; + } + + /** + * Retrieve value of argument 1 from tuple + * + * @return Argument value + */ + const Arg1 &GetArg1() const + { + return m_arg1; + } + + /** + * Set value of argument 1 in tuple + * + * @param[in] value Argument value + */ + void SetArg1(const Arg1 &value) + { + m_arg1 = value; + } + + /** + * Retrieve value of argument 2 from tuple + * + * @return Argument value + */ + const Arg2 &GetArg2() const + { + return m_arg2; + } + + /** + * Set value of argument 2 in tuple + * + * @param[in] value Argument value + */ + void SetArg2(const Arg2 &value) + { + m_arg2 = value; + } + + /** + * Serialize event 3-tuple + * + * @return Ouput serialized stream + * @warning Internal use only + */ + virtual std::string SerializeType() const + { + std::ostringstream s; + SerializeField(s, m_arg0); + SerializeSeparator(s); + SerializeField(s, m_arg1); + SerializeSeparator(s); + SerializeField(s, m_arg2); + return s.str(); + } + + /** + * Deserialize event 3-tuple + * + * @param[in] stream Input stream to deserialize from + * @warning Internal use only + */ + virtual void DeserializeType(const std::string &stream) + { + std::vector splitVector = SplitStream(stream); + if (splitVector.size() != 3) + return; + std::istringstream s0(splitVector[0]); + std::istringstream s1(splitVector[1]); + std::istringstream s2(splitVector[2]); + m_arg0 = DeserializeField(s0, Arg0()); + m_arg1 = DeserializeField(s1, Arg1()); + m_arg2 = DeserializeField(s2, Arg2()); + } +}; + +template +class Generic4 + : public GenericBase +{ +public: + typedef Arg0Type Arg0; + typedef Arg1Type Arg1; + typedef Arg2Type Arg2; + typedef Arg3Type Arg3; + +private: + Arg0 m_arg0; + Arg1 m_arg1; + Arg2 m_arg2; + Arg3 m_arg3; + +public: + /** + * Init constructor + */ + explicit Generic4(const char *Gcid) + : GenericBase(Gcid), + m_arg0(), + m_arg1(), + m_arg2(), + m_arg3() + { + } + + /** + * Full init constructor + */ + explicit Generic4(const char *Gcid, const Arg0 &arg0, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) + : GenericBase(Gcid), + m_arg0(arg0), + m_arg1(arg1), + m_arg2(arg2), + m_arg3(arg3) + { + } + + /** + * Destructor + */ + virtual ~Generic4() + { + } + + /** + * Retrieve value of argument 0 from tuple + * + * @return Argument value + */ + const Arg0 &GetArg0() const + { + return m_arg0; + } + + /** + * Set value of argument 0 in tuple + * + * @param[in] value Argument value + */ + void SetArg0(const Arg0 &value) + { + m_arg0 = value; + } + + /** + * Retrieve value of argument 1 from tuple + * + * @return Argument value + */ + const Arg1 &GetArg1() const + { + return m_arg1; + } + + /** + * Set value of argument 1 in tuple + * + * @param[in] value Argument value + */ + void SetArg1(const Arg1 &value) + { + m_arg1 = value; + } + + /** + * Retrieve value of argument 2 from tuple + * + * @return Argument value + */ + const Arg2 &GetArg2() const + { + return m_arg2; + } + + /** + * Set value of argument 2 in tuple + * + * @param[in] value Argument value + */ + void SetArg2(const Arg2 &value) + { + m_arg2 = value; + } + + /** + * Retrieve value of argument 3 from tuple + * + * @return Argument value + */ + const Arg3 &GetArg3() const + { + return m_arg3; + } + + /** + * Set value of argument 3 in tuple + * + * @param[in] value Argument value + */ + void SetArg3(const Arg3 &value) + { + m_arg3 = value; + } + + /** + * Serialize event 4-tuple + * + * @return Ouput serialized stream + * @warning Internal use only + */ + virtual std::string SerializeType() const + { + std::ostringstream s; + SerializeField(s, m_arg0); + SerializeSeparator(s); + SerializeField(s, m_arg1); + SerializeSeparator(s); + SerializeField(s, m_arg2); + SerializeSeparator(s); + SerializeField(s, m_arg3); + return s.str(); + } + + /** + * Deserialize event 4-tuple + * + * @param[in] stream Input stream to deserialize from + * @warning Internal use only + */ + virtual void DeserializeType(const std::string &stream) + { + std::vector splitVector = SplitStream(stream); + if (splitVector.size() != 4) + return; + std::istringstream s0(splitVector[0]); + std::istringstream s1(splitVector[1]); + std::istringstream s2(splitVector[2]); + std::istringstream s3(splitVector[3]); + m_arg0 = DeserializeField(s0, Arg0()); + m_arg1 = DeserializeField(s1, Arg1()); + m_arg2 = DeserializeField(s2, Arg2()); + m_arg3 = DeserializeField(s3, Arg3()); + } +}; + +// Event declarators +#define EVENT_DELIVERY_DECLARE_EVENT_MESSAGE_0(EventType) \ + void EventType##_Inject(const std::string &metaData); \ + struct EventType##_StaticDeclarator \ + { \ + static char Name[0xFF]; \ + EventType##_StaticDeclarator(); \ + }; \ + class EventType : public DPL::Event::EventMessages::Generic0 \ + { \ + public: \ + EventType() : DPL::Event::EventMessages::Generic0(EventType##_StaticDeclarator::Name) {} \ + }; + +#define EVENT_DELIVERY_DECLARE_EVENT_MESSAGE_1(EventType, EventArg0Type) \ + void EventType##_Inject(const std::string &metaData); \ + struct EventType##_StaticDeclarator \ + { \ + static char Name[0xFF]; \ + EventType##_StaticDeclarator(); \ + }; \ + class EventType : public DPL::Event::EventMessages::Generic1 \ + { \ + public: \ + EventType() : DPL::Event::EventMessages::Generic1(EventType##_StaticDeclarator::Name) {} \ + EventType(const Arg0 &arg0) : DPL::Event::EventMessages::Generic1(EventType##_StaticDeclarator::Name, arg0) {} \ + }; + +#define EVENT_DELIVERY_DECLARE_EVENT_MESSAGE_2(EventType, EventArg0Type, EventArg1Type) \ + void EventType##_Inject(const std::string &metaData); \ + struct EventType##_StaticDeclarator \ + { \ + static char Name[0xFF]; \ + EventType##_StaticDeclarator(); \ + }; \ + class EventType : public DPL::Event::EventMessages::Generic2 \ + { \ + public: \ + EventType() : DPL::Event::EventMessages::Generic2(EventType##_StaticDeclarator::Name) {} \ + EventType(const Arg0 &arg0, const Arg1 &arg1) : DPL::Event::EventMessages::Generic2(EventType##_StaticDeclarator::Name, arg0, arg1) {} \ + }; + +#define EVENT_DELIVERY_DECLARE_EVENT_MESSAGE_3(EventType, EventArg0Type, EventArg1Type, EventArg2Type) \ + void EventType##_Inject(const std::string &metaData); \ + struct EventType##_StaticDeclarator \ + { \ + static char Name[0xFF]; \ + EventType##_StaticDeclarator(); \ + }; \ + class EventType : public DPL::Event::EventMessages::Generic3 \ + { \ + public: \ + EventType() : DPL::Event::EventMessages::Generic3(EventType##_StaticDeclarator::Name) {} \ + EventType(const Arg0 &arg0, const Arg1 &arg1, const Arg2 &arg2) : DPL::Event::EventMessages::Generic3(EventType##_StaticDeclarator::Name, arg0, arg1, arg2) {} \ + }; + +#define EVENT_DELIVERY_DECLARE_EVENT_MESSAGE_4(EventType, EventArg0Type, EventArg1Type, EventArg2Type, EventArg3Type) \ + void EventType##_Inject(const std::string &metaData); \ + struct EventType##_StaticDeclarator \ + { \ + static char Name[0xFF]; \ + EventType##_StaticDeclarator(); \ + }; \ + class EventType : public DPL::Event::EventMessages::Generic4 \ + { \ + public: \ + EventType() : DPL::Event::EventMessages::Generic4(EventType##_StaticDeclarator::Name) {} \ + EventType(const Arg0 &arg0, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) : DPL::Event::EventMessages::Generic4(EventType##_StaticDeclarator::Name, arg0, arg1, arg2, arg3) {} \ + }; + +#define EVENT_DELIVERY_IMPLEMENT_EVENT_MESSAGE(EventType) \ + char EventType##_StaticDeclarator::Name[0xFF] = {}; \ + void EventType##_Inject(const std::string &metaData) \ + { \ + EventType newType; \ + newType.DeserializeType(metaData); \ + DPL::Event::EventDeliverySystemInjector::Instance().Publish(newType); \ + } \ + EventType##_StaticDeclarator::EventType##_StaticDeclarator() \ + { \ + strcpy(Name, #EventType); \ + DPL::Event::EventDeliverySystemInjector::Instance().RegisterMetaCall(EventType##_StaticDeclarator::Name, EventType##_Inject); \ + } \ + namespace { EventType##_StaticDeclarator dummyInitializer_##EventType##_StaticDeclarator; } + +// BUILT-IN EVENTS +class RoamingChanged +{ +protected: + bool m_enabled; ///< Status whether roaming is enabled during call + +public: + /** + * Constructor + */ + RoamingChanged() + : m_enabled(false) + { + } + + /** + * Init constructor + */ + RoamingChanged(bool enabled) + : m_enabled(enabled) + { + } + + /** + * Destructor + */ + virtual ~RoamingChanged() + { + } + + /** + * Retrieve status whether roaming is enabled + * + * @return Current roaming status + */ + bool GetEnabled() const + { + return m_enabled; + } +}; + +class NetworkTypeChanged +{ +protected: + /** + * Status whether phone is working in: + * home network (true) + * roaming (false) + */ + bool m_homeNetwork; + +public: + /** + * Constructor + */ + NetworkTypeChanged() + : m_homeNetwork(true) + { + } + + /** + * Init constructor + */ + NetworkTypeChanged(bool homeNetwork) + : m_homeNetwork(homeNetwork) + { + } + + /** + * Destructor + */ + virtual ~NetworkTypeChanged() + { + } + + /** + * Retrieve status whether home network is active + * + * @return Current network status + */ + bool IsHomeNetwork() const + { + return m_homeNetwork; + } + + /** + * Retrieve status whether home network is active + * + * @return Current network status + */ + bool IsRoaming() const + { + return !m_homeNetwork; + } +}; + +class HibernationEnter +{ +public: + /** + * Constructor + */ + HibernationEnter() + { + } + + /** + * Destructor + */ + virtual ~HibernationEnter() + { + } +}; + +class HibernationLeave +{ +public: + /** + * Constructor + */ + HibernationLeave() + { + } + + /** + * Destructor + */ + virtual ~HibernationLeave() + { + } +}; + +} // namespace EventMessages +} +} // namespace DPL + +#endif // DPL_EVENT_DELIVERY_MESSAGES_H diff --git a/modules/event/include/dpl/event/event_listener.h b/modules/event/include/dpl/event/event_listener.h new file mode 100644 index 0000000..bc7ecbf --- /dev/null +++ b/modules/event/include/dpl/event/event_listener.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file event_listener.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of MVC event listener + */ +#ifndef DPL_EVENT_LISTENER_H +#define DPL_EVENT_LISTENER_H + +#include + +namespace DPL +{ +namespace Event +{ + +template +class EventListener + : private Noncopyable +{ +public: + EventListener() + { + } + + virtual ~EventListener() + { + } + + virtual void OnEventReceived(const EventType &event) = 0; +}; + +} +} // namespace DPL + +#endif // DPL_EVENT_LISTENER_H diff --git a/modules/event/include/dpl/event/event_support.h b/modules/event/include/dpl/event/event_support.h new file mode 100644 index 0000000..3aaab81 --- /dev/null +++ b/modules/event/include/dpl/event/event_support.h @@ -0,0 +1,788 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file event_support.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of MVC event support + */ +#ifndef DPL_EVENT_SUPPORT_H +#define DPL_EVENT_SUPPORT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Event +{ +namespace EmitMode +{ +enum Type +{ + Auto, ///< If calling thread is the same as receiver's use + ///< direct call, otherwise call is queued + + Queued, ///< Call is always queued + + Blocking, ///< If calling thread is the same as receiver's use + ///< direct call, otherwise call is queued and blocking + + Deffered ///< Call is always queued for a period of time +}; +} // namespace EmitMode + +template +class EventSupport + : private Noncopyable +{ +public: + typedef EventSupport EventSupportType; + + typedef EventListener EventListenerType; + typedef FastDelegate1 DelegateType; + + class EventSupportData; // Forward declaration + typedef EventSupportData *EventSupportDataPtr; + +private: + typedef typename GenericEventCall:: + template Rebind:: + Other GenericEventCallType; + + // Event listener list + typedef std::map EventListenerList; + EventListenerList m_eventListenerList; + + // Delegate list + typedef std::map DelegateList; + DelegateList m_delegateList; + + // Event support operation mutex + Mutex m_listenerDelegateMutex; + + // Dedicated instance of thread event dispatcher + ThreadEventDispatcher m_threadEventDispatcher; + + // Guard destruction of event support in event handler + Atomic m_guardedCallInProgress; + + // Events created by this support + typedef std::list EventCallList; + EventCallList m_eventsList; + + // Events list mutex + Mutex m_eventListMutex; + +public: + class EventSupportData + { + private: + typedef void (EventSupportType::*ReceiveAbstractEventCallMethod)( + const EventType &event, + EventListenerType *eventListener, + DelegateType delegate, + WaitableEvent *synchronization); + + EventSupportType *m_eventSupport; + ReceiveAbstractEventCallMethod m_method; + typename EventCallList::iterator m_iterator; + + //TODO: Add dispatcher iterator to remove events from + // framework/thread's event queue + WaitableEvent *m_synchronization; + + Mutex m_dataMutex; + + public: + EventSupportData(EventSupportType *support, + ReceiveAbstractEventCallMethod method, + WaitableEvent *synchronization) + : m_eventSupport(support), + m_method(method), + m_synchronization(synchronization) + { + } + + ~EventSupportData() + { + Mutex::ScopedLock lock(&m_dataMutex); + + if (!m_eventSupport) + { + LogPedantic("EventSupport for this call does not exist"); + return; + } + + m_eventSupport->RemoveEventCall(m_iterator); + } + + // TODO: Make private and make EventSupport friend + void SetIterator(typename EventCallList::iterator iter) + { + m_iterator = iter; + } + + // This method at the end destroys this as it will not be used anymore + void CallAndDestroy(const EventType &event, + EventListenerType *listener, + DelegateType delegate) + { + { + Mutex::ScopedLock lock(&m_dataMutex); + + if (m_eventSupport != NULL) + { + (*m_eventSupport.*m_method)(event, + listener, + delegate, + m_synchronization); + } + else + { + LogPedantic("EventSupport for this call does not " + "exist anymore. Ignored."); + } + + // releasing mutex lock + } + + // EventSupportData object is no more used. + // It can be safely destroyed now. + delete this; + } + + void Reset() + { + LogPedantic("Reseting my EventSupport"); + + Mutex::ScopedLock lock(&m_dataMutex); + m_eventSupport = NULL; + } + }; + +private: + GenericEventCallType *RegisterEventCall(const EventType &event, + EventListenerType *eventListener, + DelegateType delegate, + WaitableEvent *waitableEvent) + { + LogPedantic("Create and Register EventCall in EventSupport"); + + Mutex::ScopedLock lock(&m_eventListMutex); + + EventSupportDataPtr supportData = + new EventSupportData( + this, + &EventSupportType::ReceiveAbstractEventCall, + waitableEvent); + + GenericEventCallType *eventCall = + new GenericEventCallType(supportData, eventListener, + delegate, event); + + typename EventCallList::iterator eventCallIter = + m_eventsList.insert(m_eventsList.end(), eventCall); + + supportData->SetIterator(eventCallIter); + + return eventCall; + } + + void RemoveEventCall(typename EventCallList::iterator eventIterator) + { + Mutex::ScopedLock lock(&m_eventListMutex); + + LogPedantic("Removing event call from EventSupport"); + + m_eventsList.erase(eventIterator); + } + + // Note: Reentrant metod + void GuardedEventCall(const EventType &event, + EventListenerType *eventListener) + { + LogPedantic("Guarded event listener call..."); + + ++m_guardedCallInProgress; + + UNHANDLED_EXCEPTION_HANDLER_BEGIN + { + eventListener->OnEventReceived(event); + } + UNHANDLED_EXCEPTION_HANDLER_END + + --m_guardedCallInProgress; + + LogPedantic("Guarded event listener finished"); + } + + // Note: Reentrant metod + void GuardedEventCall(const EventType &event, + DelegateType delegate) + { + LogPedantic("Guarded delegate call..."); + + ++m_guardedCallInProgress; + + UNHANDLED_EXCEPTION_HANDLER_BEGIN + { + delegate(event); + } + UNHANDLED_EXCEPTION_HANDLER_END + + --m_guardedCallInProgress; + + LogPedantic("Guarded delegate call finished"); + } + + void ReceiveAbstractEventCall(const EventType &event, + EventListenerType *eventListener, + DelegateType delegate, + WaitableEvent *synchronization) + { + LogPedantic("Received abstract event call method"); + + Thread *targetThread; + + // Listener might have been removed, ensure that it still exits + if (eventListener != NULL) + { + Mutex::ScopedLock lock(&m_listenerDelegateMutex); + + typename EventListenerList::iterator iterator = + m_eventListenerList.find(eventListener); + + if (iterator == m_eventListenerList.end()) + { + LogPedantic("Abstract event call listener disappeared." + "Event ignored."); + + // Even though, synchronize caller if needed + if (synchronization != NULL) + synchronization->Signal(); + + return; + } + + // Get target thread id + targetThread = iterator->second; + } + else + { + // Delegate might have been removed, ensure that it still exits + Mutex::ScopedLock lock(&m_listenerDelegateMutex); + + typename DelegateList::iterator iterator = + m_delegateList.find(delegate); + + if (iterator == m_delegateList.end()) + { + LogPedantic("Abstract event call delegate disappeared." + "Event ignored."); + + // Even though, synchronize caller if needed + if (synchronization != NULL) + synchronization->Signal(); + + return; + } + + // Get target thread id + targetThread = iterator->second; + } + + // Ensure that we are now in proper thread now + if (targetThread != Thread::GetCurrentThread()) + { + LogPedantic("Detected event dispatching ping-pong scenario"); + + // Retry if it was not synchronized + if (synchronization == NULL) + { + // Cheat with event delivery + EmitEvent(event, EmitMode::Queued); + + LogPedantic("Ping-Pong: Resent as queued event"); + } + else + { + // There is a problem + // Developer did something nasty, and we will not clean up his mess + synchronization->Signal(); + + LogPedantic("### Ping-Pong: Failed to deliver synchronized" + "event in ping-pong scenario!"); + } + + return; + } + + // Guard listener code for exceptions + if (eventListener != NULL) + GuardedEventCall(event, eventListener); + else + GuardedEventCall(event, delegate); + + // Release caller if synchronizing + if (synchronization != NULL) + synchronization->Signal(); + } + +protected: + void EmitEvent(const EventType &event, + EmitMode::Type mode = EmitMode::Queued, + double dueTime = 0.0) + { + // Emit event, and retrieve later in current context to dispatch + ScopedPtr lock( + new Mutex::ScopedLock(&m_listenerDelegateMutex)); + + // Show some info + switch (mode) + { + case EmitMode::Auto: + LogPedantic("Emitting AUTO event..."); + break; + + case EmitMode::Queued: + LogPedantic("Emitting QUEUED event..."); + break; + + case EmitMode::Blocking: + LogPedantic("Emitting BLOCKING event..."); + break; + + case EmitMode::Deffered: + LogPedantic("Emitting DEFFERED event..."); + break; + + default: + break; + } + + // In some configurations there is a barrier + std::vector synchronizationBarrier; + + // Emit to all listeners + FOREACH(iterator, m_eventListenerList) + { + // Switch to proper dispatcher and emit event + AbstractEventDispatcher *dispatcher = NULL; + + if (iterator->second == NULL) + { + // Send to main thread + LogPedantic("Sending event to main dispatcher"); + dispatcher = &GetMainEventDispatcherInstance(); + } + else + { + // Setup thread dispatcher, and send to proper thread + LogPedantic("Sending event to thread dispatcher"); + m_threadEventDispatcher.SetThread(iterator->second); + dispatcher = &m_threadEventDispatcher; + } + + // Dispatch event to abstract dispatcher + WaitableEvent *synchronization; + + // TODO: Pool synchronization objects + switch (mode) + { + case EmitMode::Auto: + // Check thread + if (iterator->second == Thread::GetCurrentThread()) + { + // Guard listener code for exceptions + GuardedEventCall(event, iterator->first); + } + else + { + // Handle non-synchronized event + dispatcher->AddEventCall( + RegisterEventCall(event, iterator->first, + DelegateType(), NULL)); + } + break; + + case EmitMode::Queued: + // Handle non-synchronized event + dispatcher->AddEventCall( + RegisterEventCall(event, iterator->first, + DelegateType(), NULL)); + + break; + + case EmitMode::Blocking: + // Check thread + if (iterator->second == Thread::GetCurrentThread()) + { + // Guard listener code for exceptions + GuardedEventCall(event, iterator->first); + } + else + { + // New synchronization object is needed + synchronization = new WaitableEvent(); + + // Handle synchronized event + dispatcher->AddEventCall( + RegisterEventCall(event, iterator->first, + DelegateType(), synchronization)); + + // Add to barrier + synchronizationBarrier.push_back(synchronization); + } + break; + + case EmitMode::Deffered: + // Handle deffered events + Assert(dueTime >= 0.0 && "Due time must be non-negative"); + + dispatcher->AddTimedEventCall( + RegisterEventCall(event, iterator->first, + DelegateType(), NULL), dueTime); + + break; + + default: + Assert("Invalid emit mode"); + } + } + + LogPedantic("Added event to dispatchers"); + + // Emit to all delegates + FOREACH(iterator, m_delegateList) + { + // Switch to proper dispatcher and emit event + AbstractEventDispatcher *dispatcher = NULL; + + if (iterator->second == NULL) + { + // Send to main thread + LogPedantic("Sending event to main dispatcher"); + dispatcher = &GetMainEventDispatcherInstance(); + } + else + { + // Setup thread dispatcher, and send to proper thread + LogPedantic("Sending event to thread dispatcher"); + m_threadEventDispatcher.SetThread(iterator->second); + dispatcher = &m_threadEventDispatcher; + } + + // Dispatch event to abstract dispatcher + WaitableEvent *synchronization; + + // TODO: Pool synchronization objects + switch (mode) + { + case EmitMode::Auto: + // Check thread + if (iterator->second == Thread::GetCurrentThread()) + { + // Guard listener code for exceptions + GuardedEventCall(event, iterator->first); + } + else + { + // Handle non-synchronized event + dispatcher->AddEventCall( + RegisterEventCall(event, + NULL, + iterator->first, + NULL)); + } + break; + + case EmitMode::Queued: + // Handle non-synchronized event + dispatcher->AddEventCall( + RegisterEventCall(event, + NULL, + iterator->first, + NULL)); + + break; + + case EmitMode::Blocking: + // Check thread + if (iterator->second == Thread::GetCurrentThread()) + { + // Guard listener code for exceptions + GuardedEventCall(event, iterator->first); + } + else + { + // New synchronization object is needed + synchronization = new WaitableEvent(); + + // Handle synchronized event + dispatcher->AddEventCall( + RegisterEventCall(event, + NULL, + iterator->first, + synchronization)); + + // Add to barrier + synchronizationBarrier.push_back(synchronization); + } + break; + + case EmitMode::Deffered: + // Handle deffered events + Assert(dueTime >= 0.0 && "Due time must be non-negative"); + + dispatcher->AddTimedEventCall( + RegisterEventCall(event, + NULL, + iterator->first, + NULL), dueTime); + + break; + + default: + Assert("Invalid emit mode"); + } + } + + LogPedantic("Added event to dispatchers"); + + // Leave listeners lock in case of blocking call + if (!synchronizationBarrier.empty()) + { + LogPedantic("Leaving lock due to existing barrier"); + lock.Reset(); + } + + LogPedantic("Size of barrier: " << synchronizationBarrier.size()); + + // Synchronize with barrier + // TODO: Implement generic WaitForAllMultipleHandles call + while (!synchronizationBarrier.empty()) + { + // Get barrier waitable handles + WaitableHandleList barrierHandles; + + FOREACH(iterator, synchronizationBarrier) + barrierHandles.push_back((*iterator)->GetHandle()); + + // Await more events + WaitableHandleIndexList indexList = + WaitForMultipleHandles(barrierHandles); + + // Remove all awaited handles + // TODO: Return handles to pool + FOREACH(iterator, indexList) + { + // Delete object + delete synchronizationBarrier[*iterator]; + + // Zero out place + synchronizationBarrier[*iterator] = NULL; + } + + // Now clean up + std::vector clearedSynchronizationBarrier; + + FOREACH(iterator, synchronizationBarrier) + { + if (*iterator == NULL) + continue; + + clearedSynchronizationBarrier.push_back(*iterator); + } + + synchronizationBarrier.swap(clearedSynchronizationBarrier); + + LogPedantic("Reduced size of barrier: " + << synchronizationBarrier.size()); + } + + LogPedantic("Event emitted"); + } + +public: + EventSupport() + : m_guardedCallInProgress(false) + { + } + + virtual ~EventSupport() + { + Assert(m_guardedCallInProgress == false); + + m_eventListenerList.clear(); + m_delegateList.clear(); + + Mutex::ScopedLock lock(&m_eventListMutex); + + LogPedantic("Disabling events for EventSupport"); + + FOREACH(iterator, m_eventsList) + (*iterator)->DisableEvent(); + } + + void AddListener(EventListenerType *eventListener) + { + Mutex::ScopedLock lock(&m_listenerDelegateMutex); + + // Listener must not be NULL + Assert(eventListener != NULL); + + // Listener must not already exists + Assert(m_eventListenerList.find(eventListener) + == m_eventListenerList.end()); + + // Add new listener, inherit dispatcher from current context + m_eventListenerList.insert( + std::make_pair(eventListener, Thread::GetCurrentThread())); + + // Done + LogPedantic("Listener registered"); + } + + void AddListener(DelegateType delegate) + { + Mutex::ScopedLock lock(&m_listenerDelegateMutex); + + // Delegate must not be empty + Assert(!delegate.empty()); + + // Delegate must not already exists + Assert(m_delegateList.find(delegate) == m_delegateList.end()); + + // Add new delegate, inherit dispatcher from current context + m_delegateList.insert( + std::make_pair(delegate, Thread::GetCurrentThread())); + + // Done + LogPedantic("Delegate registered"); + } + + void RemoveListener(EventListenerType *eventListener) + { + Mutex::ScopedLock lock(&m_listenerDelegateMutex); + + // Listener must not be NULL + Assert(eventListener != NULL); + + // Listener must exist + typename EventListenerList::iterator iterator = + m_eventListenerList.find(eventListener); + + Assert(iterator != m_eventListenerList.end()); + + // Remove listener from list + m_eventListenerList.erase(iterator); + LogPedantic("Listener unregistered"); + } + + void RemoveListener(DelegateType delegate) + { + Mutex::ScopedLock lock(&m_listenerDelegateMutex); + + // Delegate must not be empty + Assert(!delegate.empty()); + + // Delegate must exist + typename DelegateList::iterator iterator = + m_delegateList.find(delegate); + + Assert(iterator != m_delegateList.end()); + + // Remove delegate from list + m_delegateList.erase(iterator); + LogPedantic("Delegate unregistered"); + } + + void SwitchListenerToThread(EventListenerType *eventListener, + Thread *thread) + { + Mutex::ScopedLock lock(&m_listenerDelegateMutex); + + // Listener must not be NULL + Assert(eventListener != NULL); + + // Listener must exist + typename EventListenerList::iterator iterator = + m_eventListenerList.find(eventListener); + + Assert(iterator != m_eventListenerList.end()); + + // Set listener thread + iterator->second = thread; + + LogPedantic("Listener switched"); + } + + void SwitchListenerToThread(DelegateType delegate, + Thread *thread) + { + Mutex::ScopedLock lock(&m_listenerDelegateMutex); + + // Delegate must not be empty + Assert(!delegate.empty()); + + // Delegate must exist + typename EventListenerList::iterator iterator = + m_delegateList.find(delegate); + + Assert(iterator != m_delegateList.end()); + + // Set delegate thread + iterator->second = thread; + + LogPedantic("Delegate switched"); + } + + void SwitchAllListenersToThread(Thread *thread) + { + Mutex::ScopedLock lock(&m_listenerDelegateMutex); + + // Switch all listeners and delegates + FOREACH(iterator, m_eventListenerList) + iterator->second = thread; + + FOREACH(iterator, m_delegateList) + iterator->second = thread; + + LogPedantic("All listeners and delegates switched"); + } +}; + +} +} // namespace DPL + +#endif // DPL_EVENT_SUPPORT_H diff --git a/modules/event/include/dpl/event/generic_event_call.h b/modules/event/include/dpl/event/generic_event_call.h new file mode 100644 index 0000000..7c0567c --- /dev/null +++ b/modules/event/include/dpl/event/generic_event_call.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_event_call.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of generic event call + */ +#ifndef DPL_GENERIC_EVENT_CALL_H +#define DPL_GENERIC_EVENT_CALL_H + +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Event +{ + +template +class GenericEventCall + : public AbstractEventCall +{ +public: + typedef EventListener EventListenerType; + typedef FastDelegate1 DelegateType; + +protected: + SupportDataType m_supportData; + EventListenerType *m_eventListener; + DelegateType m_delegate; + EventType m_event; + +public: + template + struct Rebind + { + typedef GenericEventCall Other; + }; + + GenericEventCall(SupportDataType supportData, + EventListenerType *eventListener, + DelegateType delegate, + const EventType &event) + : m_supportData(supportData), + m_eventListener(eventListener), + m_delegate(delegate), + m_event(event) + { + } + + virtual ~GenericEventCall() + { + Assert(m_supportData == NULL && + "Call method hasn't been called" + " (support data wasn't destroyed)"); + } + + virtual void Call() + { + LogPedantic("Calling generic event call"); + + m_supportData->CallAndDestroy(m_event, m_eventListener, m_delegate); + + // Now m_supportData points to invalid object. Marking it as NULL. + m_supportData = NULL; + + LogPedantic("Generic event called"); + } + + virtual void DisableEvent() + { + LogPedantic("Disabling this EventCall"); + m_supportData->Reset(); + + // TODO: In the future, event should be completely removed + // from the event queue (not just marked "disabled") + } +}; + +} +} // namespace DPL + +#endif // DPL_GENERIC_EVENT_CALL_H diff --git a/modules/event/include/dpl/event/inter_context_delegate.h b/modules/event/include/dpl/event/inter_context_delegate.h new file mode 100644 index 0000000..55af651 --- /dev/null +++ b/modules/event/include/dpl/event/inter_context_delegate.h @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file inter_context_delegate.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief This file is the header file of inter context delegate + */ + +#ifndef DPL_INTER_CONTEXT_DELEGATE_H_ +#define DPL_INTER_CONTEXT_DELEGATE_H_ + +#ifndef __GXX_EXPERIMENTAL_CXX0X__ // C++11 compatibility check +# include +#else + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * - Created ICDelegate can be passed freely to other threads. + * - ICDelegate can be called just once. All following calls will be + * silently ignored. + * - When ICDelegateSupport is destroyed, all its ICDelegates + * are invalidated and safetly removed. + * - ICDelegate can be invalidated by call to disable method on it. + * - To use ICDelegate you have to do two steps: + * + * 1. Class that will be used to create delegate have to derive from templated + * class ICDelegateSupport + * + * 2. Create instance of ICDelegate by calling + * makeICDelegate and passing to it pointer to method + * + * class A : ICDelegateSupport + * { + * void methodA(int) {} + * void createDelegate() { + * ICDelegate dlg; + * dlg = makeICDelegate(&A::MethodA); + * }; + */ + +namespace DPL { +namespace Event { + +//forward declaration +template +class ICDelegate; + +namespace ICD{ +// This Type defines whether ICDelegate should be destroyed after the call, or +// could be reused later. +enum class Reuse{ Yes, No }; +} + +namespace ICDPrivate { +// Base for all ICDSharedDatas. Needed for auto disabling and deleting of +// ICDSharedDatas. +// If ICDSharedData is disabled, delegate won't be called anymore. +class ICDSharedDataBase +{ + public: + typedef DPL::SharedPtr ICDSharedDataBasePtr; + typedef std::list ICDSharedDataBaseList; + + class ScopedLock : DPL::Noncopyable + { + public: + explicit ScopedLock(ICDSharedDataBasePtr helperBase) : + m_scopedLock(&helperBase->m_mutex) + { + } + + private: + DPL::RecursiveMutex::ScopedLock m_scopedLock; + }; + + ICDSharedDataBase() : m_disabled(false) + { + } + virtual ~ICDSharedDataBase() + { + } + + bool isDisabled() const + { + return m_disabled; + } + virtual void disable() + { + m_disabled = true; + } + + void setIterator(ICDSharedDataBaseList::iterator pos) + { + m_position = pos; + } + + ICDSharedDataBaseList::iterator getIterator() const + { + return m_position; + } + + private: + bool m_disabled; + DPL::RecursiveMutex m_mutex; + ICDSharedDataBaseList::iterator m_position; +}; + +// Pure Event to remove ICDSharedData. +class DeleteICDSharedDataBaseEventCall : public DPL::Event::AbstractEventCall +{ + public: + DeleteICDSharedDataBaseEventCall( + ICDSharedDataBase::ICDSharedDataBasePtr helperBase) : + m_helperBase(helperBase) + { + } + virtual void Call() + { + m_helperBase.Reset(); + } + + private: + ICDSharedDataBase::ICDSharedDataBasePtr m_helperBase; +}; + + +class ICDelegateSupportInterface +{ + protected: + virtual ~ICDelegateSupportInterface() + { + } + virtual void unregisterICDSharedData( + ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper) = 0; + virtual void registerICDSharedData( + ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper) = 0; + private: + template + friend class DPL::Event::ICDelegate; +}; +} //ICDPrivate + +// Better makeDelegate then DPL::MakeDelegate +template +FastDelegate +makeDelegate(ThisType* This, + void (ThisType::*Func)(ArgTypesList ...)) +{ + return FastDelegate(This, Func); +} + +// ICDelegate class represents delegate that can be called from +// any context (thread). The actual calling context (thread) is allways the same +// as the context in which it was created. +template +class ICDelegate +{ + public: + ICDelegate() + { + } + ICDelegate(ICDPrivate::ICDelegateSupportInterface* base, + DPL::FastDelegate outerDelegate, + ICD::Reuse reuse) + { + ICDSharedData* hlp = new ICDSharedData(base, outerDelegate, reuse); + m_helper.Reset(hlp); + } + + // Calling operator will pass all args passed to it to correct context and + // will call apropriate method that was registered with. + void operator()(ArgTypesList ... args) + { + Assert(m_helper); + ICDPrivate::ICDSharedDataBase::ScopedLock lock( + DPL::StaticPointerCast(m_helper)); + m_helper->CallDelegate(args ...); + } + + //Disable delegate (it won't be called anymore) + void disable() + { + Assert(m_helper); + ICDPrivate::ICDSharedDataBase::ScopedLock lock( + DPL::StaticPointerCast(m_helper)); + m_helper->disable(); + } + + protected: + ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr + getRelatedICDSharedData() const + { + return DPL::StaticPointerCast(m_helper); + } + + private: + template + friend class ICDelegateSupport; + class ICDSharedData; + typedef DPL::SharedPtr ICDSharedDataPtr; + + struct PrivateEvent + { + PrivateEvent(ICDSharedDataPtr a_helper, + ArgTypesList ... arguments) : + helper(a_helper), + args(std::make_tuple(arguments ...)) + { + } + + ICDSharedDataPtr helper; + std::tuple args; + }; + + typedef DPL::FastDelegate + ICDSharedDataDelegateType; + class ICDSharedData : private DPL::Event::EventSupport, + private DPL::EnableSharedFromThis, + public ICDPrivate::ICDSharedDataBase + { + public: + ICDSharedData( + ICDPrivate::ICDelegateSupportInterface *base, + DPL::FastDelegate outerDelegate, + ICD::Reuse reuse) : + m_base(base), + m_outerDelegate(outerDelegate), + m_reuse(reuse) + { + Assert(m_base); + // lock is not needed: this object is not shared at that moment + m_subDelegate = + DPL::Event::makeDelegate(this, + &ICDSharedData::delegateForwarder); + EventSupport::AddListener(m_subDelegate); + } + + void CallDelegate(ArgTypesList ... args) + { + ICDPrivate::ICDSharedDataBase::ScopedLock lock( + DPL::StaticPointerCast( + this->SharedFromThis())); + if (!isDisabled()) { + EmitEvent(PrivateEvent(this->SharedFromThis(), + args ...)); + } + } + + virtual void disable() + { + ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr ptr( + DPL::StaticPointerCast( + this->SharedFromThis())); + ICDPrivate::ICDSharedDataBase::ScopedLock lock(ptr); + if (!isDisabled()) { + ICDPrivate::ICDSharedDataBase::disable(); + EventSupport::RemoveListener(m_subDelegate); + m_base->unregisterICDSharedData(ptr); + } + } + + private: + friend class DPL::SharedPtr; + ICDSharedDataDelegateType m_subDelegate; + ICDPrivate::ICDelegateSupportInterface* m_base; + DPL::FastDelegate m_outerDelegate; + ICD::Reuse m_reuse; + + void delegateForwarder(const PrivateEvent& event) + { + ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr ptr( + DPL::StaticPointerCast(event.helper)); + ICDPrivate::ICDSharedDataBase::ScopedLock lock(ptr); + + Assert(!m_outerDelegate.empty()); + if (ptr->isDisabled()) { + LogPedantic("ICDSharedData has been disabled - call is ignored"); + } else { + DPL::Apply(m_outerDelegate, event.args); + + if(m_reuse == ICD::Reuse::Yes) + return; + + disable(); + deleteICDSharedDataBase(ptr); + } + } + }; + + // Schedules helper removal. + static void deleteICDSharedDataBase(ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper) + { + using namespace ICDPrivate; + ICDSharedDataBase::ScopedLock lock(helper); + DeleteICDSharedDataBaseEventCall* event = + new DeleteICDSharedDataBaseEventCall(helper); + if (DPL::Thread::GetCurrentThread() == NULL) { + DPL::Event::GetMainEventDispatcherInstance().AddEventCall(event); + } else { + DPL::Event::ThreadEventDispatcher dispatcher; + dispatcher.SetThread(DPL::Thread::GetCurrentThread()); + dispatcher.AddEventCall(event); + } + } + + ICDSharedDataPtr m_helper; +}; + +template +class ICDelegateSupport : public ICDPrivate::ICDelegateSupportInterface +{ + protected: + template + ICDelegate makeICDelegate( + void (ThisType::*Func)(ArgTypesList ...), + ICD::Reuse reuse = ICD::Reuse::No) + { + ThisType* This = static_cast(this); + ICDelegate icdelegate( + This, + makeDelegate(This, Func), + reuse); + this->registerICDSharedData(icdelegate.getRelatedICDSharedData()); + return icdelegate; + } + + ICDelegateSupport() + { + } + + ~ICDelegateSupport() + { + ICDPrivate::ICDSharedDataBase::ICDSharedDataBaseList list = + m_ICDSharedDatas; + FOREACH(helper, list) { + ICDPrivate::ICDSharedDataBase::ScopedLock lock( + DPL::StaticPointerCast(*helper)); + (*helper)->disable(); + } + m_ICDSharedDatas.clear(); + } + + private: + virtual void unregisterICDSharedData( + ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper) + { + m_ICDSharedDatas.erase(helper->getIterator()); + } + + virtual void registerICDSharedData( + ICDPrivate::ICDSharedDataBase::ICDSharedDataBasePtr helper) + { + helper->setIterator( + m_ICDSharedDatas.insert(m_ICDSharedDatas.begin(), + helper)); + } + + private: + ICDPrivate::ICDSharedDataBase::ICDSharedDataBaseList m_ICDSharedDatas; +}; + +} +} //namespace + +#endif // __GXX_EXPERIMENTAL_CXX0X__ + +#endif //DPL_INTER_CONTEXT_DELEGATE_H_ diff --git a/modules/event/include/dpl/event/main_event_dispatcher.h b/modules/event/include/dpl/event/main_event_dispatcher.h new file mode 100644 index 0000000..97145f6 --- /dev/null +++ b/modules/event/include/dpl/event/main_event_dispatcher.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file main_event_dispatcher.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of main event dispatcher for EFL + */ +#ifndef DPL_MAIN_EVENT_DISPATCHER_H +#define DPL_MAIN_EVENT_DISPATCHER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Event +{ + +class MainEventDispatcher + : public AbstractEventDispatcher +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, CreateFailed) + DECLARE_EXCEPTION_TYPE(Base, AddEventFailed) + DECLARE_EXCEPTION_TYPE(Base, AddTimedEventFailed) + }; + +protected: + struct WrappedEventCall + { + AbstractEventCall *abstractEventCall; + bool timed; + double dueTime; + + WrappedEventCall(AbstractEventCall *abstractEventCallArg, + bool timedArg, + double dueTimeArg) + : abstractEventCall(abstractEventCallArg), + timed(timedArg), + dueTime(dueTimeArg) + { + } + }; + + typedef std::list WrappedEventCallList; + + // Cross thread send support + WrappedEventCallList m_wrappedCrossEventCallList; + Mutex m_crossEventCallMutex; + WaitableEvent m_crossEventCallInvoker; + + Ecore_Event_Handler *m_eventCallHandler; + Ecore_Fd_Handler *m_crossEventCallHandler; + + int m_eventId; + + // Timed event support + struct TimedEventStruct + { + AbstractEventCall *abstractEventCall; + MainEventDispatcher *This; + + TimedEventStruct(AbstractEventCall *abstractEventCallArg, + MainEventDispatcher *ThisArg) + : abstractEventCall(abstractEventCallArg), + This(ThisArg) + { + } + }; + + void InternalAddEvent(AbstractEventCall *abstractEventCall, bool timed, double dueTime); + + static void StaticDeleteEvent(void *data, void *event); + static Eina_Bool StaticDispatchEvent(void *data, int type, void *event); + static Eina_Bool StaticDispatchTimedEvent(void *event); + static Eina_Bool StaticDispatchCrossInvoker(void *data, Ecore_Fd_Handler *fd_handler); + + void DeleteEvent(AbstractEventCall *abstractEventCall); + void DispatchEvent(AbstractEventCall *abstractEventCall); + void DispatchTimedEvent(AbstractEventCall *abstractEventCall); + void DispatchCrossInvoker(); + +public: + explicit MainEventDispatcher(); + virtual ~MainEventDispatcher(); + + virtual void AddEventCall(AbstractEventCall *abstractEventCall); + virtual void AddTimedEventCall(AbstractEventCall *abstractEventCall, double dueTime); +}; + +MainEventDispatcher& GetMainEventDispatcherInstance(); + +} +} // namespace DPL + +#endif // DPL_MAIN_EVENT_DISPATCHER_H diff --git a/modules/event/include/dpl/event/model.h b/modules/event/include/dpl/event/model.h new file mode 100644 index 0000000..07c0340 --- /dev/null +++ b/modules/event/include/dpl/event/model.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file model.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Header file for model + */ +#ifndef DPL_MODEL_H +#define DPL_MODEL_H + +#include +#include + +namespace DPL +{ +namespace Event +{ + +class Model + : public Noncopyable +{ +protected: + mutable DPL::ReadWriteMutex m_mutex; + + template + friend class PropertyBase; + + template + friend class Property; + +public: + virtual ~Model() = 0; +}; + +} +} // namespace DPL + +#endif // DPL_MODEL_H diff --git a/modules/event/include/dpl/event/model_bind_to_dao.h b/modules/event/include/dpl/event/model_bind_to_dao.h new file mode 100644 index 0000000..51473e8 --- /dev/null +++ b/modules/event/include/dpl/event/model_bind_to_dao.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file model_bind_to_dao.h + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef DPL_MODEL_BIND_TO_DAO_H_ +#define DPL_MODEL_BIND_TO_DAO_H_ + +namespace DPL +{ +namespace Event +{ + +/** + * @param ObjectType type of object used as delegate argument + * @param RetType Type returned from the external function + * @param ExtArg Type of argument required by external fun + * @param getterFun Object Type method which returns value of type ExtArg + * used as argument for external function + * */ +//STATIC FUNCTION +template < + typename ObjectType, + typename ValueType, + typename ExtArg, + ExtArg(ObjectType::*argGetter) () const, + ValueType(*externalGetter) (ExtArg) + > +struct BindToDAO_Static +{ + static ValueType Get(DPL::Event::Model* obj) + { + ObjectType* instance = static_cast(obj); + + return externalGetter((instance->*argGetter)()); + } +}; + +template < + typename ObjectType, + typename ValueType, + typename ExtArg, + typename ExtObject, + ExtArg(ObjectType::*argGetter) () const, + ValueType(ExtObject::*externalGetter) () const + > +struct BindToDAO +{ + static ValueType Get(DPL::Event::Model* obj) + { + ObjectType* instance = static_cast(obj); + ExtObject extObject((instance->*argGetter)()); + return (extObject.*externalGetter)(); + } +}; + +} +} + +#endif diff --git a/modules/event/include/dpl/event/nested_loop.h b/modules/event/include/dpl/event/nested_loop.h new file mode 100644 index 0000000..7138c8a --- /dev/null +++ b/modules/event/include/dpl/event/nested_loop.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file contains the declaration of the NestedLoopManager class. + * + * @file nested_loop.h + * @author Lukasz Marek (l.marek@samsung.com) + * @version 0.1 + * @brief This file contains the declaration of the NestedLoopManager class. + */ + +#ifndef DPL_NESTED_LOOP_H_ +#define DPL_NESTED_LOOP_H_ + +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Event +{ + +class LoopExitEvent +{ +}; + +typedef unsigned LoopHandle; +typedef std::list LoopHandleList; + +const LoopHandle UNDEFINED_LOOP_HANDLE = 0; + +class NestedLoopManager : + protected Controller::Type> +{ + public: + void* begin(LoopHandle loopHandle); + void exit(LoopHandle loopHandle, + void *userParam = NULL); + size_t getLevel() const; + LoopHandle getNewHandle(); + + protected: + void OnEventReceived(const LoopExitEvent& event); + + private: + struct LoopInformation + { + LoopInformation(LoopHandle handle) : + loopHandle(handle), + exitFlag(false), + userParam(NULL) + { + } + + LoopHandle loopHandle; + bool exitFlag; + void *userParam; + }; + + class RunningLoopsHandlePredicate + { + public: + RunningLoopsHandlePredicate(LoopHandle handle) : + m_handle(handle) + { + } + + bool operator()(const LoopInformation &loopInformation) const + { + return loopInformation.loopHandle == m_handle; + } + + private: + LoopHandle m_handle; + }; + + NestedLoopManager(); + ~NestedLoopManager(); + + typedef std::vector RunningLoopsList; + typedef RunningLoopsList::iterator RunningLoopsListIterator; + + bool m_eventGuard; // only one event is allowed + LoopHandle m_handle; + RunningLoopsList m_runningLoops; + + friend class Singleton; +}; + +typedef Singleton NestedLoopManagerSingleton; + +} +} // namespace DPL + +#endif // WRT_ENGINE_SRC_COMMON_NESTED_LOOP_H_ diff --git a/modules/event/include/dpl/event/property.h b/modules/event/include/dpl/event/property.h new file mode 100644 index 0000000..33da4df --- /dev/null +++ b/modules/event/include/dpl/event/property.h @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file property.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Header file for property + */ +#ifndef DPL_PROPERTY_H +#define DPL_PROPERTY_H + +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Event +{ + +/** + * Property is a class that encapsulates model's property fields. + * Its main purpose is to automate things related to model's properties + * such as: data storage, synchronization and event emitting. + * + * Property is a template of the following schema: + * + * template class Property + * + * Type is an internal type that property is encapsulating. It is required + * that the type is default-constructible and copyable. + * + * Property access rights control which operations are allowed to be + * executed on property. + * + * Property storage mode is a mode describing where and how internal data should + * be stored. + * + * Property modifiers: + * + * PropertyStorageCached: The data is stored internally as one copy. It can + * never be changed from external. + * + * PropertyStorageDynamic: The data is stored remotely and is accessed via + * provided delegates. It can change at any time + * if external mechanism changes its value. + * + * PropertyStorageDynamicCached: The data is stored internally, but only after + * it has been retrieved by delegates. The + * changed data is stored internally and also set + * remotely with delegate. After the value has + * been received from external source, it will + * never be updated externally. + * + * Property access modes: + * + * PropertyReadOnly: Property is a read-only property. + * It doesn't have Set() method. + * + * PropertyReadWrite: Property is a read-write property. It have both Get() + * and Set() methods. + * + * Examples: + * + * Note: All properties, if not specified otherwise in template arguments, + * have read-write access rights and cached data storage. + * + * Simple property with int: + * @code DPL::Property Number; + * + * A property with string: + * @code DPL::Property Name; + * + * A read-only float property: + * @code DPL::Property Gravity; + * + * A read-write string property: + * @code DPL::Property Caption; + * + * A read-write string property which is stored internally: + * @code DPL::Property Name; + * + * A read-write string property which is stored externally: + * @code DPL::Property RemoteName; + * + * A read-write string property which is stored externally, but also cached: + * @code DPL::Property CachedName; + * + * A model is an agregation of many properties. Whenever some of them change, + * an event with this change is emmited along with model handle which + * contains this property. These changes can be listened to. To achieve this, + * one have to add a delegate which contains changed property value and + * a pointer to the model. + * + * Example of a model with a property: + * + * @code + * class MyModel: public DPL::Model + * { + * public: + * DPL::Property Number1; + * DPL::Property Number2; + * DPL::Property Number3; + * + * // Read write property delegate method can be static + * static int ReadCustomValue(Model *model); + * + * // Read write property delegate method can also be method + * void WriteCustomValue(const int &value, Model *model); + * + * MyModel() + * : // Initialize property with default value and this model + * Number1(this), + * + * // Initialize property with 123 and this model + * Number2(this, 123), + * + * // Initialize property with 0 and custom read delegate and this model + * Number3(this, 0, &ReadCustomValue), + * + * // Initialize property with 7 and custom read-write delegates + * // and this model + * Number4(this, 7, &ReadCustomValue, + * DPL::MakeDelegate(this, &WriteCustomValue) + * { + * } + * }; + * + * DPL's delegate mechanism is a general solution, which is capable of + * binding various types of functions and method as a delegate and + * using them in unified way. + * + * Example of registering and unregistering for model's property changes: + * + * @code + * class SomeModel : public DPL::Model + * { + * public: + * DPL::Property Value; + * + * SomeModel() + * : Value(this) + * { + * } + * }; + * + * void ValueChanged(const int &value, Model *model) + * { + * std::cout << "Value changed to: " << value << std::endl; + * } + * + * int main() + * { + * SomeModel model; + * + * // Register a global function as property changed listener + * model.AddListener(&ValueChanged); + * [...] + * model.RemoveListener(&ValueChanged); + * + * // Register a class method as property changed listener + * class Receiver + * { + * public: + * void OnValueChanged(const int &value, Model *model) + * { + * [...] + * } + * } receiver; + * + * model.AddListener( + * DPL::MakeDelegate(&receiver, &Receiver::OnValueChanged)); + * [...] + * model.RemoveListener( + * DPL::MakeDelegate(&receiver, &Receiver::OnValueChanged)); + * } + */ +struct PropertyStorageCached {}; ///< Always use cached +struct PropertyStorageDynamic {}; ///< Always use dynamic +struct PropertyStorageDynamicCached {}; ///< Use dynamic then cache + +struct PropertyReadOnly {}; ///< Read only, not setter available +struct PropertyReadWrite {}; ///< Read and write + + +template +struct PropertyEvent +{ + PropertyEvent(const Type &v, Model *s) + : value(v), + sender(s) + { + } + + Type value; + Model *sender; +}; + +template +class PropertyStorageMethodDynamicBase +{ +protected: + ReadDelegateType m_readValue; + WriteDelegateType m_writeValue; + + PropertyStorageMethodDynamicBase(ReadDelegateType readValue, + WriteDelegateType writeValue) + : m_readValue(readValue), + m_writeValue(writeValue) + { + } +}; + +template +class PropertyStorageMethodCachedBase +{ +protected: + mutable Type m_value; + + PropertyStorageMethodCachedBase() + { + } +}; + +class PropertyStorageMethodBase +{ +protected: + explicit PropertyStorageMethodBase(Model *model) + : m_model(model) + { + } + + Model *m_model; +}; + +template +class PropertyStorageMethod; + +template +class PropertyStorageMethod + : protected PropertyStorageMethodBase, + protected PropertyStorageMethodCachedBase +{ +public: + PropertyStorageMethod(Model *model, + ReadDelegateType /*readValue*/, + WriteDelegateType /*writeValue*/) + : PropertyStorageMethodBase(model) + { + } + + Type Get() const + { + return this->m_value; + } + + void Set(const Type &value) + { + this->m_value = value; + } +}; + +template +class PropertyStorageMethod + : protected PropertyStorageMethodBase, + protected PropertyStorageMethodDynamicBase +{ +public: + PropertyStorageMethod(Model *model, + ReadDelegateType readValue, + WriteDelegateType writeValue) + : PropertyStorageMethodBase(model), + PropertyStorageMethodDynamicBase( + readValue, + writeValue) + { + } + + Type Get() const + { + Assert(!this->m_readValue.empty()); + return this->m_readValue(m_model); + } + + void Set(const Type &value) + { + Assert(!this->m_writeValue.empty()); + this->m_writeValue(value, m_model); + } +}; + +template +class PropertyStorageMethod + : protected PropertyStorageMethodBase, + protected PropertyStorageMethodDynamicBase, + protected PropertyStorageMethodCachedBase +{ +private: + typedef PropertyStorageMethod ThisType; + + // These two are mutable + void OnceEnsure() const + { + this->m_value = this->m_readValue(m_model); + } + + void OnceDisable() const + { + } + +protected: + mutable Once m_once; + +public: + PropertyStorageMethod(Model *model, + ReadDelegateType readValue, + WriteDelegateType writeValue) + : PropertyStorageMethodBase(model), + PropertyStorageMethodDynamicBase( + readValue, writeValue) + { + } + + Type Get() const + { + Assert(!this->m_readValue.empty()); + m_once.Call(Once::Delegate(this, &ThisType::OnceEnsure)); + return this->m_value; + } + + void Set(const Type &value) + { + Assert(!this->m_writeValue.empty()); + + this->m_writeValue(value, m_model); + this->m_value = value; + m_once.Call(Once::Delegate(this, &ThisType::OnceDisable)); + } +}; + +template +class PropertyBase + : protected EventSupport > +{ +public: + typedef typename EventSupport >::EventListenerType + EventListenerType; + + typedef typename EventSupport >::DelegateType + DelegateType; + + typedef FastDelegate + ReadDelegateType; + + typedef FastDelegate + WriteDelegateType; + +protected: + PropertyStorageMethod m_storage; + Model *m_model; + + PropertyBase(Model *model, + ReadDelegateType readValue, + WriteDelegateType writeValue) + : m_storage(model, readValue, writeValue), + m_model(model) + { + } + +public: + virtual Type Get() const + { + ReadWriteMutex::ScopedReadLock lock(&m_model->m_mutex); + return m_storage.Get(); + } + + void AddListener(DelegateType delegate) + { + EventSupport >::AddListener(delegate); + } + + void RemoveListener(DelegateType delegate) + { + EventSupport >::RemoveListener(delegate); + } +}; + +template +class Property; + +template +class Property + : public PropertyBase +{ +public: + typedef typename PropertyBase::EventListenerType + EventListenerType; + + typedef typename PropertyBase::DelegateType + DelegateType; + + typedef typename PropertyBase::ReadDelegateType + ReadDelegateType; + + typedef typename PropertyBase::WriteDelegateType + WriteDelegateType; + +public: + explicit Property(Model *model, + ReadDelegateType readValue = NULL) + : PropertyBase(model, readValue, NULL) + { + } + + Property(Model *model, + const Type &value, + ReadDelegateType readValue = NULL) + : PropertyBase(model, readValue, NULL) + { + this->m_storage.Set(value); + } +}; + +template +class Property + : public PropertyBase +{ +public: + typedef typename PropertyBase::EventListenerType + EventListenerType; + + typedef typename PropertyBase::DelegateType + DelegateType; + + typedef typename PropertyBase::ReadDelegateType + ReadDelegateType; + + typedef typename PropertyBase::WriteDelegateType + WriteDelegateType; + +public: + explicit Property(Model *model, + ReadDelegateType readValue = NULL, + WriteDelegateType writeValue = NULL) + : PropertyBase(model, readValue, writeValue) + { + } + + Property(Model *model, + const Type &value, + ReadDelegateType readValue = NULL, + WriteDelegateType writeValue = NULL) + : PropertyBase(model, readValue, writeValue) + { + this->m_storage.Set(value); + } + + virtual void Set(const Type &value) + { + ReadWriteMutex::ScopedWriteLock lock(&this->m_model->m_mutex); + + if (this->m_storage.Get() == value) + return; + + this->m_storage.Set(value); + + EmitEvent(PropertyEvent(value, this->m_model), + EmitMode::Auto); + } +}; + +} +} // namespace DPL + +#endif // DPL_PROPERTY_H diff --git a/modules/event/include/dpl/event/thread_event_dispatcher.h b/modules/event/include/dpl/event/thread_event_dispatcher.h new file mode 100644 index 0000000..e5e81a4 --- /dev/null +++ b/modules/event/include/dpl/event/thread_event_dispatcher.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file thread_event_dispatcher.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of thread event dispatcher + */ +#ifndef DPL_THREAD_EVENT_DISPATCHER_H +#define DPL_THREAD_EVENT_DISPATCHER_H + +#include +#include +#include + +namespace DPL +{ +namespace Event +{ + +class ThreadEventDispatcher + : public AbstractEventDispatcher +{ +protected: + Thread *m_thread; + + static void StaticEventDelete(void *event, void *userParam); + static void StaticEventDispatch(void *event, void *userParam); + + void EventDelete(AbstractEventCall *abstractEventCall); + void EventDispatch(AbstractEventCall *abstractEventCall); + +public: + explicit ThreadEventDispatcher(); + virtual ~ThreadEventDispatcher(); + + void SetThread(Thread *thread); + + virtual void AddEventCall(AbstractEventCall *abstractEventCall); + virtual void AddTimedEventCall(AbstractEventCall *abstractEventCall, double dueTime); +}; + +} +} // namespace DPL + +#endif // DPL_THREAD_EVENT_DISPATCHER_H diff --git a/modules/event/src/abstract_event_call.cpp b/modules/event/src/abstract_event_call.cpp new file mode 100644 index 0000000..093e5b5 --- /dev/null +++ b/modules/event/src/abstract_event_call.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_event_call.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of abstract event call + */ +#include + +namespace DPL +{ +namespace Event +{ + +AbstractEventCall::AbstractEventCall() +{ +} + +AbstractEventCall::~AbstractEventCall() +{ +} + +} +} // namespace DPL diff --git a/modules/event/src/abstract_event_dispatcher.cpp b/modules/event/src/abstract_event_dispatcher.cpp new file mode 100644 index 0000000..043e452 --- /dev/null +++ b/modules/event/src/abstract_event_dispatcher.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_event_dispatcher.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of abstract event dispatcher + */ +#include + +namespace DPL +{ +namespace Event +{ + +AbstractEventDispatcher::AbstractEventDispatcher() +{ +} + +AbstractEventDispatcher::~AbstractEventDispatcher() +{ +} + +} +} // namespace DPL diff --git a/modules/event/src/controller.cpp b/modules/event/src/controller.cpp new file mode 100644 index 0000000..abb126e --- /dev/null +++ b/modules/event/src/controller.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file controller.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of MVC controller + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/event/src/event_delivery.cpp b/modules/event/src/event_delivery.cpp new file mode 100644 index 0000000..4a3dacc --- /dev/null +++ b/modules/event/src/event_delivery.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file wrt_event_delivery.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of event delivery class + */ +#include +#include + +namespace DPL +{ +namespace Event +{ + +EventDeliverySystemDetail EventDeliverySystem::m_detailSystem; +EventDeliverySystem::AbstractPublisherPtrContainerMapType EventDeliverySystem::m_publishers; + +namespace // anonymous +{ +int InitializeMainSingleton(); + +int g_dummyInitializer = InitializeMainSingleton(); + +int InitializeMainSingleton() +{ + (void)g_dummyInitializer; + MainSingleton::Instance(); + return 0; +} +} // namespace anonymous + +AbstractEventDeliverySystemPublisher::~AbstractEventDeliverySystemPublisher() { } + +} +} // namespace DPL diff --git a/modules/event/src/event_delivery_detail.cpp b/modules/event/src/event_delivery_detail.cpp new file mode 100644 index 0000000..9c7e493 --- /dev/null +++ b/modules/event/src/event_delivery_detail.cpp @@ -0,0 +1,568 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file wrt_event_delivery_detail.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of event delivery EFL detail class + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace // anonymous +{ +const char *GENERIC_NOTI_ROOT_PATH = "/tmp/noti/generic"; +} // namespace anonymous + +namespace DPL +{ +namespace Event +{ + +const char *EventDeliverySystemDetail::NOTI_EVENT_ID_GENERIC_0 = "GenericEvent0_"; +const char *EventDeliverySystemDetail::NOTI_EVENT_ID_GENERIC_1 = "GenericEvent1_"; +const char *EventDeliverySystemDetail::NOTI_EVENT_ID_GENERIC_2 = "GenericEvent2_"; +const char *EventDeliverySystemDetail::NOTI_EVENT_ID_GENERIC_3 = "GenericEvent3_"; +const char *EventDeliverySystemDetail::NOTI_EVENT_ID_GENERIC_4 = "GenericEvent4_"; + +// Global noti signal names +const char *GlobalNotiHibernationEnterSignal = "HIBERNATION_ENTER"; +const char *GlobalNotiHibernationLeaveSignal = "HIBERNATION_LEAVE"; + +EventDeliverySystemDetail::EventDeliverySystemDetail() +{ + // Open noti subsystem + LogInfo("Initializing core_util_noti subsystem..."); + + m_notiHandle = heynoti_init(); + Assert(m_notiHandle != -1); + + // Assume that there is ECORE environment running, attach noti library to it + LogPedantic("Attaching core_util_noti subsystem to ECORE..."); + + int ret = heynoti_attach_handler(m_notiHandle); + Assert(ret != -1); + + (void)ret; + + LogInfo("core_util_noti subsystem successfuly initialized."); +} + +EventDeliverySystemDetail::~EventDeliverySystemDetail() +{ + // All signal handlers should be deattached + Assert(m_notiSignalList.empty()); + Assert(m_settingSignalList.empty()); + Assert(m_globalNotiSignalList.empty()); + + // Detach noti library from ECORE environment + LogPedantic("Detaching core_util_noti subsystem from ECORE..."); + + int ret = heynoti_detach_handler(m_notiHandle); + Assert(ret != -1); + + (void)ret; + + // Close noti sybsystem + LogInfo("Deinitializing core_util_noti subsystem..."); + + Assert(m_notiHandle != -1); + heynoti_close(m_notiHandle); + + LogInfo("core_util_noti subsystem successfuly deinitialized."); +} + +std::string EventDeliverySystemDetail::ReadFile(const std::string &fileName) const +{ + LogPedantic("Reading event file: " << fileName << "..."); + + std::ifstream fileStream(fileName.c_str(), std::ios::binary); + + if (fileStream.fail()) + return std::string(""); + + fileStream.seekg (0, std::ios::end); + std::streamoff length = fileStream.tellg(); + fileStream.seekg (0, std::ios::beg); + + ScopedArray buffer(new char[length]); + fileStream.read(buffer.Get(), static_cast(length)); + + if (fileStream.fail()) + return std::string(); + + LogPedantic("Successfuly read: " << fileName << "."); + + return std::string(buffer.Get(), buffer.Get() + length); +} + +void EventDeliverySystemDetail::OnNotiGlobalSignal(void *notiData) +{ + Assert(notiData); + NotiSignal *notiSignal = static_cast(notiData); + notiSignal->GetReceiver()->OnNotiSignal(notiSignal); +} + +void EventDeliverySystemDetail::OnSettingSignal(keynode_t *keyNode, void *userParam) +{ + SettingSignal *settingSignal = static_cast(userParam); + (void)settingSignal; + + LogPedantic("Got setting signal for key: " << settingSignal->GetKey()); + + std::string key = vconf_keynode_get_name(keyNode); + + if (key == VCONFKEY_SETAPPL_STATE_DATA_ROAMING_BOOL) + { + int value = vconf_keynode_get_bool(keyNode); + EventMessages::RoamingChanged message(value > 0); + EventDeliverySystemInjector::Instance().Publish(message); + } + else if (key == VCONFKEY_TELEPHONY_SVC_ROAM) + { + int result = 0; + if(vconf_get_int(VCONFKEY_TELEPHONY_SVC_ROAM, &result) != 0) + { + LogPedantic("Cannot get current roaming status"); + return; + } + else + { + bool homeNetwork = (result != VCONFKEY_TELEPHONY_SVC_ROAM_ON); + EventMessages::NetworkTypeChanged message1(homeNetwork); + EventDeliverySystemInjector::Instance().Publish(message1); + } + } + else + { + LogPedantic("Unexpected setting signal key: " << key << "!"); + return; + } +} + +void EventDeliverySystemDetail::OnGlobalNotiSignal(void *globalNotiData) +{ + GlobalNotiSignal *globalNotiSignal = static_cast(globalNotiData); + + LogPedantic("Got global noti signal for key: " << globalNotiSignal->GetKey()); + + std::string key = globalNotiSignal->GetKey(); + + if (key == GlobalNotiHibernationEnterSignal) + { + // We need to disconnect from VCONF + for (SettingSignalList::iterator iterator = globalNotiSignal->GetReceiver()->m_settingSignalList.begin(); + iterator != globalNotiSignal->GetReceiver()->m_settingSignalList.end(); ++iterator) + { + // Unegister from VCONF signals + int ret = vconf_ignore_key_changed(iterator->GetKey().c_str(), OnSettingSignal); + + if (ret == -1) + LogPedantic("Failed to unlisten setting: " << key << ", errno = " << errno << "."); + } + + // Publish + EventMessages::HibernationEnter message; + EventDeliverySystemInjector::Instance().Publish(message); + } + else if (key == GlobalNotiHibernationLeaveSignal) + { + // We need to reconnect to VCONF + for (SettingSignalList::iterator iterator = globalNotiSignal->GetReceiver()->m_settingSignalList.begin(); + iterator != globalNotiSignal->GetReceiver()->m_settingSignalList.end(); ++iterator) + { + // Register for VCONF signals + int ret = vconf_notify_key_changed(iterator->GetKey().c_str(), OnSettingSignal, &*iterator); + + if (ret == -1) + LogPedantic("Failed to listen setting: key = " << key << ", errno = " << errno << "."); + } + + // Publish + EventMessages::HibernationLeave message; + EventDeliverySystemInjector::Instance().Publish(message); + } + else + { + LogPedantic("Unexpected global noti signal key: " << key << "!"); + return; + } +} + +void EventDeliverySystemDetail::OnNotiSignal(NotiSignal *notiSignal) +{ + LogPedantic("Got core_util_noti signal."); + + if (notiSignal->GetEventName().substr(0, strlen(NOTI_EVENT_ID_GENERIC_0)) == NOTI_EVENT_ID_GENERIC_0) + { + LogPedantic("GeneticEvent0 message signal."); + + EventDeliverySystemInjector::Instance().PublishMetaType(notiSignal->GetGcid(), ReadFile(GetGenericNotiFilePath(notiSignal->GetEventName()))); + } + else if (notiSignal->GetEventName().substr(0, strlen(NOTI_EVENT_ID_GENERIC_1)) == NOTI_EVENT_ID_GENERIC_1) + { + LogPedantic("GeneticEvent1 message signal."); + + EventDeliverySystemInjector::Instance().PublishMetaType(notiSignal->GetGcid(), ReadFile(GetGenericNotiFilePath(notiSignal->GetEventName()))); + } + else if (notiSignal->GetEventName().substr(0, strlen(NOTI_EVENT_ID_GENERIC_2)) == NOTI_EVENT_ID_GENERIC_2) + { + LogPedantic("GeneticEvent2 message signal."); + + EventDeliverySystemInjector::Instance().PublishMetaType(notiSignal->GetGcid(), ReadFile(GetGenericNotiFilePath(notiSignal->GetEventName()))); + } + else if (notiSignal->GetEventName().substr(0, strlen(NOTI_EVENT_ID_GENERIC_3)) == NOTI_EVENT_ID_GENERIC_3) + { + LogPedantic("GeneticEvent3 message signal."); + + EventDeliverySystemInjector::Instance().PublishMetaType(notiSignal->GetGcid(), ReadFile(GetGenericNotiFilePath(notiSignal->GetEventName()))); + } + else if (notiSignal->GetEventName().substr(0, strlen(NOTI_EVENT_ID_GENERIC_4)) == NOTI_EVENT_ID_GENERIC_4) + { + LogPedantic("GeneticEvent4 message signal."); + + EventDeliverySystemInjector::Instance().PublishMetaType(notiSignal->GetGcid(), ReadFile(GetGenericNotiFilePath(notiSignal->GetEventName()))); + } +} + +void EventDeliverySystemDetail::RegisterFileNotiCallback(const char *gcid, const std::string &eventName) +{ + LogPedantic("Registering core_util_noti callback: gcid = " << (gcid ? gcid : "") << ", eventName = " << eventName << "."); + Assert(m_notiHandle != -1); + + // Check if already registered for signal + NotiSignalList::const_iterator iterator; + + for (iterator = m_notiSignalList.begin(); iterator != m_notiSignalList.end(); ++iterator) + { + if (iterator->GetEventName() == eventName) + return; + } + + // Make real noti path + std::string filePath = GetGenericNotiFilePath(eventName); + + LogPedantic("Listening file: " << filePath << "."); + + // New noti signal + m_notiSignalList.push_back(NotiSignal(this, gcid, eventName)); + NotiSignal *notiSignal = &m_notiSignalList.back(); + + // Register for noti signals + int ret = heynoti_subscribe_file(m_notiHandle, filePath.c_str(), OnNotiGlobalSignal, notiSignal, IN_CLOSE_WRITE); + + if (ret == -1) + { + LogPedantic("Failed to listen file: " << filePath << "."); + return; + } + + LogPedantic("Successfuly registered listening file: " << filePath << "."); +} + +void EventDeliverySystemDetail::UnregisterFileNotiCallback(const std::string &eventName) +{ + LogPedantic("Unegistering core_util_noti callback: eventName = " << eventName << "."); + + Assert(m_notiHandle != -1); + + NotiSignalList::iterator iterator; + + for (iterator = m_notiSignalList.begin(); iterator != m_notiSignalList.end(); ++iterator) + { + if (iterator->GetEventName() == eventName) + break; + } + + if (iterator == m_notiSignalList.end()) + return; + + // Remove registered slot + m_notiSignalList.erase(iterator); + + // Make real noti path + std::string filePath = GetGenericNotiFilePath(eventName); + + LogPedantic("Unlistening file: " << filePath << "."); + + // Register for noti signals + int ret = heynoti_unsubscribe_file(m_notiHandle, filePath.c_str(), OnNotiGlobalSignal); + + if (ret == -1) + { + LogPedantic("Failed to unlisten file: " << filePath << "."); + return; + } +} + +void EventDeliverySystemDetail::RegisterSettingCallback(const std::string &key) +{ + LogPedantic("Registering setting: key = " << key << "."); + + // Check if already registered for signal + SettingSignalList::const_iterator iterator; + + for (iterator = m_settingSignalList.begin(); iterator != m_settingSignalList.end(); ++iterator) + { + if (iterator->GetKey() == key) + return; + } + + // New noti signal + m_settingSignalList.push_back(SettingSignal(this, key)); + SettingSignal *settingSignal = &m_settingSignalList.back(); + + // Register for setting signals + int ret = vconf_notify_key_changed(key.c_str(), OnSettingSignal, settingSignal); + + if (ret == -1) + { + LogPedantic("Failed to listen setting: key = " << key << ", errno = " << errno << "."); + return; + } + + LogPedantic("Successfuly registered setting: key = " << key << "."); +} + +void EventDeliverySystemDetail::UnregisterSettingCallback(const std::string &key) +{ + LogPedantic("Unegistering setting: key = " << key << "."); + + SettingSignalList::iterator iterator; + + for (iterator = m_settingSignalList.begin(); iterator != m_settingSignalList.end(); ++iterator) + { + if (iterator->GetKey() == key) + break; + } + + if (iterator == m_settingSignalList.end()) + return; + + // Remove registered slot + m_settingSignalList.erase(iterator); + + // Register for noti signals + int ret = vconf_ignore_key_changed(key.c_str(), OnSettingSignal); + + if (ret == -1) + { + LogPedantic("Failed to unlisten setting: " << key << ", errno = " << errno << "."); + return; + } +} + +void EventDeliverySystemDetail::RegisterGlobalNotiCallback(const std::string &key) +{ + LogPedantic("Registering global noti: key = " << key << "."); + + // Check if already registered for signal + GlobalNotiSignalList::const_iterator iterator; + + for (iterator = m_globalNotiSignalList.begin(); iterator != m_globalNotiSignalList.end(); ++iterator) + { + if (iterator->GetKey() == key) + return; + } + + // New noti signal + m_globalNotiSignalList.push_back(GlobalNotiSignal(this, key)); + GlobalNotiSignal *globalNotiSignal = &m_globalNotiSignalList.back(); + + // Register for global noti signals + int ret = heynoti_subscribe(m_notiHandle, key.c_str(), OnGlobalNotiSignal, globalNotiSignal); + + if (ret == -1) + { + LogPedantic("Failed to listen global noti: key = " << key << ", errno = " << errno << "."); + return; + } + + LogPedantic("Successfuly registered global noti: key = " << key << "."); +} + +void EventDeliverySystemDetail::UnregisterGlobalNotiCallback(const std::string &key) +{ + LogPedantic("Unegistering global noti: key = " << key << "."); + + GlobalNotiSignalList::iterator iterator; + + for (iterator = m_globalNotiSignalList.begin(); iterator != m_globalNotiSignalList.end(); ++iterator) + { + if (iterator->GetKey() == key) + break; + } + + if (iterator == m_globalNotiSignalList.end()) + return; + + // Remove registered slot + m_globalNotiSignalList.erase(iterator); + + // Register for noti signals + int ret = heynoti_unsubscribe(m_notiHandle, key.c_str(), OnGlobalNotiSignal); + + if (ret == -1) + { + LogPedantic("Failed to unlisten global noti: " << key << ", errno = " << errno << "."); + return; + } +} + +void EventDeliverySystemDetail::SignalGenericNoti(const std::string &eventName, const std::string &contents) const +{ + LogPedantic("Signaling core_util_noti with: eventName = " << eventName << ", buffer = " << contents << "."); + + Assert(m_notiHandle != -1); + + // Make real noto path + std::string filePath = GetGenericNotiFilePath(eventName); + + LogPedantic("Signaling file: " << filePath << "."); + + // Emit noti signal with user data + int fd = open(filePath.c_str(), O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); + + if (fd == -1) + { + LogPedantic("Failed to signal file: " << filePath << "."); + return; + } + + // Write contents of signal to file + std::string::size_type written = 0; + + while (written < contents.size()) + { + int done = TEMP_FAILURE_RETRY(write(fd, static_cast(&contents[written]), contents.size() - written)); + + if (done <= 0) + { + LogPedantic("File stream broken: " << filePath << "."); + break; + } + + written += done; + } + + // Close signal file + int ret = close(fd); + + if (ret == -1) + { + LogPedantic("Failed to close signal file: " << filePath << "."); + return; + } +} + +std::string EventDeliverySystemDetail::GetGenericNotiFilePath(const std::string &eventName) const +{ + // Make sure that the path exists + if (access(GENERIC_NOTI_ROOT_PATH, F_OK) == -1) + { + LogPedantic("No root path found. Creating root path..."); + + // Create path + if (mkdir(GENERIC_NOTI_ROOT_PATH, 0755) == -1) + { + LogPedantic("Failed to create root path!"); + return std::string("~") + eventName; + } + + LogPedantic("Root path created."); + } + + return std::string(GENERIC_NOTI_ROOT_PATH) + "/" + eventName; +} + +void EventDeliverySystemDetail::Listen(const EventMessages::RoamingChanged &event) +{ + LogPedantic("RoamingChanged message listen."); + + (void)event; + RegisterSettingCallback(VCONFKEY_SETAPPL_STATE_DATA_ROAMING_BOOL); +} + +void EventDeliverySystemDetail::Listen(const EventMessages::NetworkTypeChanged &event) +{ + LogPedantic("NetworkTypeChanged message listen."); + + (void)event; + RegisterSettingCallback(VCONFKEY_TELEPHONY_SVC_ROAM); +} + +void EventDeliverySystemDetail::Listen(const EventMessages::HibernationEnter &event) +{ + LogPedantic("HibernationEnter message listen."); + + (void)event; + RegisterGlobalNotiCallback(GlobalNotiHibernationEnterSignal); +} + +void EventDeliverySystemDetail::Listen(const EventMessages::HibernationLeave &event) +{ + LogPedantic("HibernationLeave message listen."); + + (void)event; + RegisterGlobalNotiCallback(GlobalNotiHibernationLeaveSignal); +} + +void EventDeliverySystemDetail::Unlisten(const EventMessages::RoamingChanged &event) +{ + LogPedantic("RoamingChanged message unlisten."); + + (void)event; + UnregisterSettingCallback(VCONFKEY_SETAPPL_STATE_DATA_ROAMING_BOOL); +} + +void EventDeliverySystemDetail::Unlisten(const EventMessages::NetworkTypeChanged &event) +{ + LogPedantic("NetworkTypeChanged message unlisten."); + + (void)event; + UnregisterSettingCallback(VCONFKEY_TELEPHONY_SVC_ROAM); +} + +void EventDeliverySystemDetail::Unlisten(const EventMessages::HibernationEnter &event) +{ + LogPedantic("HibernationEnter message unlisten."); + + (void)event; + UnregisterGlobalNotiCallback(GlobalNotiHibernationEnterSignal); +} + +void EventDeliverySystemDetail::Unlisten(const EventMessages::HibernationLeave &event) +{ + LogPedantic("HibernationLeave message unlisten."); + + (void)event; + UnregisterGlobalNotiCallback(GlobalNotiHibernationLeaveSignal); +} + +} +} // namespace DPL diff --git a/modules/event/src/event_listener.cpp b/modules/event/src/event_listener.cpp new file mode 100644 index 0000000..a90973c --- /dev/null +++ b/modules/event/src/event_listener.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file event_listener.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of MVC event listener + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/event/src/event_support.cpp b/modules/event/src/event_support.cpp new file mode 100644 index 0000000..641b311 --- /dev/null +++ b/modules/event/src/event_support.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file event_support.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of MVC event support + */ +#include + +namespace DPL +{ +namespace Event +{ + +namespace // anonymous +{ +int dummyInitializerProc() +{ + GetMainEventDispatcherInstance(); + return 0; +} + +int g_dummyInitializer = dummyInitializerProc(); + +} // namespace anonymous + +} +} // namespace DPL + diff --git a/modules/event/src/generic_event_call.cpp b/modules/event/src/generic_event_call.cpp new file mode 100644 index 0000000..2ad49b8 --- /dev/null +++ b/modules/event/src/generic_event_call.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_event_call.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of generic event call + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/event/src/inter_context_delegate.cpp b/modules/event/src/inter_context_delegate.cpp new file mode 100644 index 0000000..e1dfc52 --- /dev/null +++ b/modules/event/src/inter_context_delegate.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file inter_context_delegate.cpp + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of ICDelegate functionality + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/event/src/main_event_dispatcher.cpp b/modules/event/src/main_event_dispatcher.cpp new file mode 100644 index 0000000..dc2de85 --- /dev/null +++ b/modules/event/src/main_event_dispatcher.cpp @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file main_event_dispatcher.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of main event dispatcher for EFL + */ +#include +#include +#include +#include + +namespace DPL +{ + +IMPLEMENT_SINGLETON(Event::MainEventDispatcher) + +namespace Event +{ + +typedef Singleton MainEventDispatcherSingleton; + +namespace // anonymous +{ +static const pthread_t g_threadMain = pthread_self(); + +// Late EFL event handling +MainEventDispatcher *g_lateMainEventDispatcher = NULL; +} // namespace anonymous + +MainEventDispatcher::MainEventDispatcher() +{ + // Late EFL event handling + Assert(g_lateMainEventDispatcher == NULL); + g_lateMainEventDispatcher = this; + + // Increment ECORE init count to ensure we have all + // subsystems correctly set-up until main dispatcher dtor + // This is especially important when MainEventDispatcher + // is a global object destroyed no earlier than crt destroy routine + ecore_init(); + + // Add new global ECORE event + m_eventId = ecore_event_type_new(); + + LogPedantic("ECORE event class registered: " << m_eventId); + + // Register event class handler + if ((m_eventCallHandler = ecore_event_handler_add(m_eventId, &StaticDispatchEvent, this)) == NULL) + ThrowMsg(Exception::CreateFailed, "Failed to register event handler!"); + + // Register cross event handler + m_crossEventCallHandler = ecore_main_fd_handler_add(m_crossEventCallInvoker.GetHandle(), ECORE_FD_READ, &StaticDispatchCrossInvoker, this, NULL, NULL); + + if (m_crossEventCallHandler == NULL) + ThrowMsg(Exception::CreateFailed, "Failed to register cross event handler!"); + + LogPedantic("ECORE cross-event handler registered"); +} + +MainEventDispatcher::~MainEventDispatcher() +{ + // Remove event class handler + ecore_event_handler_del(m_eventCallHandler); + m_eventCallHandler = NULL; + + // Remove cross event handler + ecore_main_fd_handler_del(m_crossEventCallHandler); + m_crossEventCallHandler = NULL; + + LogPedantic("ECORE cross-event handler unregistered"); + + // Decrement ECORE init count + // We do not need ecore routines any more + ecore_shutdown(); + + // Late EFL event handling + Assert(g_lateMainEventDispatcher == this); + g_lateMainEventDispatcher = NULL; +} + +void MainEventDispatcher::StaticDeleteEvent(void *data, void *event) +{ + LogPedantic("Static ECORE delete event handler"); + + MainEventDispatcher *This = static_cast(data); + AbstractEventCall *abstractEventCall = static_cast(event); + + Assert(This != NULL); + Assert(abstractEventCall != NULL); + + // Late EFL event handling + if (g_lateMainEventDispatcher == NULL) + { + LogPedantic("WARNING: Late EFL event delete!"); + delete abstractEventCall; + } + else + { + This->DeleteEvent(abstractEventCall); + } +} + +Eina_Bool MainEventDispatcher::StaticDispatchEvent(void *data, int type, void *event) +{ + LogPedantic("Static ECORE dispatch event"); + + MainEventDispatcher *This = static_cast(data); + AbstractEventCall *abstractEventCall = static_cast(event); + (void)type; + + Assert(This != NULL); + Assert(abstractEventCall != NULL); + + // Late EFL event handling + if (g_lateMainEventDispatcher == NULL) + { + LogPedantic("WARNING: Late EFL event dispatch!"); + } + else + { + This->DispatchEvent(abstractEventCall); + } + + // Continue to handler other ECORE events + return ECORE_CALLBACK_RENEW; +} + +Eina_Bool MainEventDispatcher::StaticDispatchTimedEvent(void *data) +{ + LogPedantic("Static ECORE dispatch timed event"); + + TimedEventStruct *timedEventStruct = static_cast(data); + MainEventDispatcher *This = timedEventStruct->This; + AbstractEventCall *abstractEventCall = timedEventStruct->abstractEventCall; + delete timedEventStruct; + + Assert(This != NULL); + Assert(abstractEventCall != NULL); + + // Late EFL event handling + if (g_lateMainEventDispatcher == NULL) + { + LogPedantic("WARNING: Late EFL timed event dispatch!"); + } + else + { + // Dispatch timed event + This->DispatchEvent(abstractEventCall); + } + + // And delete manually event, because ECORE does not + // use delete handler for timers + StaticDeleteEvent(static_cast(This), static_cast(abstractEventCall)); + + // Do not continue timed event handlers + // This also releases ECORE timer + return ECORE_CALLBACK_CANCEL; +} + +Eina_Bool MainEventDispatcher::StaticDispatchCrossInvoker(void *data, Ecore_Fd_Handler *fd_handler) +{ + LogPedantic("Static ECORE dispatch cross invoker"); + + MainEventDispatcher *This = static_cast(data); + (void)fd_handler; + + Assert(This != NULL); + + // Late EFL event handling + if (g_lateMainEventDispatcher == NULL) + { + LogPedantic("WARNING: Late EFL cross invoker dispatch!"); + } + else + { + This->DispatchCrossInvoker(); + } + + return ECORE_CALLBACK_RENEW; +} + +void MainEventDispatcher::DeleteEvent(AbstractEventCall *abstractEventCall) +{ + LogPedantic("ECORE delete event"); + delete abstractEventCall; +} + +void MainEventDispatcher::DispatchEvent(AbstractEventCall *abstractEventCall) +{ + LogPedantic("ECORE dispatch event"); + + // Call event handler + abstractEventCall->Call(); +} + +void MainEventDispatcher::DispatchTimedEvent(AbstractEventCall *abstractEventCall) +{ + LogPedantic("ECORE dispatch timed event"); + + // Call event handler + abstractEventCall->Call(); +} + +void MainEventDispatcher::DispatchCrossInvoker() +{ + LogPedantic("ECORE dispatch cross invoker"); + + // Steal cross events list + WrappedEventCallList stolenCrossEvents; + + // Critical section + { + m_crossEventCallInvoker.Reset(); + Mutex::ScopedLock lock(&m_crossEventCallMutex); + m_wrappedCrossEventCallList.swap(stolenCrossEvents); + } + + LogPedantic("Cross-thread event list stolen. Number of events: " << stolenCrossEvents.size()); + + // Repush all stolen events + WrappedEventCallList::const_iterator eventIterator; + + for (eventIterator = stolenCrossEvents.begin(); eventIterator != stolenCrossEvents.end(); ++eventIterator) + { + // Unwrap events + LogPedantic("Dispatching event from invoker"); + InternalAddEvent(eventIterator->abstractEventCall, eventIterator->timed, eventIterator->dueTime); + } + + LogPedantic("Cross-thread events dispatched"); +} + +void MainEventDispatcher::AddEventCall(AbstractEventCall *abstractEventCall) +{ + if (pthread_equal(pthread_self(), g_threadMain)) + { + LogPedantic("Main thread ECORE event push"); + InternalAddEvent(abstractEventCall, false, 0.0); + } + else + { + LogPedantic("Cross-thread ECORE event push"); + + // Push event to cross event list + { + Mutex::ScopedLock lock(&m_crossEventCallMutex); + m_wrappedCrossEventCallList.push_back(WrappedEventCall(abstractEventCall, false, 0.0)); + m_crossEventCallInvoker.Signal(); + } + + LogPedantic("Event pushed to cross-thread event list"); + } +} + +void MainEventDispatcher::AddTimedEventCall(AbstractEventCall *abstractEventCall, double dueTime) +{ + if (pthread_equal(pthread_self(), g_threadMain)) + { + LogPedantic("Main thread timed ECORE event push"); + InternalAddEvent(abstractEventCall, true, dueTime); + } + else + { + LogPedantic("Cross-thread timed ECORE event push"); + + // Push event to cross event list + { + Mutex::ScopedLock lock(&m_crossEventCallMutex); + m_wrappedCrossEventCallList.push_back(WrappedEventCall(abstractEventCall, true, dueTime)); + m_crossEventCallInvoker.Signal(); + } + + LogPedantic("Event pushed to cross-thread event list"); + } +} + +void MainEventDispatcher::InternalAddEvent(AbstractEventCall *abstractEventCall, bool timed, double dueTime) +{ + LogPedantic("Adding base event"); + + if (timed == true) + { + // Push timed event onto ecore stack + TimedEventStruct* eventData = new TimedEventStruct(abstractEventCall, this); + Ecore_Timer *timedEvent = ecore_timer_add(dueTime, &StaticDispatchTimedEvent, eventData); + + if (timedEvent == NULL) + { + delete eventData; + delete abstractEventCall; + ThrowMsg(Exception::AddTimedEventFailed, "Failed to add ECORE timed event"); + } + + LogPedantic("Timed wrapped event added"); + } + else + { + // Push immediate event onto ecore stack + Ecore_Event *event = ecore_event_add(m_eventId, abstractEventCall, &StaticDeleteEvent, this); + + if (event == NULL) + { + delete abstractEventCall; + ThrowMsg(Exception::AddEventFailed, "Failed to add ECORE event"); + } + + LogPedantic("Wrapped event added"); + } +} + +MainEventDispatcher& GetMainEventDispatcherInstance() +{ + return MainEventDispatcherSingleton::Instance(); +} + +} +} // namespace DPL diff --git a/modules/event/src/model.cpp b/modules/event/src/model.cpp new file mode 100644 index 0000000..a2e951d --- /dev/null +++ b/modules/event/src/model.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file model.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of model + */ +#include + +namespace DPL +{ +namespace Event +{ +Model::~Model() +{ +} +} +} // namespace DPL diff --git a/modules/event/src/nested_loop.cpp b/modules/event/src/nested_loop.cpp new file mode 100644 index 0000000..c142fd7 --- /dev/null +++ b/modules/event/src/nested_loop.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file contains the definition of the NestedLoopManager class. + * + * @file nested_loop.cpp + * @author Lukasz Marek (l.marek@samsung.com) + * @version 0.1 + * @brief This file contains the definition of the NestedLoopManager class. + */ + +#include +#include +#include +#include +#include +#include + +IMPLEMENT_SINGLETON(DPL::Event::NestedLoopManager) + +namespace DPL +{ +namespace Event +{ + +NestedLoopManager::NestedLoopManager() : m_eventGuard(false), + m_handle(0) +{ + Touch(); +} + +NestedLoopManager::~NestedLoopManager() +{ +} + +void* NestedLoopManager::begin(LoopHandle loopHandle) +{ + LoopInformation info(loopHandle); + m_runningLoops.push_back(info); + LogPedantic("Nested loop begin. Nested loop level: " << getLevel()); + + ecore_main_loop_begin(); + + Assert(m_runningLoops.size() && "No loop on the stack"); + + info = m_runningLoops.back(); + m_runningLoops.pop_back(); + + Assert(info.loopHandle == loopHandle && "You exit from wrong loop"); + Assert(info.exitFlag == true && "Exit flag not set"); + + LogPedantic("Nested loop quit. Nested loop level: " << getLevel()); + + if (!m_runningLoops.empty() && m_runningLoops.back().exitFlag && + !m_eventGuard) { + m_eventGuard = true; + LoopExitEvent event; + PostEvent(event); + } + return info.userParam; +} + +void NestedLoopManager::exit(LoopHandle loopHandle, + void *userParam) +{ + RunningLoopsListIterator iterator = std::find_if( + m_runningLoops.begin(), + m_runningLoops.end(), + RunningLoopsHandlePredicate(loopHandle)); + + Assert(iterator != m_runningLoops.end() && "Unknown loopHandle"); + Assert(iterator->exitFlag == false && "You cannot close a loop twice."); + + iterator->exitFlag = true; + iterator->userParam = userParam; + + if (m_runningLoops.back().exitFlag && !m_eventGuard) { + m_eventGuard = true; + LoopExitEvent event; + PostEvent(event); + } +} + +size_t NestedLoopManager::getLevel() const +{ + return m_runningLoops.size(); +} + +LoopHandle NestedLoopManager::getNewHandle() +{ + return m_handle++; +} + +void NestedLoopManager::OnEventReceived(const LoopExitEvent& event) +{ + (void)event; + Assert(!m_runningLoops.empty()); + m_eventGuard = false; // no event in queue + if (m_runningLoops.back().exitFlag) { + //exit loop when last started one is readu to finish + //this will post event if next loop is ready to exit + ecore_main_loop_quit(); + } +} + +} +} diff --git a/modules/event/src/thread_event_dispatcher.cpp b/modules/event/src/thread_event_dispatcher.cpp new file mode 100644 index 0000000..fa155bc --- /dev/null +++ b/modules/event/src/thread_event_dispatcher.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file thread_event_dispatcher.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of thread event dispatcher + */ +#include +#include +#include + +namespace DPL +{ +namespace Event +{ + +ThreadEventDispatcher::ThreadEventDispatcher() + : m_thread(NULL) +{ +} + +ThreadEventDispatcher::~ThreadEventDispatcher() +{ +} + +void ThreadEventDispatcher::SetThread(Thread *thread) +{ + m_thread = thread; +} + +void ThreadEventDispatcher::StaticEventDelete(void *event, void *userParam) +{ + AbstractEventCall *abstractEventCall = static_cast(event); + ThreadEventDispatcher *This = static_cast(userParam); + + LogPedantic("Received static event delete from thread"); + + Assert(abstractEventCall != NULL); + Assert(This != NULL); + + This->EventDelete(abstractEventCall); +} + +void ThreadEventDispatcher::StaticEventDispatch(void *event, void *userParam) +{ + AbstractEventCall *abstractEventCall = static_cast(event); + ThreadEventDispatcher *This = static_cast(userParam); + + LogPedantic("Received static event dispatch from thread"); + + Assert(abstractEventCall != NULL); + Assert(This != NULL); + + This->EventDispatch(abstractEventCall); +} + +void ThreadEventDispatcher::EventDelete(AbstractEventCall *abstractEventCall) +{ + LogPedantic("Deleting event"); + delete abstractEventCall; +} + +void ThreadEventDispatcher::EventDispatch(AbstractEventCall *abstractEventCall) +{ + LogPedantic("Dispatching event to event support"); + abstractEventCall->Call(); +} + +void ThreadEventDispatcher::AddEventCall(AbstractEventCall *abstractEventCall) +{ + // Thread must be set prior to call + Assert(m_thread != NULL); + + LogPedantic("Adding event to thread event loop"); + + // Call abstract event call in dedicated thread + m_thread->PushEvent(abstractEventCall, &StaticEventDispatch, &StaticEventDelete, this); +} + +void ThreadEventDispatcher::AddTimedEventCall(AbstractEventCall *abstractEventCall, double dueTime) +{ + // Thread must be set prior to call + Assert(m_thread != NULL); + + LogPedantic("Adding timed event to thread event loop"); + + // Call abstract event call in dedicated thread + m_thread->PushTimedEvent(abstractEventCall, dueTime, &StaticEventDispatch, &StaticEventDelete, this); +} + +} +} // namespace DPL diff --git a/modules/localization/config.cmake b/modules/localization/config.cmake new file mode 100644 index 0000000..5f52e6f --- /dev/null +++ b/modules/localization/config.cmake @@ -0,0 +1,38 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file config.cmake +# @author Soyoung Kim(sy037.kim@samsung.com) +# @version 1.0 +# @brief +# + +SET(DPL_LOCALIZATION_SOURCES + ${PROJECT_SOURCE_DIR}/modules/localization/src/localization_utils.cpp + ${PROJECT_SOURCE_DIR}/modules/localization/src/w3c_file_localization.cpp + PARENT_SCOPE +) + + +SET(DPL_LOCALIZATION_HEADERS + ${PROJECT_SOURCE_DIR}/modules/localization/include/dpl/localization/localization_utils.h + ${PROJECT_SOURCE_DIR}/modules/localization/include/dpl/localization/w3c_file_localization.h + PARENT_SCOPE +) + +SET(DPL_LOCALIZATION_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/modules/localization/include + PARENT_SCOPE +) diff --git a/modules/localization/include/dpl/localization/localization_utils.h b/modules/localization/include/dpl/localization/localization_utils.h new file mode 100644 index 0000000..6779fd5 --- /dev/null +++ b/modules/localization/include/dpl/localization/localization_utils.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file localization_utils.h + * @author Bartosz Janiak (b.janiak@samsung.com) + * @version 1.0 + */ + +#ifndef LOCALIZATION_UTILS_H +#define LOCALIZATION_UTILS_H + +#include + +#include +#include +#include +#include + +/** + * WidgetIcon + * Structure to hold information about widget icon. + */ + +struct WidgetIcon +{ + WidgetIcon() : + width(DPL::Optional::Null), + height(DPL::Optional::Null) + { + } + + /* + * a valid URI to an image file inside the widget package that represents an + * iconic representation of the widget + */ + DPL::String src; + DPL::Optional width; /// the width of the icon in pixels + DPL::Optional height; /// the height of the icon in pixels + + bool operator==(const WidgetIcon &other) const + { + return src == other.src; + } +}; + +struct WidgetStartFileInfo +{ + DPL::String file; + DPL::String localizedPath; + DPL::String encoding; + DPL::String type; + + bool operator==(const WidgetStartFileInfo& other) const + { + return file == other.file && + localizedPath == other.localizedPath && + encoding == other.encoding && + type == other.type; + } +}; + +typedef DPL::Optional OptionalWidgetIcon; +typedef std::list LanguageTagsList; +typedef DPL::Optional OptionalWidgetStartFileInfo; + +namespace LocalizationUtils { +DPL::String BCP47LanguageTagToLocale(const DPL::String&); +DPL::String LocaleToBCP47LanguageTag(const DPL::String&); + +void SetUserLanguageTags(const LanguageTagsList& tags); +void SetSystemLanguageTags(const LanguageTagsList& tags); +LanguageTagsList GetUserAgentLanguageTags(); + +void Initialize(); +} + +#endif //LOCALIZATION_UTILS_H diff --git a/modules/localization/include/dpl/localization/w3c_file_localization.h b/modules/localization/include/dpl/localization/w3c_file_localization.h new file mode 100644 index 0000000..d52bd6c --- /dev/null +++ b/modules/localization/include/dpl/localization/w3c_file_localization.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file w3c_file_localization.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + */ + +#ifndef W3C_FILE_LOCALIZATION_H +#define W3C_FILE_LOCALIZATION_H + +#include +#include +#include +#include + +#include + +// WrtDB::DbWidgetHandle +#include + +namespace W3CFileLocalization { +typedef std::list WidgetIconList; + +DPL::Optional getFilePathInWidgetPackageFromUrl( + WrtDB::DbWidgetHandle widgetHandle, + const LanguageTagsList &languageTags, + const DPL::String &url); + +DPL::Optional getFilePathInWidgetPackage( + WrtDB::DbWidgetHandle widgetHandle, + const LanguageTagsList &languageTags, + const DPL::String& file); + +DPL::OptionalString getStartFile(WrtDB::DbWidgetHandle widgetHandle); +OptionalWidgetIcon getIcon(WrtDB::DbWidgetHandle widgetHandle); +WidgetIconList getValidIconsList( + WrtDB::DbWidgetHandle widgetHandle, + const LanguageTagsList &languageTags); + +OptionalWidgetStartFileInfo getStartFileInfo( + WrtDB::DbWidgetHandle widgetHandle, + const LanguageTagsList &tagsList); +WrtDB::WidgetLocalizedInfo getLocalizedInfo(WrtDB::DbWidgetHandle widgetHandle); +} + +#endif //W3C_FILE_LOCALIZATION_H diff --git a/modules/localization/src/localization_utils.cpp b/modules/localization/src/localization_utils.cpp new file mode 100644 index 0000000..9a63800 --- /dev/null +++ b/modules/localization/src/localization_utils.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file localization_utils.cpp + * @author Bartosz Janiak (b.janiak@samsung.com) + * @version 1.0 + */ + +#include + +#include +#include +#include + +#include +#include +#include + +namespace { + +static int LanguageChanged(void *) +{ + char* lang = vconf_get_str(VCONFKEY_LANGSET); + if (!lang) { + LogError("Cannot get locale settings from vconf"); + return 0; + } + LogDebug("Language set to: " << lang); + + using namespace LocalizationUtils; + + LanguageTagsList list; + list.push_back(DPL::FromUTF8String(lang)); + SetSystemLanguageTags(list); + + LogDebug("LanguageChanged to " << lang); + + return 0; +} +} + +namespace LocalizationUtils { +static LanguageTagsList m_systemLanguageTags; +static LanguageTagsList m_userLanguageTags; +static LanguageTagsList m_languageTags; +static DPL::ReadWriteMutex m_readWriteMutex; + +template +void FindAndReplace(StringType& source, + const StringType& find, + const StringType& replace) +{ + size_t pos = 0; + while ((pos = source.find(find, pos)) != StringType::npos) { + source.replace(pos, find.length(), replace); + pos += replace.length(); + } +} + +DPL::String BCP47LanguageTagToLocale(const DPL::String& inLanguageTag) +{ + DPL::String languageTag(inLanguageTag); + FindAndReplace(languageTag, DPL::String(L"-"), DPL::String(L"_")); + return languageTag; +} + +DPL::String LocaleToBCP47LanguageTag(const DPL::String& inLocaleString) +{ + DPL::String localeString = inLocaleString.substr( + 0, + inLocaleString. + find_first_of(L".")); + FindAndReplace(localeString, DPL::String(L"_"), DPL::String(L"-")); + return localeString; +} + +void UpdateUserAgentLanguageTags() +{ + // WARNING!!!!! This function shall be called + // only when mutex is locked in readWriteMode! + + LanguageTagsList list = m_userLanguageTags; + list.insert(list.begin(), + m_systemLanguageTags.begin(), + m_systemLanguageTags.end()); + m_languageTags.clear(); + + FOREACH(i, list) { + DPL::String tag = LocaleToBCP47LanguageTag(*i); + while (true) { //W3C Packaging 9. Step 5. 2. D + if (tag.empty()) { continue; } + + LogDebug("Adding user locale '" << tag << "'"); + m_languageTags.push_back(tag); + + size_t subtagPos = tag.find_last_of(L"-"); + if (subtagPos == DPL::String::npos) { + break; + } + tag = tag.substr(0, subtagPos); + } + } + + m_languageTags.push_back(L"en"); + m_languageTags.push_back(L""); +} + +void SetUserLanguageTags(const LanguageTagsList& tags) +{ + DPL::ReadWriteMutex::ScopedWriteLock lock(&m_readWriteMutex); + if (m_userLanguageTags != tags) { + m_userLanguageTags = tags; + UpdateUserAgentLanguageTags(); + } +} + +void SetSystemLanguageTags(const LanguageTagsList& tags) +{ + DPL::ReadWriteMutex::ScopedWriteLock lock(&m_readWriteMutex); + if (m_systemLanguageTags != tags) { + m_systemLanguageTags = tags; + UpdateUserAgentLanguageTags(); + } +} + +LanguageTagsList GetUserAgentLanguageTags() +{ + DPL::ReadWriteMutex::ScopedReadLock lock(&m_readWriteMutex); + return m_languageTags; +} + +void Initialize() +{ + appcore_set_event_callback( + APPCORE_EVENT_LANG_CHANGE, + &LanguageChanged, + NULL); + + LanguageChanged(NULL); // updating language for the first time +} + +} diff --git a/modules/localization/src/w3c_file_localization.cpp b/modules/localization/src/w3c_file_localization.cpp new file mode 100644 index 0000000..bb41c5d --- /dev/null +++ b/modules/localization/src/w3c_file_localization.cpp @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file w3c_file_localization.cpp + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + */ + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace WrtDB; + +namespace { +const DPL::String FILE_URI_BEGIN = L"file://"; +const DPL::String WIDGET_URI_BEGIN = L"widget://"; + +DPL::Optional GetFilePathInWidgetPackageInternal( + const LanguageTagsList &tags, + const std::string& basePath, + std::string filePath) +{ + LogDebug("Looking for file: " << filePath << " in: " << basePath); + using namespace LocalizationUtils; + + //Check if string isn't empty + if (filePath.size() == 0) { return DPL::Optional::Null; } + //Removing preceding '/' + if (filePath[0] == '/') { filePath.erase(0, 1); } + //Check if string isn't empty + if (filePath.size() == 0) { return DPL::Optional::Null; } + + LogDebug("locales size = " << tags.size()); + for (LanguageTagsList::const_iterator it = tags.begin(); + it != tags.end(); + ++it) { + LogDebug("Trying locale: " << *it); + std::string path = basePath; + if (path[path.size() - 1] == '/') { + path.erase(path.size() - 1); + } + + if (it->empty()) { + path += "/" + filePath; + } else { + path += "/locales/" + DPL::ToUTF8String(*it) + "/" + filePath; + } + + LogDebug("Trying locale: " << *it << " | " << path); + struct stat buf; + if (0 == stat(path.c_str(), &buf)) { + if ((buf.st_mode & S_IFMT) == S_IFREG) { + path.erase(0, basePath.length()); + return DPL::Optional(path); + } + } + } + + return DPL::Optional::Null; +} + +DPL::Optional GetFilePathInWidgetPackageInternal( + const LanguageTagsList &languageTags, + const DPL::String& basePath, + const DPL::String& filePath) +{ + DPL::Optional path = + GetFilePathInWidgetPackageInternal(languageTags, + DPL::ToUTF8String(basePath), + DPL::ToUTF8String(filePath)); + DPL::Optional dplPath; + if (!!path) { + dplPath = DPL::FromUTF8String(*path); + } + return dplPath; +} +} + +namespace W3CFileLocalization { +DPL::Optional getFilePathInWidgetPackageFromUrl( + DbWidgetHandle widgetHandle, + const LanguageTagsList &languageTags, + const DPL::String &url) +{ + DPL::String req = url; + WidgetDAO dao(widgetHandle); + + if (req.find(WIDGET_URI_BEGIN) == 0) { + req.erase(0, WIDGET_URI_BEGIN.length()); + } else if (req.find(FILE_URI_BEGIN) == 0) { + req.erase(0, FILE_URI_BEGIN.length()); + if (req.find(dao.getPath()) == 0) { + req.erase(0, dao.getPath().length()); + } + } else { + LogDebug("Unknown path format, ignoring"); + return DPL::Optional::Null; + } + + auto widgetPath = dao.getPath(); + + DPL::Optional found = + GetFilePathInWidgetPackageInternal(languageTags, widgetPath, req); + + if (!found) { + LogError("Path not found within current locale in current widget"); + return DPL::Optional::Null; + } + + found = widgetPath + *found; + + return found; +} + +DPL::Optional getFilePathInWidgetPackage( + WrtDB::DbWidgetHandle widgetHandle, + const LanguageTagsList &languageTags, + const DPL::String& file) +{ + WidgetDAO dao(widgetHandle); + return GetFilePathInWidgetPackageInternal(languageTags, dao.getPath(), file); +} + +DPL::OptionalString getStartFile(const WrtDB::DbWidgetHandle widgetHandle) +{ + using namespace LocalizationUtils; + + WidgetDAO dao(widgetHandle); + + WidgetDAO::LocalizedStartFileList locList = dao.getLocalizedStartFileList(); + WidgetDAO::WidgetStartFileList list = dao.getStartFileList(); + LanguageTagsList tagsList = LocalizationUtils::GetUserAgentLanguageTags(); + + DPL::OptionalString defaultLoc = dao.getDefaultlocale(); + if (!!defaultLoc) { + tagsList.push_back(*defaultLoc); + } + + FOREACH(tag, tagsList) + { + FOREACH(sFile, locList) + { + if (*tag == sFile->widgetLocale) { + FOREACH(it, list) + { + if (it->startFileId == sFile->startFileId) { + return it->src; + } + } + } + } + } + + return DPL::OptionalString::Null; +} + +OptionalWidgetIcon getIcon(const WrtDB::DbWidgetHandle widgetHandle) +{ + using namespace LocalizationUtils; + WidgetDAO dao(widgetHandle); + + WidgetDAO::WidgetLocalizedIconList locList = dao.getLocalizedIconList(); + WidgetDAO::WidgetIconList list = dao.getIconList(); + LanguageTagsList tagsList = LocalizationUtils::GetUserAgentLanguageTags(); + + DPL::OptionalString defaultLoc = dao.getDefaultlocale(); + if (!!defaultLoc) { + tagsList.push_back(*defaultLoc); + } + + FOREACH(tag, tagsList) + { + FOREACH(icon, locList) + { + if (*tag == icon->widgetLocale) { + FOREACH(it, list) + { + if (it->iconId == icon->iconId) { + WidgetIcon ret; + ret.src = it->iconSrc; + ret.width = it->iconWidth; + ret.height = it->iconHeight; + return ret; + } + } + } + } + } + + return OptionalWidgetIcon::Null; +} + +WidgetIconList getValidIconsList( + WrtDB::DbWidgetHandle widgetHandle, + const LanguageTagsList &languageTags) +{ + using namespace LocalizationUtils; + WidgetDAO dao(widgetHandle); + WidgetDAO::WidgetIconList list = dao.getIconList(); + + WidgetIconList outlist; + + FOREACH(it, list) + { + LogDebug(":" << it->iconSrc); + if (!!getFilePathInWidgetPackage(widgetHandle, + languageTags, + it->iconSrc)) + { + WidgetIcon ret; + ret.src = it->iconSrc; + ret.width = it->iconWidth; + ret.height = it->iconHeight; + outlist.push_back(ret); + } + } + return outlist; +} + +OptionalWidgetStartFileInfo getStartFileInfo( + WrtDB::DbWidgetHandle widgetHandle, + const LanguageTagList &tagsList) +{ + using namespace LocalizationUtils; + + WidgetStartFileInfo info; + + WidgetDAO dao(widgetHandle); + WidgetDAO::LocalizedStartFileList locList = + dao.getLocalizedStartFileList(); + WidgetDAO::WidgetStartFileList list = dao.getStartFileList(); + + FOREACH(tag, tagsList) + { + FOREACH(sFile, locList) + { + if (*tag == sFile->widgetLocale) { + FOREACH(it, list) + { + if (it->startFileId == + sFile->startFileId) { + info.file = it->src; + info.encoding = sFile->encoding; + info.type = sFile->type; + if (tag->empty()) { + info.localizedPath = it->src; + } else { + info.localizedPath = L"locales/" + *tag; + info.localizedPath += it->src; + } + return info; + } + } + } + } + } + + return OptionalWidgetStartFileInfo::Null; +} + +WidgetLocalizedInfo getLocalizedInfo(const WrtDB::DbWidgetHandle handle) +{ + LanguageTagList languages = + LocalizationUtils::GetUserAgentLanguageTags(); + WidgetDAO dao(handle); + DPL::OptionalString dl = dao.getDefaultlocale(); + if (!!dl) { + languages.push_back(*dl); + } + + WidgetLocalizedInfo result; + FOREACH(i, languages) + { + WidgetLocalizedInfo languageResult = dao.getLocalizedInfo(*i); + +#define OVERWRITE_IF_NULL(FIELD) if (!result.FIELD) { \ + result.FIELD = languageResult.FIELD; \ +} + + OVERWRITE_IF_NULL(name); + OVERWRITE_IF_NULL(shortName); + OVERWRITE_IF_NULL(description); + OVERWRITE_IF_NULL(license); + OVERWRITE_IF_NULL(licenseHref); + +#undef OVERWRITE_IF_NULL + } + + return result; +} +} diff --git a/modules/log/config.cmake b/modules/log/config.cmake new file mode 100644 index 0000000..26e35f6 --- /dev/null +++ b/modules/log/config.cmake @@ -0,0 +1,41 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file config.cmake +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +SET(DPL_LOG_SOURCES + ${PROJECT_SOURCE_DIR}/modules/log/src/abstract_log_provider.cpp + ${PROJECT_SOURCE_DIR}/modules/log/src/dlog_log_provider.cpp + ${PROJECT_SOURCE_DIR}/modules/log/src/log.cpp + ${PROJECT_SOURCE_DIR}/modules/log/src/old_style_log_provider.cpp + PARENT_SCOPE +) + +SET(DPL_LOG_HEADERS + ${PROJECT_SOURCE_DIR}/modules/log/include/dpl/log/abstract_log_provider.h + ${PROJECT_SOURCE_DIR}/modules/log/include/dpl/log/dlog_log_provider.h + ${PROJECT_SOURCE_DIR}/modules/log/include/dpl/log/log.h + ${PROJECT_SOURCE_DIR}/modules/log/include/dpl/log/old_style_log_provider.h + PARENT_SCOPE +) + +SET(DPL_LOG_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/modules/log/include/ + PARENT_SCOPE +) diff --git a/modules/log/include/dpl/log/abstract_log_provider.h b/modules/log/include/dpl/log/abstract_log_provider.h new file mode 100644 index 0000000..fdce9c0 --- /dev/null +++ b/modules/log/include/dpl/log/abstract_log_provider.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_log_provider.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of abstract log provider + */ +#ifndef DPL_ABSTRACT_LOG_PROVIDER_H +#define DPL_ABSTRACT_LOG_PROVIDER_H + +namespace DPL +{ +namespace Log +{ + +class AbstractLogProvider +{ +public: + virtual ~AbstractLogProvider() {} + + virtual void Debug(const char *message, const char *fileName, int line, const char *function) = 0; + virtual void Info(const char *message, const char *fileName, int line, const char *function) = 0; + virtual void Warning(const char *message, const char *fileName, int line, const char *function) = 0; + virtual void Error(const char *message, const char *fileName, int line, const char *function) = 0; + virtual void Pedantic(const char *message, const char *fileName, int line, const char *function) = 0; + +protected: + static const char *LocateSourceFileName(const char *filename); +}; + +} +} // namespace DPL + +#endif // DPL_ABSTRACT_LOG_PROVIDER_H diff --git a/modules/log/include/dpl/log/dlog_log_provider.h b/modules/log/include/dpl/log/dlog_log_provider.h new file mode 100644 index 0000000..0b3edb5 --- /dev/null +++ b/modules/log/include/dpl/log/dlog_log_provider.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file dlog_log_provider.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of DLOG log provider + */ +#ifndef DPL_DLOG_LOG_PROVIDER_H +#define DPL_DLOG_LOG_PROVIDER_H + +#include +#include +#include + +namespace DPL +{ +namespace Log +{ + +class DLOGLogProvider + : public AbstractLogProvider +{ +private: + DPL::ScopedFree m_tag; + + static std::string FormatMessage(const char *message, const char *filename, int line, const char *function); +public: + DLOGLogProvider(); + virtual ~DLOGLogProvider(); + + virtual void Debug(const char *message, const char *fileName, int line, const char *function); + virtual void Info(const char *message, const char *fileName, int line, const char *function); + virtual void Warning(const char *message, const char *fileName, int line, const char *function); + virtual void Error(const char *message, const char *fileName, int line, const char *function); + virtual void Pedantic(const char *message, const char *fileName, int line, const char *function); + + // Set global Tag according to DLOG + void SetTag(const char *tag); +}; + +} +} // namespace DPL + +#endif // DPL_DLOG_LOG_PROVIDER_H diff --git a/modules/log/include/dpl/log/log.h b/modules/log/include/dpl/log/log.h new file mode 100644 index 0000000..c9564bb --- /dev/null +++ b/modules/log/include/dpl/log/log.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file log.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of log system + */ +#ifndef DPL_LOG_H +#define DPL_LOG_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Log +{ +/** + * DPL log system + * + * To switch logs into old style, export + * DPL_USE_OLD_STYLE_LOGS before application start + */ +class LogSystem + : private Noncopyable +{ +private: + ReadWriteMutex m_spinLock; + + typedef std::list AbstractLogProviderPtrList; + AbstractLogProviderPtrList m_providers; + + DLOGLogProvider *m_dlogProvider; + OldStyleLogProvider *m_oldStyleProvider; + + bool m_isLoggingEnabled; + +public: + bool IsLoggingEnabled() const; + LogSystem(); + virtual ~LogSystem(); + + /** + * Log debug message + */ + void Debug(const char *message, const char *filename, int line, const char *function); + + /** + * Log info message + */ + void Info(const char *message, const char *filename, int line, const char *function); + + /** + * Log warning message + */ + void Warning(const char *message, const char *filename, int line, const char *function); + + /** + * Log error message + */ + void Error(const char *message, const char *filename, int line, const char *function); + + /** + * Log pedantic message + */ + void Pedantic(const char *message, const char *filename, int line, const char *function); + + /** + * Set default's DLOG provider Tag + */ + void SetTag(const char *tag); + + /** + * Add abstract provider to providers list + * + * @notice Ownership is transfered to LogSystem and deleted upon exit + */ + void AddProvider(AbstractLogProvider *provider); + + /** + * Remove abstract provider from providers list + */ + void RemoveProvider(AbstractLogProvider *provider); +}; + +/* + * Replacement low overhead null logging class + */ +class NullStream { + public: + NullStream() {} + + template + NullStream& operator<<(const T&) { return *this; } +}; + +/** + * Log system singleton + */ +typedef Singleton LogSystemSingleton; + +} +} // namespace DPL + +// +// Log support +// +// + +#ifdef DPL_LOGS_ENABLED + #define DPL_MACRO_FOR_LOGGING(message, function) \ + do \ + { \ + if (DPL::Log::LogSystemSingleton::Instance().IsLoggingEnabled()) \ + { \ + std::ostringstream platformLog; \ + platformLog << message; \ + DPL::Log::LogSystemSingleton::Instance().function( \ + platformLog.str().c_str(), \ + __FILE__, __LINE__, __FUNCTION__); \ + } \ + } while (0) +#else +/* avoid warnings about unused variables */ + #define DPL_MACRO_FOR_LOGGING(message, function) \ + do { \ + DPL::Log::NullStream ns; \ + ns << message; \ + } while (0) +#endif + + +#define LogDebug(message) DPL_MACRO_FOR_LOGGING(message, Debug) +#define LogInfo(message) DPL_MACRO_FOR_LOGGING(message, Info) +#define LogWarning(message) DPL_MACRO_FOR_LOGGING(message, Warning) +#define LogError(message) DPL_MACRO_FOR_LOGGING(message, Error) +#define LogPedantic(message) DPL_MACRO_FOR_LOGGING(message, Pedantic) + +#endif // DPL_LOG_H diff --git a/modules/log/include/dpl/log/old_style_log_provider.h b/modules/log/include/dpl/log/old_style_log_provider.h new file mode 100644 index 0000000..3ff3d89 --- /dev/null +++ b/modules/log/include/dpl/log/old_style_log_provider.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file old_style_log_provider.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of old style log provider + */ +#ifndef DPL_OLD_STYLE_LOG_PROVIDER_H +#define DPL_OLD_STYLE_LOG_PROVIDER_H + +#include +#include + +namespace DPL +{ +namespace Log +{ +class OldStyleLogProvider + : public AbstractLogProvider +{ +private: + bool m_showDebug; + bool m_showInfo; + bool m_showWarning; + bool m_showError; + bool m_showPedantic; + + static std::string FormatMessage(const char *message, const char *filename, int line, const char *function); + +public: + OldStyleLogProvider(bool showDebug, bool showInfo, bool showWarning, bool showError, bool showPedantic); + virtual ~OldStyleLogProvider() {} + + virtual void Debug(const char *message, const char *fileName, int line, const char *function); + virtual void Info(const char *message, const char *fileName, int line, const char *function); + virtual void Warning(const char *message, const char *fileName, int line, const char *function); + virtual void Error(const char *message, const char *fileName, int line, const char *function); + virtual void Pedantic(const char *message, const char *fileName, int line, const char *function); +}; + +} +} // namespace DPL + +#endif // DPL_OLD_STYLE_LOG_PROVIDER_H diff --git a/modules/log/src/abstract_log_provider.cpp b/modules/log/src/abstract_log_provider.cpp new file mode 100644 index 0000000..d16976c --- /dev/null +++ b/modules/log/src/abstract_log_provider.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_log_provider.cpp + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of abstract log provider + */ +#include +#include + +namespace DPL +{ +namespace Log +{ +const char *AbstractLogProvider::LocateSourceFileName(const char *filename) +{ + const char *ptr = strrchr(filename, '/'); + return ptr != NULL ? ptr + 1 : filename; +} +} +} diff --git a/modules/log/src/dlog_log_provider.cpp b/modules/log/src/dlog_log_provider.cpp new file mode 100644 index 0000000..448ae2c --- /dev/null +++ b/modules/log/src/dlog_log_provider.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file dlog_log_provider.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of DLOG log provider + */ +#include +#include +#include +#include + +namespace DPL +{ +namespace Log +{ + +std::string DLOGLogProvider::FormatMessage(const char *message, const char *filename, int line, const char *function) +{ + std::ostringstream val; + + val << std::string("[") << + LocateSourceFileName(filename) << std::string(":") << line << + std::string("] ") << function << std::string("(): ") << message; + + return val.str(); +} + +DLOGLogProvider::DLOGLogProvider() +{ +} + +DLOGLogProvider::~DLOGLogProvider() +{ +} + +void DLOGLogProvider::SetTag(const char *tag) +{ + m_tag.Reset(strdup(tag)); +} + +void DLOGLogProvider::Debug(const char *message, const char *filename, int line, const char *function) +{ + LOG(LOG_DEBUG, m_tag.Get(), "%s" , FormatMessage(message, filename, line, function).c_str()); +} + +void DLOGLogProvider::Info(const char *message, const char *filename, int line, const char *function) +{ + LOG(LOG_INFO, m_tag.Get(), "%s" , FormatMessage(message, filename, line, function).c_str()); +} + +void DLOGLogProvider::Warning(const char *message, const char *filename, int line, const char *function) +{ + LOG(LOG_WARN, m_tag.Get(), "%s" , FormatMessage(message, filename, line, function).c_str()); +} + +void DLOGLogProvider::Error(const char *message, const char *filename, int line, const char *function) +{ + LOG(LOG_ERROR, m_tag.Get(), "%s" , FormatMessage(message, filename, line, function).c_str()); +} + +void DLOGLogProvider::Pedantic(const char *message, const char *filename, int line, const char *function) +{ + LOG(LOG_DEBUG, "DPL", "%s", FormatMessage(message, filename, line, function).c_str()); +} + +} +} // namespace DPL diff --git a/modules/log/src/log.cpp b/modules/log/src/log.cpp new file mode 100644 index 0000000..b03ab61 --- /dev/null +++ b/modules/log/src/log.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file log.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of log system + */ +#include +#include + +IMPLEMENT_SINGLETON(DPL::Log::LogSystem) + +namespace DPL +{ +namespace Log +{ +namespace // anonymous +{ +const char *OLD_STYLE_LOGS_ENV_NAME = "DPL_USE_OLD_STYLE_LOGS"; +const char *OLD_STYLE_PEDANTIC_LOGS_ENV_NAME = "DPL_USE_OLD_STYLE_PEDANTIC_LOGS"; +const char *OLD_STYLE_LOGS_MASK_ENV_NAME = "DPL_USE_OLD_STYLE_LOGS_MASK"; +const char *DPL_LOG_OFF = "DPL_LOG_OFF"; +} // namespace anonymous + + +bool LogSystem::IsLoggingEnabled() const +{ + return m_isLoggingEnabled; +} + +LogSystem::LogSystem() + : m_dlogProvider(NULL), + m_oldStyleProvider(NULL), + m_isLoggingEnabled(!getenv(DPL_LOG_OFF)) +{ + bool oldStyleLogs = false; + bool oldStyleDebugLogs = true; + bool oldStyleInfoLogs = true; + bool oldStyleWarningLogs = true; + bool oldStyleErrorLogs = true; + bool oldStylePedanticLogs = false; + + // Check environment settings about pedantic logs + const char *value = getenv(OLD_STYLE_LOGS_ENV_NAME); + + if (value != NULL && !strcmp(value, "1")) + oldStyleLogs = true; + + value = getenv(OLD_STYLE_PEDANTIC_LOGS_ENV_NAME); + + if (value != NULL && !strcmp(value, "1")) + oldStylePedanticLogs = true; + + value = getenv(OLD_STYLE_LOGS_MASK_ENV_NAME); + + if (value != NULL) + { + size_t len = strlen(value); + + if (len >= 1) + { + if (value[0] == '0') + oldStyleDebugLogs = false; + else if (value[0] == '1') + oldStyleDebugLogs = true; + } + + if (len >= 2) + { + if (value[1] == '0') + oldStyleInfoLogs = false; + else if (value[1] == '1') + oldStyleInfoLogs = true; + } + + if (len >= 3) + { + if (value[2] == '0') + oldStyleWarningLogs = false; + else if (value[2] == '1') + oldStyleWarningLogs = true; + } + + if (len >= 4) + { + if (value[3] == '0') + oldStyleErrorLogs = false; + else if (value[3] == '1') + oldStyleErrorLogs = true; + } + } + + // Setup default DLOG and old style logging + if (oldStyleLogs) + { + // Old style + m_oldStyleProvider = new OldStyleLogProvider(oldStyleDebugLogs, oldStyleInfoLogs, oldStyleWarningLogs, oldStyleErrorLogs, oldStylePedanticLogs); + AddProvider(m_oldStyleProvider); + } + else + { + // DLOG + m_dlogProvider = new DLOGLogProvider(); + AddProvider(m_dlogProvider); + } +} + +LogSystem::~LogSystem() +{ + // Delete all providers + for (AbstractLogProviderPtrList::iterator iterator = m_providers.begin(); iterator != m_providers.end(); ++iterator) + delete *iterator; + + m_providers.clear(); + + // And even default providers + m_dlogProvider = NULL; + m_oldStyleProvider = NULL; +} + +void LogSystem::SetTag(const char* tag) +{ + if (m_dlogProvider != NULL) + m_dlogProvider->SetTag(tag); +} + +void LogSystem::AddProvider(AbstractLogProvider *provider) +{ + ReadWriteMutex::ScopedWriteLock lock(&m_spinLock); + m_providers.push_back(provider); +} + +void LogSystem::RemoveProvider(AbstractLogProvider *provider) +{ + ReadWriteMutex::ScopedWriteLock lock(&m_spinLock); + m_providers.remove(provider); +} + +void LogSystem::Debug(const char *message, const char *filename, int line, const char *function) +{ + ReadWriteMutex::ScopedReadLock lock(&m_spinLock); + + for (AbstractLogProviderPtrList::iterator iterator = m_providers.begin(); iterator != m_providers.end(); ++iterator) + (*iterator)->Debug(message, filename, line, function); +} + +void LogSystem::Info(const char *message, const char *filename, int line, const char *function) +{ + ReadWriteMutex::ScopedReadLock lock(&m_spinLock); + + for (AbstractLogProviderPtrList::iterator iterator = m_providers.begin(); iterator != m_providers.end(); ++iterator) + (*iterator)->Info(message, filename, line, function); +} + +void LogSystem::Warning(const char *message, const char *filename, int line, const char *function) +{ + ReadWriteMutex::ScopedReadLock lock(&m_spinLock); + + for (AbstractLogProviderPtrList::iterator iterator = m_providers.begin(); iterator != m_providers.end(); ++iterator) + (*iterator)->Warning(message, filename, line, function); +} + +void LogSystem::Error(const char *message, const char *filename, int line, const char *function) +{ + ReadWriteMutex::ScopedReadLock lock(&m_spinLock); + + for (AbstractLogProviderPtrList::iterator iterator = m_providers.begin(); iterator != m_providers.end(); ++iterator) + (*iterator)->Error(message, filename, line, function); +} + +void LogSystem::Pedantic(const char *message, const char *filename, int line, const char *function) +{ + ReadWriteMutex::ScopedReadLock lock(&m_spinLock); + + for (AbstractLogProviderPtrList::iterator iterator = m_providers.begin(); iterator != m_providers.end(); ++iterator) + (*iterator)->Pedantic(message, filename, line, function); +} + +} +} // namespace DPL diff --git a/modules/log/src/old_style_log_provider.cpp b/modules/log/src/old_style_log_provider.cpp new file mode 100644 index 0000000..7c3b1fc --- /dev/null +++ b/modules/log/src/old_style_log_provider.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file old_style_log_provider.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of old style log provider + */ +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Log +{ +namespace // anonymous +{ +using namespace DPL::Colors::Text; +const char *DEBUG_BEGIN = GREEN_BEGIN; +const char *DEBUG_END = GREEN_END; +const char *INFO_BEGIN = CYAN_BEGIN; +const char *INFO_END = CYAN_END; +const char *ERROR_BEGIN = RED_BEGIN; +const char *ERROR_END = RED_END; +const char *WARNING_BEGIN = BOLD_GOLD_BEGIN; +const char *WARNING_END = BOLD_GOLD_END; +const char *PEDANTIC_BEGIN = PURPLE_BEGIN; +const char *PEDANTIC_END = PURPLE_END; + + +std::string GetFormattedTime() +{ + timeval tv; + tm localNowTime; + + gettimeofday(&tv, NULL); + localtime_r(&tv.tv_sec, &localNowTime); + + char format[64]; + snprintf(format, sizeof(format), "%02i:%02i:%02i.%03i", localNowTime.tm_hour, localNowTime.tm_min, localNowTime.tm_sec, static_cast(tv.tv_usec / 1000)); + return format; +} + +} // namespace anonymous + +std::string OldStyleLogProvider::FormatMessage(const char *message, const char *filename, int line, const char *function) +{ + std::ostringstream val; + + val << std::string("[") << GetFormattedTime() << std::string("] [") << + static_cast(pthread_self()) << "/" << static_cast(getpid()) << std::string("] [") << + LocateSourceFileName(filename) << std::string(":") << line << + std::string("] ") << function << std::string("(): ") << message; + + return val.str(); +} + +OldStyleLogProvider::OldStyleLogProvider(bool showDebug, bool showInfo, bool showWarning, bool showError, bool showPedantic) + : m_showDebug(showDebug), + m_showInfo(showInfo), + m_showWarning(showWarning), + m_showError(showError), + m_showPedantic(showPedantic) +{ +} + +void OldStyleLogProvider::Debug(const char *message, const char *filename, int line, const char *function) +{ + if (m_showDebug) + fprintf(stdout, "%s%s%s\n", DEBUG_BEGIN, FormatMessage(message, filename, line, function).c_str(), DEBUG_END); +} + +void OldStyleLogProvider::Info(const char *message, const char *filename, int line, const char *function) +{ + if (m_showInfo) + fprintf(stdout, "%s%s%s\n", INFO_BEGIN, FormatMessage(message, filename, line, function).c_str(), INFO_END); +} + +void OldStyleLogProvider::Warning(const char *message, const char *filename, int line, const char *function) +{ + if (m_showWarning) + fprintf(stdout, "%s%s%s\n", WARNING_BEGIN, FormatMessage(message, filename, line, function).c_str(), WARNING_END); +} + +void OldStyleLogProvider::Error(const char *message, const char *filename, int line, const char *function) +{ + if (m_showError) + fprintf(stdout, "%s%s%s\n", ERROR_BEGIN, FormatMessage(message, filename, line, function).c_str(), ERROR_END); +} + +void OldStyleLogProvider::Pedantic(const char *message, const char *filename, int line, const char *function) +{ + if (m_showPedantic) + fprintf(stdout, "%s%s%s\n", PEDANTIC_BEGIN, FormatMessage(message, filename, line, function).c_str(), PEDANTIC_END); +} + +} +} // namespace DPL diff --git a/modules/popup/DESCRIPTION b/modules/popup/DESCRIPTION new file mode 100644 index 0000000..665e43e --- /dev/null +++ b/modules/popup/DESCRIPTION @@ -0,0 +1,2 @@ +!!!options!!! stop +UI module for displaying all kinds of pop-up dialogs diff --git a/modules/popup/config.cmake b/modules/popup/config.cmake new file mode 100644 index 0000000..7a304d9 --- /dev/null +++ b/modules/popup/config.cmake @@ -0,0 +1,42 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file CMakeLists.txt +# @author Pawel Sikorski (p.sikorski@samsung.com) +# @version 1.0 +# + +SET(DPL_POPUP_SOURCES + ${PROJECT_SOURCE_DIR}/modules/popup/src/popup_manager.cpp + ${PROJECT_SOURCE_DIR}/modules/popup/src/evas_object.cpp + ${PROJECT_SOURCE_DIR}/modules/popup/src/popup_renderer.cpp + ${PROJECT_SOURCE_DIR}/modules/popup/src/popup_controller.cpp + PARENT_SCOPE + ) + +SET(DPL_POPUP_HEADERS + ${PROJECT_SOURCE_DIR}/modules/popup/include/dpl/popup/popup.h + ${PROJECT_SOURCE_DIR}/modules/popup/include/dpl/popup/evas_object.h + ${PROJECT_SOURCE_DIR}/modules/popup/include/dpl/popup/popup_controller.h + ${PROJECT_SOURCE_DIR}/modules/popup/include/dpl/popup/popup_manager.h + ${PROJECT_SOURCE_DIR}/modules/popup/include/dpl/popup/popup_object.h + ${PROJECT_SOURCE_DIR}/modules/popup/include/dpl/popup/popup_renderer.h + PARENT_SCOPE + ) + +SET(DPL_POPUP_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/modules/popup/include + PARENT_SCOPE +) \ No newline at end of file diff --git a/modules/popup/include/dpl/popup/evas_object.h b/modules/popup/include/dpl/popup/evas_object.h new file mode 100644 index 0000000..ba4ad9b --- /dev/null +++ b/modules/popup/include/dpl/popup/evas_object.h @@ -0,0 +1,661 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file evas_object.h + * @author Lukasz Wrzosek (l.wrzosel@samsung.com) + * @version 1.0 + * @brief This file is the header for Evas_Object wrapper from Efl. + */ +#ifndef WRT_SRC_DOMAIN_EFL_EVAS_OBJECT_H +#define WRT_SRC_DOMAIN_EFL_EVAS_OBJECT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL { +namespace Popup { + +class EvasObject +{ + class EvasObjectShared; + typedef DPL::SharedPtr EvasObjectSharedPtr; + + public: + class IConnection + { + public: + Evas_Object* GetEvasObject(); + void Disconnect(); + + private: + IConnection(EvasObjectShared* object); + virtual ~IConnection() + { + } + virtual void Call(void* /*event_info*/) = 0; + + static void SmartCallbackWrapper(void* data, + Evas_Object* /*object*/, + void* event_info); + static void EvasCallbackWrapper(void* data, + Evas* /*evas*/, + Evas_Object* /*object*/, + void* event_info); + + virtual void ConnectPrv() = 0; + virtual void DisconnectPrv() = 0; + + friend class EvasObjectShared; + + EvasObjectShared* m_object; + }; + + private: + class EvasObjectShared : DPL::Noncopyable + { + public: + friend class IConnection; + Evas_Object* GetObject(); + + typedef std::set IConnectionsSet; + + class SmartConnectionBase : public IConnection + { + public: + SmartConnectionBase(const std::string& name, + EvasObjectShared* object); + + virtual void ConnectPrv(); + virtual void DisconnectPrv(); + std::string m_callbackName; + }; + + template + class SmartConnection : public SmartConnectionBase + { + public: + typedef void (*CbType)(IConnection* connection, + void* event_info, + Args ... args); + + SmartConnection(const std::string& name, + CbType callback, + EvasObjectShared* object, + Args ... args) : + SmartConnectionBase(name, object), + m_callback(callback), + m_args(args ...) + { + } + + virtual ~SmartConnection() + { + } + + virtual void Call(void* event_info) + { + DPL::Apply(m_callback, + m_args, + this, + event_info); + } + + private: + CbType m_callback; + std::tuple m_args; + }; + + template + class SmartMemberConnection1 : public SmartConnectionBase + { + public: + typedef void (ThisType::*CbType)(IConnection* connection, + void* event_info, ArgType1 *arg1); + + SmartMemberConnection1(const std::string& name, + CbType callback, + ThisType* callee, + ArgType1* arg1, + EvasObjectShared* object) : + SmartConnectionBase(name, object), + m_callback(callback), + m_callee(callee), + m_arg1(arg1) + { + } + + virtual ~SmartMemberConnection1() + { + } + + virtual void Call(void* event_info) + { + (m_callee->*m_callback)(this, event_info, m_arg1); + } + + private: + CbType m_callback; + ThisType* m_callee; + ArgType1* m_arg1; + }; + + template + class SmartMemberConnection2 : public SmartConnectionBase + { + public: + typedef void (ThisType::*CbType)(IConnection* connection, + void* event_info, ArgType1 *arg1, + ArgType2* arg2); + + SmartMemberConnection2(const std::string& name, + CbType callback, + ThisType* callee, + ArgType1* arg1, + ArgType2* arg2, + EvasObjectShared* object) : + SmartConnectionBase(name, object), + m_callback(callback), + m_callee(callee), + m_arg1(arg1), + m_arg2(arg2) + { + } + + virtual ~SmartMemberConnection2() + { + } + + virtual void Call(void* event_info) + { + (m_callee->*m_callback)(this, event_info, m_arg1, m_arg2); + } + + private: + CbType m_callback; + ThisType* m_callee; + ArgType1* m_arg1; + ArgType2* m_arg2; + }; + + class EvasConnectionBase : public IConnection + { + public: + EvasConnectionBase(Evas_Callback_Type type, + EvasObjectShared* object); + + virtual void ConnectPrv(); + virtual void DisconnectPrv(); + + Evas_Callback_Type m_callbackType; + }; + + template + class EvasConnection1 : public EvasConnectionBase + { + public: + typedef void (*CbType)(IConnection* connection, void* event_info, + ArgType1 *arg1); + + EvasConnection1(Evas_Callback_Type type, + CbType callback, + ArgType1* arg1, + EvasObjectShared* object) : + EvasConnectionBase(type, object), + m_callback(callback), + m_arg1(arg1) + { + } + + virtual ~EvasConnection1() + { + } + + virtual void Call(void* event_info) + { + m_callback(this, event_info, m_arg1); + } + + private: + CbType m_callback; + ArgType1* m_arg1; + }; + + template + class EvasConnection2 : public EvasConnectionBase + { + public: + typedef void (*CbType)(IConnection* connection, void* event_info, + ArgType1 *arg1, ArgType2 *arg2); + + EvasConnection2(Evas_Callback_Type type, + CbType callback, + ArgType1* arg1, + ArgType2* arg2, + EvasObjectShared* object) : + EvasConnectionBase(type, object), + m_callback(callback), + m_arg1(arg1), + m_arg2(arg2) + { + } + + virtual ~EvasConnection2() + { + } + + virtual void Call(void* event_info) + { + m_callback(this, event_info, m_arg1, m_arg2); + } + + private: + CbType m_callback; + ArgType1* m_arg1; + ArgType2* m_arg2; + }; + + template + class EvasMemberConnection1 : public EvasConnectionBase + { + public: + typedef void (ThisType::*CbType)(IConnection* connection, + void* event_info, ArgType1 *arg1); + + EvasMemberConnection1(Evas_Callback_Type type, + CbType callback, + ThisType* callee, + ArgType1* arg1, + EvasObjectShared* object) : + EvasConnectionBase(type, object), + m_callback(callback), + m_callee(callee), + m_arg1(arg1) + { + } + + virtual ~EvasMemberConnection1() + { + } + + virtual void Call(void* event_info) + { + (m_callee->*m_callback)(this, event_info, m_arg1); + } + + private: + CbType m_callback; + ThisType* m_callee; + ArgType1* m_arg1; + }; + + template + class EvasMemberConnection2 : public EvasConnectionBase + { + public: + typedef void (ThisType::*CbType)(IConnection* connection, + void* event_info, ArgType1* arg1, + ArgType2* arg2); + + EvasMemberConnection2(Evas_Callback_Type type, + CbType callback, + ThisType* callee, + ArgType1* arg1, + ArgType2* arg2, + EvasObjectShared* object) : + EvasConnectionBase(type, object), + m_callback(callback), + m_callee(callee), + m_arg1(arg1), + m_arg2(arg2) + { + } + + virtual ~EvasMemberConnection2() + { + } + + virtual void Call(void* event_info) + { + (m_callee->*m_callback)(this, event_info, m_arg1, m_arg2); + } + + private: + CbType m_callback; + ThisType* m_callee; + ArgType1* m_arg1; + ArgType2* m_arg2; + }; + + EvasObjectShared(); + explicit EvasObjectShared(Evas_Object* object); + void SetObject(Evas_Object* object); + ~EvasObjectShared(); + + template + IConnection* ConnectSmartCallback(const char* callbackName, + typename SmartConnection::CbType callback, + Args ... args) + { + Assert(m_object); + Assert(callbackName); + Assert(callback); + IConnection* connection = new SmartConnection( + callbackName, + callback, + this, + args ...); + m_connections.insert(connection); + connection->ConnectPrv(); + return connection; + } + + template + IConnection* ConnectMemberSmartCallback( + const char* callbackName, + typename SmartMemberConnection2::CbType callback, + ThisType* callee, + ArgType1* arg1, + ArgType2* arg2) + { + Assert(m_object); + Assert(callee); + Assert(callbackName); + Assert(callback); + IConnection* connection = + new SmartMemberConnection2( + callbackName, + callback, + callee, + arg1, + arg2, + this); + m_connections.insert(connection); + connection->ConnectPrv(); + return connection; + } + + template + IConnection* ConnectMemberSmartCallback( + const char* callbackName, + typename SmartMemberConnection1::CbType callback, + ThisType* callee, + ArgType1* arg1) + { + Assert(m_object); + Assert(callee); + Assert(callbackName); + Assert(callback); + IConnection* connection = + new SmartMemberConnection1(callbackName, + callback, + callee, + arg1, + this); + m_connections.insert(connection); + connection->ConnectPrv(); + return connection; + } + + template + IConnection* ConnectEvasCallback(Evas_Callback_Type callbackType, + typename EvasConnection2::CbType callback, + ArgType1* arg1, + ArgType2* arg2) + { + Assert(m_object); + Assert(callbackType); + Assert(callback); + IConnection* connection = new EvasConnection2( + callbackType, + callback, + arg1, + arg2, + this); + m_connections.insert(connection); + connection->ConnectPrv(); + return connection; + } + + template + IConnection* ConnectEvasCallback(Evas_Callback_Type callbackType, + typename EvasConnection1::CbType callback, + ArgType1* arg1) + { + Assert(m_object); + Assert(callbackType); + Assert(callback); + IConnection* connection = new EvasConnection1( + callbackType, + callback, + arg1, + this); + m_connections.insert(connection); + connection->ConnectPrv(); + return connection; + } + + template + IConnection* ConnectMemberEvasCallback( + Evas_Callback_Type callbackType, + typename EvasMemberConnection2::CbType callback, + ThisType* callee, + ArgType1* arg1, + ArgType2* arg2) + { + Assert(m_object); + Assert(callee); + Assert(callbackType); + Assert(callback); + IConnection* connection = + new EvasMemberConnection2( + callbackType, + callback, + callee, + arg1, + arg2, + this); + m_connections.insert(connection); + connection->ConnectPrv(); + return connection; + } + + template + IConnection* ConnectMemberEvasCallback( + Evas_Callback_Type callbackType, + typename EvasMemberConnection1::CbType callback, + ThisType* callee, + ArgType1* arg1) + { + Assert(m_object); + Assert(callee); + Assert(callbackType); + Assert(callback); + IConnection* connection = + new EvasMemberConnection1(callbackType, + callback, + callee, + arg1, + this); + m_connections.insert(connection); + connection->ConnectPrv(); + return connection; + } + + bool DisconnectCallback(IConnection* connection); + void DisconnectAll(); + + static void StaticOnDelEvent(void* data, + Evas* /*e*/, + Evas_Object* /*o*/, + void* /*ev*/); + + IConnectionsSet m_connections; + Evas_Object* m_object; + }; + + public: + EvasObject(); + explicit EvasObject(Evas_Object* object); + EvasObject(const EvasObject& other); + ~EvasObject(); + + EvasObject& operator=(const EvasObject& other); + EvasObject* operator=(Evas_Object* object); + + operator Evas_Object *(); + + bool IsValid() const + { + Assert(m_object); + return m_object->GetObject() != NULL; + } + + bool DisconnectCallback(IConnection* connection); + void DisconnectAll(); + + template + IConnection* ConnectSmartCallback( + const char* callbackName, + typename EvasObjectShared::SmartConnection::CbType + callback, + Args ... args) + { + Assert(m_object); + return m_object->ConnectSmartCallback(callbackName, callback, args ...); + } + + template + IConnection* ConnectMemberSmartCallback( + const char* callbackName, + typename EvasObjectShared::SmartMemberConnection2::CbType + callback, + ThisType* callee, + ArgType1* arg1, + ArgType2* arg2) + { + Assert(m_object); + Assert(callee); + Assert(callback); + return m_object->ConnectMemberSmartCallback(callbackName, + callback, + callee, + arg1, + arg2); + } + + template + IConnection* ConnectMemberSmartCallback( + const char* callbackName, + typename EvasObjectShared::SmartMemberConnection1::CbType + callback, + ThisType* callee, + ArgType1* arg1) + { + Assert(m_object); + Assert(callee); + Assert(callback); + return m_object->ConnectMemberSmartCallback(callbackName, + callback, + callee, + arg1); + } + + template + IConnection* ConnectEvasCallback( + Evas_Callback_Type callbackType, + typename EvasObjectShared::EvasConnection1::CbType + callback, + ArgType1* arg1, + ArgType2* arg2) + { + Assert(m_object); + return m_object->ConnectEvasCallback(callbackType, callback, arg1, arg2); + } + + template + IConnection* ConnectEvasCallback( + Evas_Callback_Type callbackType, + typename EvasObjectShared::EvasConnection1::CbType + callback, + ArgType1* arg1) + { + Assert(m_object); + return m_object->ConnectEvasCallback(callbackType, callback, arg1); + } + + template + IConnection* ConnectMemberEvasCallback( + Evas_Callback_Type callbackType, + typename EvasObjectShared::EvasMemberConnection1::CbType + callback, + ThisType* callee, + ArgType1* arg1) + { + Assert(m_object); + Assert(callee); + Assert(callback); + return m_object->ConnectMemberEvasCallback(callbackType, + callback, + callee, + arg1); + } + + template + IConnection* ConnectMemberEvasCallback( + Evas_Callback_Type callbackType, + typename EvasObjectShared::EvasMemberConnection2::CbType + callback, + ThisType* callee, + ArgType1* arg1, + ArgType2* arg2) + { + Assert(m_object); + Assert(callee); + Assert(callback); + return m_object->ConnectMemberEvasCallback(callbackType, + callback, + callee, + arg1, + arg2); + } + + private: + EvasObjectSharedPtr m_object; +}; + +}//namespace +}//namespace + + +#endif //WRT_SRC_DOMAIN_EFL_EVAS_OBJECT_H + diff --git a/modules/popup/include/dpl/popup/popup.h b/modules/popup/include/dpl/popup/popup.h new file mode 100644 index 0000000..59fd854 --- /dev/null +++ b/modules/popup/include/dpl/popup/popup.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file popup.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief This is popup inteface declaration + */ + +#ifndef WRT_SRC_POPUP_POPUP_H_ +#define WRT_SRC_POPUP_POPUP_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL { +namespace Popup { + +struct AnswerCallbackData +{ + int buttonAnswer; + DPL::Optional password; + bool chackState; + DPL::Event::LoopHandle loopHandle; +}; + +class PopupManager; +class IPopup; +typedef DPL::SharedPtr IPopupPtr; + +class IPopup : protected DPL::EnableSharedFromThis +{ + public: + virtual void SetTitle(const std::string &title) = 0; + /*The object is deleted automatically after rendered */ + virtual void Append(PopupObject::IPopupObject *object) = 0; + + protected: + typedef void (*PopupCallbackType)(const AnswerCallbackData& answer, + void *data); + virtual void Show(PopupCallbackType callback, + void* data) = 0; + virtual ~IPopup() + { + } + + private: + friend class PopupManager; + friend class DPL::SharedPtr; +}; + +} // namespace Popup +} // namespace DPL + +#endif //WRT_SRC_POPUP_POPUP_H_ diff --git a/modules/popup/include/dpl/popup/popup_controller.h b/modules/popup/include/dpl/popup/popup_controller.h new file mode 100644 index 0000000..3550b3a --- /dev/null +++ b/modules/popup/include/dpl/popup/popup_controller.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file popup_controller.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @bref Header file for popup controller + */ + +/** + * To display a popup from a given class: + * + **class ABC + **{ + * void AskUser() + * { + * } + * + * void DoSomeLogicWithAnswer() + * { + * } + **}; + * + * ... update the class to something simmilar: + * + **class ABC : Popup::PopupControllerUser + **{ + * void AskUser() { + * using namespace Popup; + * CtrlPopupPtr popup = + * PopupControllerSingletion::Instance().CreatePopup(); + * popup->SetTitle("Title"); + * popup->SetContent("Content"); + * popup->AddButton("name1", 1); + * popup->AddButton("name2", 2); + * ListenForAnswer(popup); + * ShowPopupEvent event(popup, + * MakeAnswerCallback(this, + * &ABC::DoSomeLogicWithAnswer)); + * CONTROLLER_POST_EVENT(PopupController, event); + * } + * + * void DoSomeLogicWithAnswer(Popup::LabelId answer) { + * if (answer == 1) + * ;//name1 pressed + * else if (answer == 2) + * ;//name2 pressed + * } + **}; + **/ + +#ifndef WRT_SRC_POPUP_CONTROLLER_POPUP_CONTROLLER_H_ +#define WRT_SRC_POPUP_CONTROLLER_POPUP_CONTROLLER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL { +namespace Popup { +typedef int LabelId; + +struct PopupAnswerCallback +{ + typedef void (PopupAnswerCallback::*MemberPtr)(); + + void* callee; + MemberPtr member; + void (*callTranslator)(PopupAnswerCallback* callData, + const AnswerCallbackData& answer); + + void Call(const AnswerCallbackData& answer) + { + callTranslator(this, answer); + } +}; + +class PopupController; +class CtrlPopup; + +typedef DPL::SharedPtr CtrlPopupPtr; + +DECLARE_GENERIC_EVENT_3(PopupAnswerEvent, + CtrlPopupPtr, + PopupAnswerCallback, + AnswerCallbackData) +DECLARE_GENERIC_EVENT_3(ShowPopupEvent, + CtrlPopupPtr, + PopupAnswerCallback, + DPL::Event::LoopHandle) + +class CtrlPopup : public DPL::Event::EventSupport, + protected DPL::EnableSharedFromThis +{ + public: + void SetTitle(const std::string &title); + void Append(PopupObject::IPopupObject *object); + + private: + friend class PopupController; + friend class DPL::SharedPtr; + + explicit CtrlPopup(IPopupPtr popup); + ~CtrlPopup(); + void EmitAnswer(const AnswerCallbackData& answer); + + IPopupPtr m_popup; + PopupAnswerCallback m_callback; + DPL::Event::LoopHandle m_loopHandle; +}; + +class PopupController : + public DPL::Event::Controller::Type> +{ + public: + CtrlPopupPtr CreatePopup() const; + + void setExternalCanvas(void* canvas) + { + m_canvas = canvas; + } + void* getExternalCanvas() const + { + return m_canvas; + } + void* m_canvas; + + protected: + virtual void OnEventReceived(const ShowPopupEvent& event); + PopupController(); + + private: + static void StaticOnAnswerReceived(const AnswerCallbackData& answer, + CtrlPopupPtr* popup); +}; + +class PopupControllerUser : DPL::Event::EventListener +{ + template + struct PopupAnswerCallbackCreator + { + typedef void (Type::*MemberPtr)(const AnswerCallbackData& answer); + union Caster + { + MemberPtr specific; + PopupAnswerCallback::MemberPtr generic; + }; + + static PopupAnswerCallback Create(Type* callee, + MemberPtr callback) + { + PopupAnswerCallback callData; + + callData.callee = callee; + + Caster caster; + caster.specific = callback; + callData.member = caster.generic; + + callData.callTranslator = + &PopupAnswerCallbackCreator::MemberCallbackTranslator; + + return callData; + } + + static void MemberCallbackTranslator(PopupAnswerCallback* callData, + const AnswerCallbackData& answer) + { + Type* typedThis = static_cast(callData->callee); + Caster caster; + caster.generic = callData->member; + MemberPtr typedCallback = caster.specific; + (typedThis->*typedCallback)(answer); + } + }; + + protected: + virtual void OnEventReceived(const PopupAnswerEvent& event); + void ListenForAnswer(CtrlPopupPtr popup); + + template + PopupAnswerCallback MakeAnswerCallback(Type* This, + void (Type::*callback) + (const AnswerCallbackData &)) + { + return PopupAnswerCallbackCreator::Create(This, callback); + } +}; + +typedef DPL::Singleton PopupControllerSingleton; + +} //namespace Popup +} //namespace DPL + +#endif //WRT_SRC_POPUP_CONTROLLER_POPUP_CONTROLLER_H_ diff --git a/modules/popup/include/dpl/popup/popup_manager.h b/modules/popup/include/dpl/popup/popup_manager.h new file mode 100644 index 0000000..512ddd6 --- /dev/null +++ b/modules/popup/include/dpl/popup/popup_manager.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file popup_manager.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief This is popup_manager declaration file + */ + +#ifndef WRT_SRC_POPUP_POPUP_MANAGER_H_ +#define WRT_SRC_POPUP_POPUP_MANAGER_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace DPL { +namespace Popup { + +class PopupManager : DPL::Noncopyable +{ + template + struct TemplatedPopupCallback + { + typedef void (*Type)(const AnswerCallbackData& answer, ArgType* data); + }; + + public: + PopupManager() : m_initialized(false) {} + ~PopupManager() + { + Assert(!m_initialized); + } + void Initialize (PopupRendererPtr creator); + void Deinitialize(); + void SetPopupRenderer (PopupRendererPtr creator); + IPopupPtr CreatePopup(); + + template + void RunAsyncWithArgType(IPopupPtr popup, + typename TemplatedPopupCallback::Type callback, + ArgType* argument) + { + Assert(callback); + WrapCbAndArg* wrapped = + new WrapCbAndArg(callback, argument); + popup->Show(&CallbackArgTypeTranslator, wrapped); + } + + void Show(IPopupPtr popup, + IPopup::PopupCallbackType callback, + void* userdata) + { + popup->Show(callback, userdata); + } + + void setExternalCanvas(void* externalCanvas) + { + Assert(m_initialized && "Manger should be initialized"); + m_popupRenderer->setExternalCanvas(externalCanvas); + } + + private: + template + struct WrapCbAndArg + { + WrapCbAndArg(typename TemplatedPopupCallback::Type cb, + ArgType* arg) : + callback(cb), + argument(arg) + { + } + + typename TemplatedPopupCallback::Type callback; + ArgType* argument; + }; + + template + static void CallbackArgTypeTranslator(const AnswerCallbackData & answer, + void* data) + { + WrapCbAndArg* wrapped = + static_cast< WrapCbAndArg* >(data); + wrapped->callback(answer, wrapped->argument); + delete wrapped; + } + + bool m_initialized; + PopupRendererPtr m_popupRenderer; +}; + +typedef DPL::Singleton PopupManagerSingleton; + +} // namespace Popup +} // namespace DPL + +#endif //WRT_SRC_POPUP_POPUP_MANAGER_H_ diff --git a/modules/popup/include/dpl/popup/popup_object.h b/modules/popup/include/dpl/popup/popup_object.h new file mode 100644 index 0000000..5eddc49 --- /dev/null +++ b/modules/popup/include/dpl/popup/popup_object.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file popup_object.h + * @author Justyna Mejzner (j.mejzner@samsung.com) + * @version 1.0 + * @brief This is declaration file of PopupObject + */ + +#ifndef WRT_SRC_POPUP_POPUPOBJECT_H_ +#define WRT_SRC_POPUP_POPUPOBJECT_H_ + +#include + +#include +#include + +namespace DPL { +namespace Popup { + +namespace PopupObject { +class IPopupObject; +class PopupObjectBase; +class Button; +class Label; +class Check; + +typedef std::list PopupObjects; + +enum PopupType +{ + BUTTON, + LABEL, + CHECK +}; + +class IPopupObject +{ + public: + virtual Button* asButton() = 0; + virtual Label* asLabel() = 0; + virtual Check* asCheck() = 0; + virtual PopupType getType() const = 0; + virtual ~IPopupObject() + { + } +}; + +class PopupObjectBase : public IPopupObject +{ + public: + virtual Button* asButton() + { + Assert("wrong type"); + return NULL; + } + virtual Label* asLabel() + { + Assert("wrong type"); + return NULL; + } + virtual Check* asCheck() + { + Assert("wrong type"); + return NULL; + } + + PopupType getType() const + { + return m_type; + } + + protected: + PopupObjectBase(PopupType type) : m_type(type) + { + } + + PopupType m_type; +}; + +class Button : public PopupObjectBase +{ + public: + Button(const std::string& label, + int labelId) : + PopupObjectBase(BUTTON), + m_label(label), + m_labelId(labelId) + { + } + + Button* asButton() + { + return this; + } + + const std::string& getLabel() const + { + return m_label; + } + + int getLabelId() const + { + return m_labelId; + } + + private: + std::string m_label; + int m_labelId; +}; + +class Label : public PopupObjectBase +{ + public: + Label(const std::string &label) : + PopupObjectBase(LABEL), + m_label(label) + { + } + + Label* asLabel() + { + return this; + } + + const std::string& getLabel() const + { + return m_label; + } + + private: + std::string m_label; +}; + +class Check : public PopupObjectBase +{ + public: + Check(const std::string &label) : + PopupObjectBase(CHECK), + m_checkLabel(label) + { + } + + Check* asCheck() + { + return this; + } + + const std::string& getCheckLabel() const + { + return m_checkLabel; + } + + private: + std::string m_checkLabel; +}; +} /*PopupObject*/ + +}//namespace Popup +}//namespace DPL + +#endif //WRT_SRC_POPUP_POPUPOBJECT_H_ diff --git a/modules/popup/include/dpl/popup/popup_renderer.h b/modules/popup/include/dpl/popup/popup_renderer.h new file mode 100644 index 0000000..593c231 --- /dev/null +++ b/modules/popup/include/dpl/popup/popup_renderer.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file popup_renderer.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief This is declaration file of PopupRenderer + */ + +#ifndef WRT_SRC_POPUP_POPUP_RENDERER_H_ +#define WRT_SRC_POPUP_POPUP_RENDERER_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace DPL { +namespace Popup { + +class PopupRenderer : public DPL::EnableSharedFromThis +{ + public: + PopupRenderer(); + ~PopupRenderer(); + void Initialize(); + void Deinitialize(); + IPopupPtr CreatePopup(); + virtual void setExternalCanvas(void* externalCanvas); + protected: + class Popup; + typedef DPL::SharedPtr PopupPtr; + + class Popup : public IPopup + { + public: + typedef std::map ButtonMap; + virtual void SetTitle(const std::string &title) + { + LogDebug(title); + Assert(m_title.empty()); + m_title = title; + } + + virtual void Append(PopupObject::IPopupObject *object) + { + m_popupObjectList.push_back(object); + } + + virtual void Show(IPopup::PopupCallbackType callback, + void* data) + { + Assert(callback); + m_data = data; + m_callback = callback; + m_renderer->Render(DPL::StaticPointerCast(SharedFromThis())); + } + + const std::string& GetTitle() const + { + return m_title; + } + + PopupObject::PopupObjects& GetPopupObjects() + { + return m_popupObjectList; + } + + virtual ~Popup() + { + FOREACH(it, m_popupObjectList) { + delete *it; + } + } + + private: + friend class PopupRenderer; + friend class DPL::SharedPtr; + friend class PopupObjectTheme; + + Popup(DPL::SharedPtr renderer) : m_renderer(renderer) + { + } + + void ForwardAnswer(const AnswerCallbackData & answer) const + { + m_callback(answer, m_data); + } + + std::string m_title; + void* m_data; + IPopup::PopupCallbackType m_callback; + PopupObject::PopupObjects m_popupObjectList; + DPL::SharedPtr m_renderer; + }; + + private: + void Render (PopupPtr popup); + + class Impl; + Impl* m_impl; +}; + +typedef DPL::SharedPtr PopupRendererPtr; + +} // namespace Popup +} // namespace DPL + +#endif //WRT_SRC_POPUP_POPUP_RENDERER_H_ diff --git a/modules/popup/src/evas_object.cpp b/modules/popup/src/evas_object.cpp new file mode 100644 index 0000000..aa41500 --- /dev/null +++ b/modules/popup/src/evas_object.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file evas_object.cpp + * @author Lukasz Wrzosek (l.wrzosel@samsung.com) + * @version 1.0 + * @brief This file is the implementation for Evas_Object wrapper from Efl. + */ + +#include +#include + +namespace DPL { +namespace Popup { + +Evas_Object* EvasObject::IConnection::GetEvasObject() +{ + return m_object->GetObject(); +} + +void EvasObject::IConnection::Disconnect() +{ + m_object->DisconnectCallback(this); +} + +EvasObject::IConnection::IConnection(EvasObject::EvasObjectShared* object) : + m_object(object) +{ +} + +void EvasObject::IConnection::SmartCallbackWrapper(void* data, + Evas_Object* /*object*/, + void* event_info) +{ + Assert(data); + IConnection* Calle = static_cast(data); + Calle->Call(event_info); +} + +void EvasObject::IConnection::EvasCallbackWrapper(void* data, + Evas* /*evas*/, + Evas_Object* /*object*/, + void* event_info) +{ + Assert(data); + IConnection* Calle = static_cast(data); + Calle->Call(event_info); +} + +Evas_Object* EvasObject::EvasObjectShared::GetObject() +{ + return m_object; +} + +EvasObject::EvasObjectShared::SmartConnectionBase::SmartConnectionBase( + const std::string& name, + EvasObject::EvasObjectShared* object) : + IConnection(object), + m_callbackName(name) +{ +} + +void EvasObject::EvasObjectShared::SmartConnectionBase::ConnectPrv() +{ + evas_object_smart_callback_add(GetEvasObject(), + m_callbackName.c_str(), + &IConnection::SmartCallbackWrapper, this); +} + +void EvasObject::EvasObjectShared::SmartConnectionBase::DisconnectPrv() +{ + evas_object_smart_callback_del(GetEvasObject(), + m_callbackName.c_str(), + &IConnection::SmartCallbackWrapper); +} + +EvasObject::EvasObjectShared::EvasConnectionBase::EvasConnectionBase( + Evas_Callback_Type type, + EvasObject::EvasObjectShared* object) : + IConnection(object), + m_callbackType(type) +{ +} + +void EvasObject::EvasObjectShared::EvasConnectionBase::ConnectPrv() +{ + evas_object_event_callback_add( + GetEvasObject(), m_callbackType, &IConnection::EvasCallbackWrapper, + this); +} + +void EvasObject::EvasObjectShared::EvasConnectionBase::DisconnectPrv() +{ + evas_object_event_callback_del_full( + GetEvasObject(), m_callbackType, &IConnection::EvasCallbackWrapper, + this); +} + +EvasObject::EvasObjectShared::EvasObjectShared() : + m_object(NULL) +{ +} + +EvasObject::EvasObjectShared::EvasObjectShared(Evas_Object* object) : + m_object(object) +{ + Assert(m_object); + evas_object_event_callback_add(m_object, + EVAS_CALLBACK_DEL, + &StaticOnDelEvent, + this); +} + +void EvasObject::EvasObjectShared::SetObject(Evas_Object* object) +{ + Assert(m_object == NULL); + Assert(object != NULL); + m_object = object; + evas_object_event_callback_add(m_object, + EVAS_CALLBACK_DEL, + &StaticOnDelEvent, + this); +} + +EvasObject::EvasObjectShared::~EvasObjectShared() +{ + if (m_object) { + DisconnectAll(); + evas_object_event_callback_del(m_object, + EVAS_CALLBACK_DEL, + &StaticOnDelEvent); + m_object = NULL; + } +} + +bool EvasObject::EvasObjectShared::DisconnectCallback(IConnection* connection) +{ + IConnectionsSet::iterator it = m_connections.find(connection); + if (it != m_connections.end()) { + (*it)->DisconnectPrv(); + delete connection; + m_connections.erase(it); + return true; + } + return false; +} + +void EvasObject::EvasObjectShared::DisconnectAll() +{ + FOREACH(it, m_connections) + { + (*it)->DisconnectPrv(); + delete *it; + } + m_connections.clear(); +} + +void EvasObject::EvasObjectShared::StaticOnDelEvent(void* data, + Evas* /*e*/, + Evas_Object* /*o*/, + void* /*ev*/) +{ + Assert(data); + EvasObjectShared* This = static_cast(data); + if (This->m_object) { + evas_object_event_callback_del(This->m_object, + EVAS_CALLBACK_DEL, + &StaticOnDelEvent); + This->DisconnectAll(); + This->m_object = NULL; + } +} + +EvasObject::EvasObject() : + m_object(new EvasObjectShared()) +{ +} + +EvasObject::EvasObject(Evas_Object* object) : + m_object(new EvasObjectShared(object)) +{ +} + +EvasObject::EvasObject(const EvasObject& other) : + m_object(other.m_object) +{ +} + +//this destructor must be here to let pimpl with shared_ptr work without warning +EvasObject::~EvasObject() +{ +} + +EvasObject& EvasObject::operator=(const EvasObject& other) +{ + Assert(m_object); + m_object = other.m_object; + return *this; +} + +EvasObject* EvasObject::operator=(Evas_Object* object) +{ + Assert(m_object); + m_object->SetObject(object); + return this; +} + +bool EvasObject::DisconnectCallback(IConnection* connection) +{ + Assert(m_object); + return m_object->DisconnectCallback(connection); +} + +void EvasObject::DisconnectAll() +{ + Assert(m_object); + m_object->DisconnectAll(); +} + +EvasObject::operator Evas_Object *() +{ + Assert(m_object); + return m_object->GetObject(); +} + +} // namespace DPL +} // namespace Popup diff --git a/modules/popup/src/popup_controller.cpp b/modules/popup/src/popup_controller.cpp new file mode 100644 index 0000000..e8163be --- /dev/null +++ b/modules/popup/src/popup_controller.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file popup_controller.cpp + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @bref Implementation file for popup controller + */ + +#include + +#include +#include +#include +IMPLEMENT_SINGLETON(DPL::Popup::PopupController) + +namespace DPL { +namespace Popup { + +void CtrlPopup::SetTitle(const std::string &title) +{ + Assert(m_popup); + m_popup->SetTitle(title); +} + +void CtrlPopup::Append(PopupObject::IPopupObject *object) +{ + Assert(m_popup); + m_popup->Append(object); +} + +CtrlPopup::CtrlPopup(IPopupPtr popup) : + m_popup(popup), + m_callback() +{ + Assert(m_popup); +} + +CtrlPopup::~CtrlPopup() +{ +} + +void CtrlPopup::EmitAnswer(const AnswerCallbackData & answer) +{ + AnswerCallbackData l_answer = answer; + l_answer.loopHandle = m_loopHandle; + PopupAnswerEvent event(SharedFromThis(), m_callback, l_answer); + DPL::Event::EventSupport::EmitEvent(event); +} + +PopupController::PopupController() : m_canvas(NULL) +{ +} + +CtrlPopupPtr PopupController::CreatePopup() const +{ + return CtrlPopupPtr( + new CtrlPopup(PopupManagerSingleton::Instance().CreatePopup())); +} + +void PopupController::OnEventReceived(const ShowPopupEvent& event) +{ + CtrlPopupPtr popup = event.GetArg0(); + popup->m_callback = event.GetArg1(); + popup->m_loopHandle = event.GetArg2(); + + //pass canvas from controller to manager + //canvas is not passed earlier because value wasn't set properly + PopupManagerSingleton::Instance().setExternalCanvas(getExternalCanvas()); + + PopupManagerSingleton::Instance().RunAsyncWithArgType( + popup->m_popup, + &PopupController::StaticOnAnswerReceived, + new CtrlPopupPtr(popup)); +} + +void PopupController::StaticOnAnswerReceived(const AnswerCallbackData & answer, + CtrlPopupPtr* popup) +{ + Assert(popup != NULL); + (*popup)->EmitAnswer(answer); + delete popup; // Just SharedPtr is destroyed, not the popup itself +} + +void PopupControllerUser::OnEventReceived(const PopupAnswerEvent& event) +{ + //Here we are in a proper context to call the callback + PopupAnswerCallback answerCall = event.GetArg1(); + AnswerCallbackData answer = event.GetArg2(); + answerCall.Call(answer); + event.GetArg0()->DPL::Event::EventSupport::RemoveListener(this); +} + +void PopupControllerUser::ListenForAnswer(CtrlPopupPtr popup) +{ + popup->DPL::Event::EventSupport::AddListener(this); +} +} //namespace Popup +} //namespace DPL diff --git a/modules/popup/src/popup_manager.cpp b/modules/popup/src/popup_manager.cpp new file mode 100644 index 0000000..6e838f6 --- /dev/null +++ b/modules/popup/src/popup_manager.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file popup_manager.cpp + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief This is popup_manager implementation file + */ + +#include +#include + +#include +#include +#include +#include + +IMPLEMENT_SINGLETON(DPL::Popup::PopupManager) + +namespace DPL { +namespace Popup { + +void PopupManager::Initialize(PopupRendererPtr renderer) +{ + Assert(!m_initialized); + m_popupRenderer = renderer; + m_popupRenderer->Initialize(); + m_initialized = true; +} + +void PopupManager::Deinitialize() +{ + m_popupRenderer->Deinitialize(); + Assert(m_initialized); + m_popupRenderer.Reset(); + m_initialized = false; +} + +IPopupPtr PopupManager::CreatePopup() +{ + Assert(m_initialized); + return m_popupRenderer->CreatePopup(); +} + +} // namespace Popup +} // namespace DPL diff --git a/modules/popup/src/popup_renderer.cpp b/modules/popup/src/popup_renderer.cpp new file mode 100644 index 0000000..7a45c77 --- /dev/null +++ b/modules/popup/src/popup_renderer.cpp @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file popup_renderer.cpp + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief This is efl specific implementation for PopupRenderer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL { + +namespace { +using namespace Popup; +const char* EDJ_NAME = "/usr/share/edje/ace/generic_popup.edj"; +const char* POPUP_LAYOUT1 = "popup_layout1"; +const char* POPUP_LAYOUT2 = "popup_layout2"; +const char* BUTTON_RESPONSE_CALLBACK_NAME = "response"; +const char* CHANGED_CALLBACK_NAME = "changed"; +const unsigned int MAX_NUMBER_OF_VERTICAL = 2; + +Evas_Object* create_layout_main(Evas_Object* parent, int totalV) +{ + Evas_Object *layout = elm_layout_add(parent); + + if (totalV == 1) { + elm_layout_file_set(layout, EDJ_NAME, POPUP_LAYOUT1); + } else if (totalV == 2) { + elm_layout_file_set(layout, EDJ_NAME, POPUP_LAYOUT2); + } else { + Assert("popup needs define new group in the edc"); + } + + evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, + EVAS_HINT_EXPAND); + return layout; +} +} //namespace + +namespace Popup { +class PopupRenderer::Impl +{ + public: + Impl() : + m_popupsToRender(), + m_current(), + m_initialized(false) + { + } + + ~Impl() + { + Assert(!m_initialized); + } + + void Initialize() + { + Assert(!m_initialized); + m_initialized = true; + } + + void Deinitialize() + { + Assert(m_initialized); + m_current.Reset(NULL); + while (!m_popupsToRender.empty()) { + m_popupsToRender.pop(); + } + m_initialized = false; + } + + void ButtonCallback(EvasObject::IConnection* /*connection*/, + void* event_info, + void* /*unused*/) + { + LogInfo("ButtonCallback"); + Assert(m_initialized); + AnswerCallbackData answerData; + + answerData.buttonAnswer = int(event_info); + answerData.chackState = m_checkState; + answerData.password = m_password; + m_current->ForwardAnswer(answerData); + m_current.Reset(); + + FOREACH(it, m_createdObjects) + { + if (it->IsValid()) { + evas_object_del(*it); + } + } + m_createdObjects.clear(); + m_checkState = false; + DoRender(); + } + + void CheckCallback(EvasObject::IConnection* connection, + void* /*event_info*/, + void* /* unused */) + { + m_checkState = + elm_check_state_get(connection->GetEvasObject()); + } + + void Render (PopupPtr popup) + { + Assert(m_initialized); + m_popupsToRender.push(popup); + DoRender(); + } + + void DoRender(const PopupObject::Label& object, + EvasObject& parent, + EvasObject& layout, + int themeIndex) + { + EvasObject label(elm_label_add(parent)); + + elm_object_style_set(label, "popup_description/default"); + elm_label_line_wrap_set(label, ELM_WRAP_WORD); + elm_object_text_set(label, object.getLabel().c_str()); + evas_object_size_hint_weight_set(label, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_align_set(label, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_show(label); + + elm_layout_content_set( + layout, + DPL::lexical_cast(themeIndex).c_str(), + label); + m_createdObjects.push_back(label); + } + + void DoRender(const PopupObject::Check& object, + EvasObject& parent, + EvasObject& layout, + int themeIndex) + { + EvasObject check(elm_check_add(parent)); + + evas_object_size_hint_align_set(check, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(check, EVAS_HINT_EXPAND, 0.0); + elm_object_text_set(check, + object.getCheckLabel().c_str()); + elm_layout_content_set( + layout, + DPL::lexical_cast(themeIndex).c_str(), + check); + + check.ConnectMemberSmartCallback(CHANGED_CALLBACK_NAME, + &Impl::CheckCallback, + this, + static_cast(NULL)); + evas_object_show(check); + m_createdObjects.push_back(check); + } + + void DoRender(const PopupObject::Button& object, + EvasObject &parent) + { + elm_popup_buttons_add(parent, + 1, + object.getLabel().c_str(), + object.getLabelId(), + NULL); + parent.ConnectMemberSmartCallback( + BUTTON_RESPONSE_CALLBACK_NAME, + &Impl::ButtonCallback, + this, + static_cast(NULL)); + } + + void DoRender(const PopupObject::Button& object1, + const PopupObject::Button& object2, + EvasObject &parent) + { + elm_popup_buttons_add(parent, + 2, + object1.getLabel().c_str(), + object1.getLabelId(), + object2.getLabel().c_str(), + object2.getLabelId(), + NULL); + parent.ConnectMemberSmartCallback( + BUTTON_RESPONSE_CALLBACK_NAME, + &Impl::ButtonCallback, + this, + static_cast(NULL)); + } + + void DoRender(const PopupObject::Button& object1, + const PopupObject::Button& object2, + const PopupObject::Button& object3, + EvasObject &parent) + { + elm_popup_buttons_add(parent, + 3, + object1.getLabel().c_str(), + object1.getLabelId(), + object2.getLabel().c_str(), + object2.getLabelId(), + object3.getLabel().c_str(), + object3.getLabelId(), + NULL); + parent.ConnectMemberSmartCallback( + BUTTON_RESPONSE_CALLBACK_NAME, + &Impl::ButtonCallback, + this, + static_cast(NULL)); + } + + EvasObject getBaseObject() + { + if (getExternalCanvas() == NULL) { + LogInfo("Create old style popup"); + EvasObject win(elm_win_add(NULL, "Popup", ELM_WIN_DIALOG_BASIC)); + elm_win_borderless_set(win, EINA_TRUE); + elm_win_alpha_set(win, EINA_TRUE); + elm_win_raise(win); + { + int w, h, x, y; + ecore_x_window_geometry_get(ecore_x_window_root_first_get(), + &x, + &y, + &w, + &h); + evas_object_resize(win, w, h); + evas_object_move(win, x, y); + } + m_createdObjects.push_back(win); + evas_object_show(win); + return win; + } else { + LogInfo("Create new style popup"); + EvasObject win(getExternalCanvas()); + evas_object_show(win); + return win; + } + } + + void DoRender() + { + if (!m_current && !m_popupsToRender.empty()) { + m_current = m_popupsToRender.front(); + m_popupsToRender.pop(); + + m_themeIndexV = 0; + + // preprocessing + std::vector countPopupObjects = {0 /* PopupObject::BUTTON */, + 0 /* PopupObject::LABEL */, + 0 /* PopupObject::CHECK */}; + FOREACH(it, m_current->GetPopupObjects()) { + Assert((*it)->getType() < countPopupObjects.size() && + "Wrong PopupObject assigned"); + countPopupObjects[(*it)->getType()]++; + } + int needsIndexV = countPopupObjects[PopupObject::LABEL] + + countPopupObjects[PopupObject::CHECK]; + + EvasObject win = getBaseObject(); + EvasObject main(elm_popup_add(win)); + + evas_object_size_hint_weight_set(main, + EVAS_HINT_EXPAND, + EVAS_HINT_EXPAND); + elm_popup_title_label_set(main, + m_current->GetTitle().c_str()); + + m_createdObjects.push_back(main); + std::vector buttonObjectList; + EvasObject layout(create_layout_main(main, needsIndexV)); + m_createdObjects.push_back(layout); + + FOREACH(it, m_current->GetPopupObjects()) { + switch ((*it)->getType()) { + case PopupObject::BUTTON: + buttonObjectList.push_back(*(*it)->asButton()); + break; + case PopupObject::LABEL: + DoRender(*(*it)->asLabel(), + main, + layout, + m_themeIndexV++); + break; + case PopupObject::CHECK: + DoRender(*(*it)->asCheck(), + main, + layout, + m_themeIndexV++); + break; + default: + Assert("incorrect type"); + } + Assert(m_themeIndexV <= MAX_NUMBER_OF_VERTICAL); + } + elm_popup_content_set(main, + layout); + + // show buution + switch(buttonObjectList.size()) { + case 0: + LogInfo("no button"); + break; + case 1: + DoRender(buttonObjectList.at(0), + main); + break; + case 2: + DoRender(buttonObjectList.at(0), + buttonObjectList.at(1), + main); + break; + case 3: + DoRender(buttonObjectList.at(0), + buttonObjectList.at(1), + buttonObjectList.at(2), + main); + break; + default: + Assert("incorrect button number"); + break; + } + + evas_object_show(main); + } + } + + void setExternalCanvas(void* externalCanvas) + { + m_externalCanvas = static_cast(externalCanvas); + } + + Evas_Object* getExternalCanvas() const + { + return m_externalCanvas; + } + + std::queue m_popupsToRender; + std::list m_createdObjects; + PopupPtr m_current; + bool m_initialized; + bool m_checkState; + DPL::Optional m_password; + unsigned int m_themeIndexV; + + private: + Evas_Object* m_externalCanvas; +}; + +PopupRenderer::PopupRenderer() +{ + m_impl = new PopupRenderer::Impl(); +} + +PopupRenderer::~PopupRenderer() +{ + delete m_impl; +} + +void PopupRenderer::Initialize() +{ + Assert(m_impl); + m_impl->Initialize(); +} + +void PopupRenderer::Deinitialize() +{ + Assert(m_impl); + m_impl->Deinitialize(); +} + +IPopupPtr PopupRenderer::CreatePopup() +{ + return DPL::StaticPointerCast(IPopupPtr + (new Popup(SharedFromThis()))); +} + +void PopupRenderer::Render(PopupPtr popup) +{ + m_impl->Render(popup); +} + +void PopupRenderer::setExternalCanvas(void* externalCanvas) +{ + m_impl->setExternalCanvas(externalCanvas); +} + +} // namespace Popup +} // namespace DPL diff --git a/modules/rpc/config.cmake b/modules/rpc/config.cmake new file mode 100644 index 0000000..9f3d810 --- /dev/null +++ b/modules/rpc/config.cmake @@ -0,0 +1,52 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file config.cmake +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +SET(DPL_RPC_SOURCES + ${PROJECT_SOURCE_DIR}/modules/rpc/src/abstract_rpc_connection.cpp + ${PROJECT_SOURCE_DIR}/modules/rpc/src/abstract_rpc_connector.cpp + ${PROJECT_SOURCE_DIR}/modules/rpc/src/generic_rpc_connection.cpp + ${PROJECT_SOURCE_DIR}/modules/rpc/src/generic_socket_rpc_client.cpp + ${PROJECT_SOURCE_DIR}/modules/rpc/src/generic_socket_rpc_connection.cpp + ${PROJECT_SOURCE_DIR}/modules/rpc/src/generic_socket_rpc_server.cpp + ${PROJECT_SOURCE_DIR}/modules/rpc/src/unix_socket_rpc_client.cpp + ${PROJECT_SOURCE_DIR}/modules/rpc/src/unix_socket_rpc_connection.cpp + ${PROJECT_SOURCE_DIR}/modules/rpc/src/unix_socket_rpc_server.cpp + PARENT_SCOPE +) + +SET(DPL_RPC_HEADERS + ${PROJECT_SOURCE_DIR}/modules/rpc/include/dpl/rpc/abstract_rpc_connection.h + ${PROJECT_SOURCE_DIR}/modules/rpc/include/dpl/rpc/abstract_rpc_connector.h + ${PROJECT_SOURCE_DIR}/modules/rpc/include/dpl/rpc/generic_rpc_connection.h + ${PROJECT_SOURCE_DIR}/modules/rpc/include/dpl/rpc/generic_socket_rpc_client.h + ${PROJECT_SOURCE_DIR}/modules/rpc/include/dpl/rpc/generic_socket_rpc_connection.h + ${PROJECT_SOURCE_DIR}/modules/rpc/include/dpl/rpc/generic_socket_rpc_server.h + ${PROJECT_SOURCE_DIR}/modules/rpc/include/dpl/rpc/rpc_function.h + ${PROJECT_SOURCE_DIR}/modules/rpc/include/dpl/rpc/unix_socket_rpc_client.h + ${PROJECT_SOURCE_DIR}/modules/rpc/include/dpl/rpc/unix_socket_rpc_connection.h + ${PROJECT_SOURCE_DIR}/modules/rpc/include/dpl/rpc/unix_socket_rpc_server.h + PARENT_SCOPE +) + +SET(DPL_RPC_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/modules/rpc/include + PARENT_SCOPE +) diff --git a/modules/rpc/include/dpl/rpc/abstract_rpc_connection.h b/modules/rpc/include/dpl/rpc/abstract_rpc_connection.h new file mode 100644 index 0000000..6f1fd63 --- /dev/null +++ b/modules/rpc/include/dpl/rpc/abstract_rpc_connection.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_rpc_connection.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file for abstract RPC connection + */ +#ifndef DPL_ABSTRACT_RPC_CONNECTION_H +#define DPL_ABSTRACT_RPC_CONNECTION_H + +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace RPC +{ +namespace AbstractRPCConnectionEvents +{ +/** + * Asynchronous call event + */ +DECLARE_GENERIC_EVENT_1(AsyncCallEvent, RPCFunction) + +/** + * Connection closed event + */ +DECLARE_GENERIC_EVENT_0(ConnectionClosedEvent) + +/** + * Connection broken event + */ +DECLARE_GENERIC_EVENT_0(ConnectionBrokenEvent) +} // namespace AbstractRPCConnectionEvents + +class AbstractRPCConnection + : public DPL::Event::EventSupport, + public DPL::Event::EventSupport, + public DPL::Event::EventSupport +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, AsyncCallFailed) + DECLARE_EXCEPTION_TYPE(Base, PingFailed) + }; + +public: + virtual ~AbstractRPCConnection() {} + + /** + * Call asynchronously RPC function + * + * @param function COnstant reference to RPC function to call + * @return none + */ + virtual void AsyncCall(const RPCFunction &function) = 0; + + /** + * Ping RPC connection + * This will send a ping/pong packet over connection to ensure it is alive + * If any errors are to occur proper events will be emitted + * + * @return none + */ + virtual void Ping() = 0; +}; + +/** + * Abstract connection ID used to represent connection being established + * or RPC server accepting connection + */ +typedef void *AbstractRPCConnectionID; + +} +} // namespace DPL + +#endif // DPL_ABSTRACT_RPC_CONNECTION_H diff --git a/modules/rpc/include/dpl/rpc/abstract_rpc_connector.h b/modules/rpc/include/dpl/rpc/abstract_rpc_connector.h new file mode 100644 index 0000000..38c1e97 --- /dev/null +++ b/modules/rpc/include/dpl/rpc/abstract_rpc_connector.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_rpc_connector.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file for abstract RPC connector + */ +#ifndef DPL_ABSTRACT_RPC_CONNECTOR_H +#define DPL_ABSTRACT_RPC_CONNECTOR_H + +#include +#include +#include + +namespace DPL +{ +namespace RPC +{ +namespace AbstractRPCConnectorEvents +{ +/** + * RPC connection established + */ +DECLARE_GENERIC_EVENT_2(ConnectionEstablishedEvent, AbstractRPCConnectionID, AbstractRPCConnection *) +} // namespace AbstractRPCClientEvents + +class AbstractRPCConnector + : public DPL::Event::EventSupport +{ +public: + /** + * Destructor + */ + virtual ~AbstractRPCConnector() {} +}; + +} +} // namespace DPL + +#endif // DPL_ABSTRACT_RPC_CONNECTOR_H diff --git a/modules/rpc/include/dpl/rpc/generic_rpc_connection.h b/modules/rpc/include/dpl/rpc/generic_rpc_connection.h new file mode 100644 index 0000000..1342859 --- /dev/null +++ b/modules/rpc/include/dpl/rpc/generic_rpc_connection.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_rpc_connection.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file for generic RPC connection + */ +#ifndef DPL_GENERIC_RPC_CONNECTION_H +#define DPL_GENERIC_RPC_CONNECTION_H + +#include +#include +#include +#include + +namespace DPL +{ +namespace RPC +{ + +class GenericRPCConnection + : public AbstractRPCConnection, + private DPL::Socket::WaitableInputOutputExecutionContextSupport +{ +private: + // WaitableInputOutputExecutionContextSupport + virtual void OnInputStreamRead(); + virtual void OnInputStreamClosed(); + virtual void OnInputStreamBroken(); + + ScopedPtr m_inputOutput; + +public: + /** + * Costructor + * + * Abstract waitable input/outobject is acquired by class and destroyed upon desctructor + */ + explicit GenericRPCConnection(AbstractWaitableInputOutput *inputOutput); + virtual ~GenericRPCConnection(); + + virtual void AsyncCall(const RPCFunction &function); + virtual void Ping(); +}; + +} +} // namespace DPL + +#endif // DPL_GENERIC_RPC_CONNECTION_H diff --git a/modules/rpc/include/dpl/rpc/generic_socket_rpc_client.h b/modules/rpc/include/dpl/rpc/generic_socket_rpc_client.h new file mode 100644 index 0000000..122dd0b --- /dev/null +++ b/modules/rpc/include/dpl/rpc/generic_socket_rpc_client.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_socket_rpc_client.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file for generic socket RPC client + */ +#ifndef DPL_GENERIC_SOCKET_RPC_CLIENT_H +#define DPL_GENERIC_SOCKET_RPC_CLIENT_H + +#include +#include +#include + +namespace DPL +{ +namespace RPC +{ + +template +class GenericSocketRPCClient + : public AbstractRPCConnector, + private DPL::Event::EventListener +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, OpenFailed) + DECLARE_EXCEPTION_TYPE(Base, CloseFailed) + }; + +protected: + // Derived class implementations for connection managment + virtual AbstractRPCConnection *OpenSpecificConnection(SocketType *socket) = 0; + +private: + typedef std::set InternalConnectionSet; + InternalConnectionSet m_internalConnectionSet; + + virtual void OnEventReceived(const DPL::Socket::AbstractSocketEvents::ConnectedEvent &event) + { + // Retrieve socket sender + SocketType *socket = static_cast(event.GetSender()); + + LogPedantic("Connection with RPC server established"); + + // Is this connection still tracked ? + // It might have disappeared on close + typename InternalConnectionSet::iterator iterator = m_internalConnectionSet.find(socket); + + if (iterator == m_internalConnectionSet.end()) + { + LogPedantic("RPC client connection socket disappeared"); + return; + } + + // Open specific connection implementation + AbstractRPCConnection *connection = OpenSpecificConnection(socket); + + // Remove internal connection + socket->EventSupport::RemoveListener(this); + m_internalConnectionSet.erase(iterator); + + // Retrieve ID once again + AbstractRPCConnectionID connectionID = static_cast(socket); + + // Inform listeners + DPL::Event::EventSupport:: + EmitEvent(AbstractRPCConnectorEvents::ConnectionEstablishedEvent( + connectionID, connection, EventSender(this)), DPL::Event::EmitMode::Queued); + } + +public: + explicit GenericSocketRPCClient() + { + } + + virtual ~GenericSocketRPCClient() + { + // Always close all connections + CloseAll(); + } + + AbstractRPCConnectionID Open(const Address &socketAddress) + { + LogPedantic("Starting client: " << socketAddress.ToString()); + + // Alloc new socket + SocketType *socket = new SocketType(); + + // Add socket listeners + socket->EventSupport::AddListener(this); + + Try + { + // Open socket + socket->Open(); + + // Start connecting to server + socket->Connect(Address(socketAddress)); + } + Catch (DPL::Socket::AbstractSocket::Exception::Base) + { + // Remove back socket listener + socket->EventSupport::RemoveListener(this); + + // Log debug message + LogPedantic("Cannot connect to: " << socketAddress.ToString()); + + // Problem with client startup + ReThrowMsg(typename Exception::OpenFailed, socketAddress.ToString()); + } + + // Register new internal connection + m_internalConnectionSet.insert(socket); + + // Debug info + LogPedantic("Client started on interface: " << socket->GetLocalAddress().ToString()); + + // Return unique identifier + return static_cast(socket); + } + + void Close(AbstractRPCConnectionID connectionID) + { + LogPedantic("Closing client interface..."); + + // Get socket from ID + SocketType *socket = static_cast(connectionID); + + // Find corresponding internal connection + typename InternalConnectionSet::iterator iterator = m_internalConnectionSet.find(socket); + + if (iterator == m_internalConnectionSet.end()) + return; + + // Close socket + socket->Close(); + + // Remove internal socket + socket->EventSupport::RemoveListener(this); + delete socket; + + m_internalConnectionSet.erase(iterator); + + // Done + LogPedantic("Closed"); + } + + void CloseAll() + { + while (!m_internalConnectionSet.empty()) + Close(static_cast(*m_internalConnectionSet.begin())); + } +}; + +} +} // namespace DPL + +#endif // DPL_GENERIC_SOCKET_RPC_CLIENT_H diff --git a/modules/rpc/include/dpl/rpc/generic_socket_rpc_connection.h b/modules/rpc/include/dpl/rpc/generic_socket_rpc_connection.h new file mode 100644 index 0000000..12e53de --- /dev/null +++ b/modules/rpc/include/dpl/rpc/generic_socket_rpc_connection.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_socket_rpc_connection.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file for generic socket RPC connection + */ +#ifndef DPL_GENERIC_SOCKET_RPC_CONNECTION_H +#define DPL_GENERIC_SOCKET_RPC_CONNECTION_H + +#include + +namespace DPL +{ +namespace RPC +{ + +template +class GenericSocketRPCConnection + : public GenericRPCConnection +{ +protected: + // Private construction with socket acquisition + GenericSocketRPCConnection(SocketType *socket) + : GenericRPCConnection(socket) + { + } +}; + +} +} // namespace DPL + +#endif // DPL_GENERIC_SOCKET_RPC_CONNECTION_H diff --git a/modules/rpc/include/dpl/rpc/generic_socket_rpc_server.h b/modules/rpc/include/dpl/rpc/generic_socket_rpc_server.h new file mode 100644 index 0000000..87d6899 --- /dev/null +++ b/modules/rpc/include/dpl/rpc/generic_socket_rpc_server.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_socket_rpc_server.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file for generic socket RPC server + */ +#ifndef DPL_GENERIC_SOCKET_RPC_SERVER_H +#define DPL_GENERIC_SOCKET_RPC_SERVER_H + +#include +#include +#include + +namespace DPL +{ +namespace RPC +{ + +template +class GenericSocketRPCServer + : public AbstractRPCConnector, + private DPL::Event::EventListener +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, OpenFailed) + DECLARE_EXCEPTION_TYPE(Base, CloseFailed) + }; + +protected: + // Derived class implementations for connection managment + virtual AbstractRPCConnection *OpenSpecificConnection(SocketType *socket) = 0; + +private: + typedef std::set InternalInterfaceSet; + InternalInterfaceSet m_internalInterfacesSet; + + virtual void OnEventReceived(const DPL::Socket::AbstractSocketEvents::AcceptEvent &event) + { + // Retrieve socket sender + SocketType *server = static_cast(event.GetSender()); + + // Is this interface still tracked ? + // It might have disappeared on close + typename InternalInterfaceSet::iterator iterator = m_internalInterfacesSet.find(server); + + if (iterator == m_internalInterfacesSet.end()) + { + LogPedantic("RPC server interface socket disappeared"); + return; + } + + // Accept incoming client + SocketType *client = static_cast(server->Accept()); + if(client == NULL) + { + LogPedantic("Spontaneous accept on socket occurred"); + return; + } + + LogPedantic("Client connected to server: " << client->GetRemoteAddress().ToString()); + + // Open specific connection implementation + AbstractRPCConnection *connection = OpenSpecificConnection(client); + + // Retrieve ID once again + AbstractRPCConnectionID connectionID = static_cast(server); + + // Inform listeners + DPL::Event::EventSupport:: + EmitEvent(AbstractRPCConnectorEvents::ConnectionEstablishedEvent( + connectionID, connection, EventSender(this)), DPL::Event::EmitMode::Queued); + } + +public: + explicit GenericSocketRPCServer() + { + } + + virtual ~GenericSocketRPCServer() + { + // Always close connection + CloseAll(); + } + + AbstractRPCConnectionID Open(const Address &socketAddress) + { + LogPedantic("Starting server: " << socketAddress.ToString()); + + // Alloc new socket + SocketType *socket = new SocketType(); + + // Add socket listener + socket->EventSupport::AddListener(this); + + Try + { + // Open socket + socket->Open(); + + // Bind socket address + socket->Bind(socketAddress); + + // Start listening + socket->Listen(8); + } + Catch (DPL::Socket::AbstractSocket::Exception::Base) + { + // Remove back socket listener + socket->EventSupport::RemoveListener(this); + + // Log debug + LogPedantic("Cannot start server: " << socketAddress.ToString()); + + // Problem with server startup + ReThrowMsg(typename Exception::OpenFailed, socketAddress.ToString()); + } + + // Register new internal connection + m_internalInterfacesSet.insert(socket); + + // Debug info + LogPedantic("Server started on interface: " << socket->GetLocalAddress().ToString()); + + // Return unique identifier + return static_cast(socket); + } + + void Close(AbstractRPCConnectionID connectionID) + { + LogPedantic("Closing server interface..."); + + // Get socket from ID + SocketType *socket = static_cast(connectionID); + + // Find corresponding internal connection + typename InternalInterfaceSet::iterator iterator = m_internalInterfacesSet.find(socket); + + if (iterator == m_internalInterfacesSet.end()) + return; + + // Close socket + socket->Close(); + + // Remove socket listeners + socket->EventSupport::RemoveListener(this); + delete socket; + + m_internalInterfacesSet.erase(iterator); + + // Done + LogPedantic("Closed"); + } + + void CloseAll() + { + while (!m_internalInterfacesSet.empty()) + Close(static_cast(*m_internalInterfacesSet.begin())); + } +}; + +} +} // namespace DPL + +#endif // DPL_GENERIC_SOCKET_RPC_SERVER_H diff --git a/modules/rpc/include/dpl/rpc/rpc_function.h b/modules/rpc/include/dpl/rpc/rpc_function.h new file mode 100644 index 0000000..4a121ff --- /dev/null +++ b/modules/rpc/include/dpl/rpc/rpc_function.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file rpc_function.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file for RPC function + */ +#ifndef DPL_RPC_FUNCTION_H +#define DPL_RPC_FUNCTION_H + +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace RPC +{ + +class RPCFunction : public IStream +{ +protected: + BinaryQueue m_buffer; ///< Serialized RPC function call as a binary queue + +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, ParseFailed) + }; + + /** + * Constructor + */ + RPCFunction() + { + } + + /** + * Constructor + * + * @param buffer Binary queue to copy initialization data from + */ + RPCFunction(const BinaryQueue &buffer) + { + m_buffer.AppendCopyFrom(buffer); + } + + /** + * Destructor + */ + virtual ~RPCFunction() + { + } + + /** + * Append argument to call + * + * @param[in] arg Template based argument to append + * @return none + * @warning Carefully add any pointers to buffer because of template nature of this method + */ + template + void AppendArg(const Type &arg) + { + size_t argSize = sizeof(arg); + m_buffer.AppendCopy(&argSize, sizeof(argSize)); + m_buffer.AppendCopy(&arg, sizeof(arg)); + } + + /** + * Append @a std::string argument to call + * + * @param[in] arg String to append to function call + * @return none + */ + void AppendArg(const std::string &arg) + { + size_t argSize = arg.size(); + m_buffer.AppendCopy(&argSize, sizeof(argSize)); + m_buffer.AppendCopy(arg.c_str(), argSize); + } + + /** + * Append @a DPL::String argument to call + * + * @param[in] arg String to append to function call + * @return none + */ + void AppendArg(const String &arg) + { + std::string localStdString = ToUTF8String(arg); + AppendArg(localStdString); + } + + /** + * Consume argument from call. Arguments are retrieved in non-reversed order + * (same as they were pushed onto RPC function argument stack) + * + * @param[out] arg Reference to output template based argument + * @warning Carefully add any pointers to buffer because of template nature of this method + * @return none + */ + template + void ConsumeArg(Type &arg) + { + Try + { + size_t argSize = sizeof(arg); + m_buffer.FlattenConsume(&argSize, sizeof(argSize)); + + if (argSize != sizeof(arg)) + ThrowMsg(Exception::ParseFailed, "Stream parse CRC failed"); + + m_buffer.FlattenConsume(&arg, sizeof(arg)); + } + Catch (BinaryQueue::Exception::OutOfData) + { + ReThrowMsg(Exception::ParseFailed, "Unexpected end of stream"); + } + } + + /** + * Consume @a std::string argument from call. Arguments are retrieved in non-reversed order + * (same as they were pushed onto RPC function argument stack) + * + * @param[out] arg Reference to output @a std::string argument + * @return none + */ + void ConsumeArg(std::string &arg) + { + Try + { + std::string::size_type size; + m_buffer.FlattenConsume(&size, sizeof(size)); + ScopedArray str(new char[size]); + m_buffer.FlattenConsume(str.Get(), size); + arg = std::string(str.Get(), str.Get() + size); + } + Catch (BinaryQueue::Exception::OutOfData) + { + ReThrowMsg(Exception::ParseFailed, "Unexpected end of stream"); + } + } + + /** + * Consume @a DPL::String argument from call. Arguments are converted to UTF-8 string + * + * @param[out] arg Reference to output @a DPL::String argument + * @return none + */ + void ConsumeArg(String &arg) + { + std::string consumedStdString; + ConsumeArg(consumedStdString); + arg = FromUTF8String(consumedStdString); + } + + /** + * Serialize all function parameters into single binary queue + * + * @return Serialized binary queue representation of RPC function + */ + BinaryQueue Serialize() const + { + return m_buffer; + } + + /** + * Reads binary data from serialized stream + * + * @param num number of bytes to read + * @param bytes buffer for read data + */ + virtual void Read(size_t num, void * bytes) + { + m_buffer.FlattenConsume(bytes, num); + } + + /** + * Writes binary data to serialized stream + * + * @param num number of bytes to write to serialization buffer + * @param bytes buffer for data to write + */ + virtual void Write(size_t num, const void * bytes) + { + m_buffer.AppendCopy(bytes, num); + } +}; + +} +} // namespace DPL + +#endif // DPL_RPC_FUNCTION_H diff --git a/modules/rpc/include/dpl/rpc/unix_socket_rpc_client.h b/modules/rpc/include/dpl/rpc/unix_socket_rpc_client.h new file mode 100644 index 0000000..730a349 --- /dev/null +++ b/modules/rpc/include/dpl/rpc/unix_socket_rpc_client.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file unix_socket_rpc_client.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file for unix socket RPC client + */ +#ifndef DPL_UNIX_SOCKET_RPC_CLIENT_H +#define DPL_UNIX_SOCKET_RPC_CLIENT_H + +#include +#include +#include + +namespace DPL +{ +namespace RPC +{ + +class UnixSocketRPCClient + : public GenericSocketRPCClient +{ +protected: + virtual AbstractRPCConnection *OpenSpecificConnection(DPL::Socket::UnixSocket *socket); + +public: + AbstractRPCConnectionID Open(const std::string &fileName); +}; + +} +} // namespace DPL + +#endif // DPL_UNIX_SOCKET_RPC_CLIENT_H diff --git a/modules/rpc/include/dpl/rpc/unix_socket_rpc_connection.h b/modules/rpc/include/dpl/rpc/unix_socket_rpc_connection.h new file mode 100644 index 0000000..5981596 --- /dev/null +++ b/modules/rpc/include/dpl/rpc/unix_socket_rpc_connection.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file unix_socket_rpc_connection.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file for unix socket RPC connection + */ +#ifndef DPL_UNIX_SOCKET_RPC_CONNECTION_H +#define DPL_UNIX_SOCKET_RPC_CONNECTION_H + +#include +#include + +namespace DPL +{ +namespace RPC +{ + +class UnixSocketRPCConnection + : public GenericSocketRPCConnection +{ +public: + // Socket acquisition + UnixSocketRPCConnection(DPL::Socket::UnixSocket *socket); +}; + +} +} // namespace DPL + +#endif // DPL_UNIX_SOCKET_RPC_CONNECTION_H diff --git a/modules/rpc/include/dpl/rpc/unix_socket_rpc_server.h b/modules/rpc/include/dpl/rpc/unix_socket_rpc_server.h new file mode 100644 index 0000000..a6d7335 --- /dev/null +++ b/modules/rpc/include/dpl/rpc/unix_socket_rpc_server.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file unix_socket_rpc_server.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file for unix socket RPC server + */ +#ifndef DPL_UNIX_SOCKET_RPC_SERVER_H +#define DPL_UNIX_SOCKET_RPC_SERVER_H + +#include +#include +#include + +namespace DPL +{ +namespace RPC +{ + +class UnixSocketRPCServer + : public GenericSocketRPCServer +{ +protected: + virtual AbstractRPCConnection *OpenSpecificConnection(DPL::Socket::UnixSocket *socket); + +public: + AbstractRPCConnectionID Open(const std::string &fileName); +}; + +} +} // namespace DPL + +#endif // DPL_UNIX_SOCKET_RPC_SERVER_H diff --git a/modules/rpc/src/abstract_rpc_connection.cpp b/modules/rpc/src/abstract_rpc_connection.cpp new file mode 100644 index 0000000..b1b2140 --- /dev/null +++ b/modules/rpc/src/abstract_rpc_connection.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_rpc_connection.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of abstract RPC connection + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/rpc/src/abstract_rpc_connector.cpp b/modules/rpc/src/abstract_rpc_connector.cpp new file mode 100644 index 0000000..63527bc --- /dev/null +++ b/modules/rpc/src/abstract_rpc_connector.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_rpc_connector.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of abstract RPC connector + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/rpc/src/generic_rpc_connection.cpp b/modules/rpc/src/generic_rpc_connection.cpp new file mode 100644 index 0000000..f087494 --- /dev/null +++ b/modules/rpc/src/generic_rpc_connection.cpp @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_rpc_connection.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of generic RPC connection + */ +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace RPC +{ +namespace // anonymous +{ +namespace Protocol +{ +// Packet definitions +enum PacketType +{ + PacketType_AsyncCall, + PacketType_PingPong +}; + +struct Header +{ + unsigned short size; + unsigned short type; +} DPL_ALIGNED(1); + +struct AsyncCall + : public Header +{ + unsigned char data[1]; +} DPL_ALIGNED(1); + +} // namespace Protocol +} // namespace anonymous + +GenericRPCConnection::GenericRPCConnection(AbstractWaitableInputOutput *inputOutput) + : m_inputOutput(inputOutput) +{ + LogPedantic("Opening generic RPC..."); + WaitableInputOutputExecutionContextSupport::Open(inputOutput); + LogPedantic("Generic RPC opened"); +} + +GenericRPCConnection::~GenericRPCConnection() +{ + // Ensure RPC is closed + LogPedantic("Closing generic RPC..."); + WaitableInputOutputExecutionContextSupport::Close(); + LogPedantic("Generic RPC closed"); +} + +void GenericRPCConnection::AsyncCall(const RPCFunction &function) +{ + LogPedantic("Executing async call"); + + // Create binary call + BinaryQueue serializedCall = function.Serialize(); + + // Append buffers + Protocol::AsyncCall call; + call.size = static_cast(serializedCall.Size()); + call.type = Protocol::PacketType_AsyncCall; + + m_outputStream.AppendCopy(&call, sizeof(Protocol::Header)); + m_outputStream.AppendMoveFrom(serializedCall); + + // Try to feed output with data + Try + { + FeedOutput(); + } + Catch (WaitableInputOutputExecutionContextSupport::Exception::NotOpened) + { + // Error occurred while feeding + ReThrow(AbstractRPCConnection::Exception::AsyncCallFailed); + } +} + +void GenericRPCConnection::Ping() +{ + LogPedantic("Executing ping call"); + + // Append buffers + Protocol::AsyncCall call; + call.size = 0; + call.type = Protocol::PacketType_PingPong; + + m_outputStream.AppendCopy(&call, sizeof(Protocol::Header)); + + // Try to feed output with data + Try + { + FeedOutput(); + } + Catch (WaitableInputOutputExecutionContextSupport::Exception::NotOpened) + { + // Error occurred while feeding + ReThrow(AbstractRPCConnection::Exception::PingFailed); + } +} + +void GenericRPCConnection::OnInputStreamRead() +{ + LogPedantic("Interpreting " << m_inputStream.Size() << " bytes buffer"); + + // Enough bytes to read at least one header ? + if (m_inputStream.Size() >= sizeof(Protocol::Header)) + { + // Begin consuming as much packets as it is possible + while (m_inputStream.Size() >= sizeof(Protocol::Header)) + { + Protocol::Header header; + m_inputStream.Flatten(&header, sizeof(header)); + + if (m_inputStream.Size() >= sizeof(Protocol::Header) + header.size) + { + LogPedantic("Will parse packet of type: " << header.type); + + // Allocate new packet (header + real packet data) + void *binaryPacket = malloc(sizeof(Protocol::Header) + header.size); + + if (binaryPacket == NULL) + throw std::bad_alloc(); + + // Get it from stream + m_inputStream.FlattenConsume(binaryPacket, sizeof(Protocol::Header) + header.size); + + // Parse specific packet + switch (header.type) + { + case Protocol::PacketType_AsyncCall: + { + BinaryQueue call; + + // No need to delete packet data, we can use it + call.AppendUnmanaged(binaryPacket, sizeof(Protocol::Header) + header.size, &BinaryQueue::BufferDeleterFree, NULL); + + // ...but just remove protocol header + call.Consume(sizeof(Protocol::Header)); + + LogPedantic("Async call of size: " << header.size << " parsed"); + + // Call async call event listeners + DPL::Event::EventSupport:: + EmitEvent(AbstractRPCConnectionEvents::AsyncCallEvent( + RPCFunction(call), EventSender(this)), DPL::Event::EmitMode::Queued); + } + break; + + case Protocol::PacketType_PingPong: + { + // Reply with ping/pong + Ping(); + + // Do not need packet data + free(binaryPacket); + + LogPedantic("Ping pong replied"); + } + break; + + default: + LogPedantic("Warning: Unknown packet type"); + free(binaryPacket); + break; + } + } + else + { + LogPedantic("Too few bytes to read packet"); + break; + } + } + } + else + { + LogPedantic("Too few bytes to read header"); + } +} + +void GenericRPCConnection::OnInputStreamClosed() +{ + // Emit closed event + DPL::Event::EventSupport:: + EmitEvent(AbstractRPCConnectionEvents::ConnectionClosedEvent( + EventSender(this)), DPL::Event::EmitMode::Queued); +} + +void GenericRPCConnection::OnInputStreamBroken() +{ + // Emit broken event + DPL::Event::EventSupport:: + EmitEvent(AbstractRPCConnectionEvents::ConnectionBrokenEvent( + EventSender(this)), DPL::Event::EmitMode::Queued); +} + +} +} // namespace DPL diff --git a/modules/rpc/src/generic_socket_rpc_client.cpp b/modules/rpc/src/generic_socket_rpc_client.cpp new file mode 100644 index 0000000..3c09aac --- /dev/null +++ b/modules/rpc/src/generic_socket_rpc_client.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_socket_rpc_client.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of generic socket RPC client + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/rpc/src/generic_socket_rpc_connection.cpp b/modules/rpc/src/generic_socket_rpc_connection.cpp new file mode 100644 index 0000000..0786c62 --- /dev/null +++ b/modules/rpc/src/generic_socket_rpc_connection.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_socket_rpc_connection.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of generic socket RPC connection + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/rpc/src/generic_socket_rpc_server.cpp b/modules/rpc/src/generic_socket_rpc_server.cpp new file mode 100644 index 0000000..2472c66 --- /dev/null +++ b/modules/rpc/src/generic_socket_rpc_server.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_socket_rpc.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of generic socket RPC + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/rpc/src/unix_socket_rpc_client.cpp b/modules/rpc/src/unix_socket_rpc_client.cpp new file mode 100644 index 0000000..fb24741 --- /dev/null +++ b/modules/rpc/src/unix_socket_rpc_client.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file unix_socket_rpc_client.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of unix socket RPC client + */ +#include +#include + +namespace DPL +{ +namespace RPC +{ +AbstractRPCConnection *UnixSocketRPCClient::OpenSpecificConnection(DPL::Socket::UnixSocket *socket) +{ + // Allocate new UNIX/RPC connection object + UnixSocketRPCConnection *connection = new UnixSocketRPCConnection(socket); + + // Return new connection + return connection; +} + +AbstractRPCConnectionID UnixSocketRPCClient::Open(const std::string &fileName) +{ + return GenericSocketRPCClient::Open(Address(fileName)); +} +} +} // namespace DPL diff --git a/modules/rpc/src/unix_socket_rpc_connection.cpp b/modules/rpc/src/unix_socket_rpc_connection.cpp new file mode 100644 index 0000000..28fea84 --- /dev/null +++ b/modules/rpc/src/unix_socket_rpc_connection.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file unix_socket_rpc_connection.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of unix socket RPC connection + */ +#include + +namespace DPL +{ +namespace RPC +{ +UnixSocketRPCConnection::UnixSocketRPCConnection(DPL::Socket::UnixSocket *socket) + : GenericSocketRPCConnection(socket) +{ +} +} +} // namespace DPL diff --git a/modules/rpc/src/unix_socket_rpc_server.cpp b/modules/rpc/src/unix_socket_rpc_server.cpp new file mode 100644 index 0000000..ea71eb7 --- /dev/null +++ b/modules/rpc/src/unix_socket_rpc_server.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file unix_socket_rpc_server.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of unix socket RPC server + */ +#include +#include + +namespace DPL +{ +namespace RPC +{ +AbstractRPCConnection *UnixSocketRPCServer::OpenSpecificConnection(DPL::Socket::UnixSocket *socket) +{ + // Allocate new UNIX/RPC connection object + UnixSocketRPCConnection *connection = new UnixSocketRPCConnection(socket); + + // Return new connection + return connection; +} + +AbstractRPCConnectionID UnixSocketRPCServer::Open(const std::string &fileName) +{ + return GenericSocketRPCServer::Open(Address(fileName)); +} + +} +} // namespace DPL diff --git a/modules/socket/config.cmake b/modules/socket/config.cmake new file mode 100644 index 0000000..6e097a4 --- /dev/null +++ b/modules/socket/config.cmake @@ -0,0 +1,40 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file config.cmake +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +SET(DPL_SOCKET_SOURCES + ${PROJECT_SOURCE_DIR}/modules/socket/src/generic_socket.cpp + ${PROJECT_SOURCE_DIR}/modules/socket/src/unix_socket.cpp + ${PROJECT_SOURCE_DIR}/modules/socket/src/waitable_input_output_execution_context_support.cpp + PARENT_SCOPE +) + +SET(DPL_SOCKET_HEADERS + ${PROJECT_SOURCE_DIR}/modules/socket/include/dpl/socket/abstract_socket.h + ${PROJECT_SOURCE_DIR}/modules/socket/include/dpl/socket/generic_socket.h + ${PROJECT_SOURCE_DIR}/modules/socket/include/dpl/socket/unix_socket.h + ${PROJECT_SOURCE_DIR}/modules/socket/include/dpl/socket/waitable_input_output_execution_context_support.h + PARENT_SCOPE +) + +SET(DPL_SOCKET_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/modules/socket/include/ + PARENT_SCOPE +) diff --git a/modules/socket/include/dpl/socket/abstract_socket.h b/modules/socket/include/dpl/socket/abstract_socket.h new file mode 100644 index 0000000..90a8fa6 --- /dev/null +++ b/modules/socket/include/dpl/socket/abstract_socket.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file abstract_socket.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of abstract socket + */ +#ifndef DPL_ABSTRACT_SOCKET_H +#define DPL_ABSTRACT_SOCKET_H + +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Socket +{ +namespace AbstractSocketEvents +{ +// Successfuly connected to server socket +DECLARE_GENERIC_EVENT_0(ConnectedEvent) + +// New connection occurred and need to be accepted +DECLARE_GENERIC_EVENT_0(AcceptEvent) + +// Connection has read data waiting +DECLARE_GENERIC_EVENT_0(ReadEvent) + +// Connection write buffer is now empty again and ready to write more +DECLARE_GENERIC_EVENT_0(WriteEvent) +} // namespace AbstractSocketEvents + +class AbstractSocket + : public AbstractWaitableInputOutput, + public DPL::Event::EventSupport, + public DPL::Event::EventSupport, + public DPL::Event::EventSupport, + public DPL::Event::EventSupport +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) ///< Base abstract socket exception + + DECLARE_EXCEPTION_TYPE(Base, OpenFailed) ///< Fatal error occurred during open socket descriptor. Socket state is inconsistent and it should be not used anymore. + + DECLARE_EXCEPTION_TYPE(Base, ConnectFailed) ///< Fatal error occurred during connect. Socket state is inconsistent and it should be not used anymore. + ///< Warning: This exception does not mean that socket did not succeed to connect, see CannotConnect + ///< for this specific scenario + + DECLARE_EXCEPTION_TYPE(Base, SetNonBlockingFailed) ///< Fatal error occurred during setting socket to non-blocking mode. Socket state is inconsistent and it should be not used anymore. + + DECLARE_EXCEPTION_TYPE(Base, BindFailed) ///< Fatal error occurred during bind. Socket state is inconsistent and it should be not used anymore. + + DECLARE_EXCEPTION_TYPE(Base, AcceptFailed) ///< Fatal error occurred during accept. Socket state is inconsistent and it should be not used anymore. + + DECLARE_EXCEPTION_TYPE(Base, ListenFailed) ///< Fatal error occurred during listen. Socket state is inconsistent and it should be not used anymore. + + DECLARE_EXCEPTION_TYPE(Base, CloseFailed) ///< Fatal error occurred during close. Socket state is inconsistent and it should be not used anymore. + + DECLARE_EXCEPTION_TYPE(Base, ReadFailed) ///< Fatal error occurred during read. Socket state is inconsistent and it should be not used anymore. + ///< Warning: This exception does not mean that connection was broken, see ConnectionBroken + ///< for this specific scenario + + DECLARE_EXCEPTION_TYPE(Base, WriteFailed) ///< Fatal error occurred during write. Socket state is inconsistent and it should be not used anymore. + ///< Warning: This exception does not mean that connection was broken, see ConnectionBroken + ///< for this specific scenario + + DECLARE_EXCEPTION_TYPE(Base, GetPeerNameFailed) ///< Fatal error occurred during getpeername or getsockname. Socket state is inconsistent and it should be not used anymore. + + DECLARE_EXCEPTION_TYPE(Base, CannotConnect) ///< Cannot connect to remote socket. This is not fatal error, socket state is still consistent and it can be reconnected. + + DECLARE_EXCEPTION_TYPE(Base, ConnectionBroken) ///< Connection was broken. This is not fatal error, socket state is still consistent and it can be reconnected. + }; + +public: + virtual ~AbstractSocket() {} + + // Connect to remote host + virtual void Connect(const Address &address) = 0; + + // Open empty, unconnected socket + virtual void Open() = 0; + + // Close connection + virtual void Close() = 0; + + // Bind server socket address + virtual void Bind(const Address &address) = 0; + + // Begin listening for incoming connections + virtual void Listen(int backlog) = 0; + + // Accept waiting connection and create derived class connection + // One should cast resulting pointer to derived class + virtual AbstractSocket *Accept() = 0; + + // Local socket address + virtual Address GetLocalAddress() const = 0; + + // Remote socket address + virtual Address GetRemoteAddress() const = 0; +}; + +} +} // namespace DPL + +#endif // DPL_ABSTRACT_SOCKET_H diff --git a/modules/socket/include/dpl/socket/generic_socket.h b/modules/socket/include/dpl/socket/generic_socket.h new file mode 100644 index 0000000..f54d752 --- /dev/null +++ b/modules/socket/include/dpl/socket/generic_socket.h @@ -0,0 +1,835 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_socket.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of generic socket + */ +#ifndef DPL_GENERIC_SOCKET_H +#define DPL_GENERIC_SOCKET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Socket +{ +// +// Generic abstract socket implementation +// Execution context is inherited +// +template +class GenericSocket + : public AbstractSocket, + private WaitableHandleWatchSupport::WaitableHandleListener +{ +protected: + /** + * Translate generic Address to specific socket kernel structure + * + * @warning Must be implemented in derived class + */ + virtual std::pair TranslateAddressGenericToSpecific(const Address &address) const = 0; + + /** + * Translate specific socket kernel structure to generic Address + * + * @warning Must be implemented in derived class + */ + virtual Address TranslateAddressSpecificToGeneric(sockaddr *, socklen_t) const = 0; + + /** + * Get specific socket kernel structure size + * + * @warning Must be implemented in derived class + */ + virtual socklen_t GetSpecificAddressSize() const = 0; + + /** + * Alloc specific implementation of socket from descriptor + * + * @warning Must be implemented in derived class + */ + virtual SocketType *AllocAcceptedSpecificSocket() const = 0; + + /** + * Alloc specific implementation of socket descriptor + * + * @warning Must be implemented in derived class + */ + virtual int AllocSpecificDescriptor() const = 0; + +private: + // Constants + static const size_t DEFAULT_READ_BUFFER_SIZE = 4096; + + // Socket handle + int m_socket; // FIXME: Consider generalization to WaitableHandle upon leaving nix platform + + // Internal state + enum InternalState + { + InternalState_None, ///< Not connected and not listening state + InternalState_Prepare, ///< Descriptor allocated, but not connected + InternalState_Listening, ///< Listening state + InternalState_Connecting, ///< Connecting state + InternalState_Connected ///< Connected state + }; + + InternalState m_internalState; + + void SetNonBlocking() + { + // Set non-blocking mode + if (fcntl(m_socket, F_SETFL, O_NONBLOCK | fcntl(m_socket, F_GETFL)) == -1) + Throw(AbstractSocket::Exception::SetNonBlockingFailed); + } + + // WaitableHandleWatchSupport::WaitableHandleListener + virtual void OnWaitableHandleEvent(WaitableHandle waitableHandle, WaitMode::Type mode) + { + (void)waitableHandle; + Assert(waitableHandle == m_socket); + + switch (m_internalState) + { + case InternalState_None: + break; + + case InternalState_Prepare: + Assert(0 && "Invalid internal generic socket state!"); + break; + + case InternalState_Listening: + Assert(mode == WaitMode::Read); + + // New client waiting for accept + DPL::Event::EventSupport:: + EmitEvent(AbstractSocketEvents::AcceptEvent( + EventSender(this)), DPL::Event::EmitMode::Queued); + + // Done + break; + + case InternalState_Connecting: + Assert(mode == WaitMode::Write); + + // Connected to server + RemoveConnectWatch(); + m_internalState = InternalState_Connected; + + // Add read watch + AddReadWatch(); + + // Emit event + DPL::Event::EventSupport:: + EmitEvent(AbstractSocketEvents::ConnectedEvent( + EventSender(this)), DPL::Event::EmitMode::Queued); + + // Done + break; + + case InternalState_Connected: + if (mode == WaitMode::Read) + { + // Emit ReadEvent + DPL::Event::EventSupport:: + EmitEvent(AbstractSocketEvents::ReadEvent( + EventSender(this)), DPL::Event::EmitMode::Queued); + } + else if (mode == WaitMode::Write) + { + // Emit WriteEvent + DPL::Event::EventSupport:: + EmitEvent(AbstractSocketEvents::WriteEvent( + EventSender(this)), DPL::Event::EmitMode::Queued); + } + else + { + Assert(0); + } + + break; + + default: + Assert(0); + break; + } + } + + void AddAcceptWatch() + { + WaitableHandleWatchSupport::InheritedContext()->AddWaitableHandleWatch(this, m_socket, WaitMode::Read); + } + + void RemoveAcceptWatch() + { + WaitableHandleWatchSupport::InheritedContext()->RemoveWaitableHandleWatch(this, m_socket, WaitMode::Read); + } + + void AddConnectWatch() + { + WaitableHandleWatchSupport::InheritedContext()->AddWaitableHandleWatch(this, m_socket, WaitMode::Write); + } + + void RemoveConnectWatch() + { + WaitableHandleWatchSupport::InheritedContext()->RemoveWaitableHandleWatch(this, m_socket, WaitMode::Write); + } + + void AddReadWatch() + { + WaitableHandleWatchSupport::InheritedContext()->AddWaitableHandleWatch(this, m_socket, WaitMode::Read); + } + + void RemoveReadWatch() + { + WaitableHandleWatchSupport::InheritedContext()->RemoveWaitableHandleWatch(this, m_socket, WaitMode::Read); + } + + void AddWriteWatch() + { + WaitableHandleWatchSupport::InheritedContext()->AddWaitableHandleWatch(this, m_socket, WaitMode::Write); + } + + void RemoveWriteWatch() + { + WaitableHandleWatchSupport::InheritedContext()->RemoveWaitableHandleWatch(this, m_socket, WaitMode::Write); + } + +public: + GenericSocket() + : m_socket(-1), + m_internalState(InternalState_None) + { + } + + virtual ~GenericSocket() + { + // Always ensure to close socket + Try + { + Close(); + } + Catch(Exception::CloseFailed) + { + LogPedantic("Close failed and ignored"); + } + + // Check consistency + Assert(m_socket == -1); + } + + virtual void Open() + { + if (m_internalState != InternalState_None) + ThrowMsg(AbstractSocket::Exception::OpenFailed, "Invalid socket state, should be 'None'"); + + LogPedantic("Opening socket..."); + + // Check consistency + Assert(m_socket == -1); + + // Allocate specific socket descriptor + m_socket = AllocSpecificDescriptor(); + + // Set socket non-blocking + SetNonBlocking(); + + // State is prepared + m_internalState = InternalState_Prepare; + + LogPedantic("Socket opened"); + } + + virtual void Connect(const Address &address) + { + if (m_internalState != InternalState_Prepare) + ThrowMsg(AbstractSocket::Exception::ConnectFailed, "Invalid socket state, should be 'Prepare'"); + + LogPedantic("Connecting to: " << address.ToString()); + + // Try to convert address + std::pair socketAddress; + + Try + { + // Translate address to specific socket address struct + socketAddress = TranslateAddressGenericToSpecific(address); + } + Catch (Address::Exception::InvalidAddress) + { + // This address is invalid. Cannot connect. + ReThrowMsg(AbstractSocket::Exception::ConnectFailed, address.ToString()); + } + + // Do connect + int result = TEMP_FAILURE_RETRY(connect(m_socket, socketAddress.first, socketAddress.second)); + + if (result == 0) + { + // Immediate connect + LogPedantic("Immediate connected to: " << address.ToString()); + + // Add read watch + AddReadWatch(); + m_internalState = InternalState_Connected; + + // Emit connected event + DPL::Event::EventSupport:: + EmitEvent(AbstractSocketEvents::ConnectedEvent( + EventSender(this)), DPL::Event::EmitMode::Queued); + } + else + { + if (errno == EINTR || errno == EINPROGRESS) + { + LogPedantic("Asynchronous connect in progress: " << address.ToString()); + + // Connecting in progress + AddConnectWatch(); + m_internalState = InternalState_Connecting; + } + else + { + // Free translation structure + free(socketAddress.first); + + // Error occurred + ThrowMsg(AbstractSocket::Exception::ConnectFailed, address.ToString()); + } + } + + // Free translation structure + free(socketAddress.first); + } + + virtual void Close() + { + if (m_internalState == InternalState_None) + return; + + Assert(m_socket != -1); + + if (m_internalState == InternalState_Listening) + { + // Remove watch in listening state + LogPedantic("Removing accept watch"); + RemoveAcceptWatch(); + m_internalState = InternalState_None; + } + else if (m_internalState == InternalState_Connecting) + { + // Remove watch in connecting state + LogPedantic("Removing connect watch"); + RemoveConnectWatch(); + m_internalState = InternalState_None; + } + else if (m_internalState == InternalState_Connected) + { + // Remove watch in connected state + LogPedantic("Removing read watch"); + RemoveReadWatch(); + m_internalState = InternalState_None; + } + else + { + // State must be just prepared only + Assert(m_internalState == InternalState_Prepare); + } + + if (TEMP_FAILURE_RETRY(close(m_socket)) == -1) + Throw(Exception::CloseFailed); + + // Reset socket + m_socket = -1; + + // Reset socket state + m_internalState = InternalState_None; + + LogPedantic("Socket closed"); + } + + virtual void Bind(const Address &address) + { + if (m_internalState != InternalState_Prepare) + ThrowMsg(AbstractSocket::Exception::BindFailed, "Invalid socket state, should be 'Prepare'"); + + LogPedantic("Binding to: " << address.GetAddress() << ":" << address.GetPort()); + + // Translate address to specific socket address struct + std::pair socketAddress = TranslateAddressGenericToSpecific(address); + + // Do bind + if (::bind(m_socket, socketAddress.first, socketAddress.second) == -1) + ThrowMsg(AbstractSocket::Exception::BindFailed, address.ToString()); + + // Free translation structure + free(socketAddress.first); + + // Show info + LogPedantic("Bound to address: " << address.GetAddress() << ":" << address.GetPort()); + } + + virtual void Listen(int backlog) + { + if (m_internalState != InternalState_Prepare) + ThrowMsg(AbstractSocket::Exception::ListenFailed, "Invalid socket state, should be 'None'"); + + LogPedantic("Starting to listen..."); + + // Do listen + if (listen(m_socket, backlog) != 0) + Throw(AbstractSocket::Exception::ListenFailed); + + // Begin read watch + AddAcceptWatch(); + m_internalState = InternalState_Listening; + + LogPedantic("Listen started"); + } + + virtual AbstractSocket *Accept() + { + if (m_internalState != InternalState_Listening) + ThrowMsg(AbstractSocket::Exception::AcceptFailed, "Invalid socket state, should be 'Listening'"); + + LogPedantic("Accepting..."); + + // Do listen + socklen_t length = 0; + int client = TEMP_FAILURE_RETRY(accept(m_socket, NULL, &length)); + + LogPedantic("Socket accept returned " << client); + if (client == -1) + { + // Check if there is any client waiting + if (errno == EWOULDBLOCK || errno == EAGAIN) + return NULL; + int err = errno; + if (errno == ENOENT) + return NULL; + LogPedantic("throwing error. errrno " << err); + // Error occurred + Throw(AbstractSocket::Exception::AcceptFailed); + } + + LogPedantic("Accepted client. Seting up..."); + + // Create derived class type + GenericSocket *acceptedSocket = AllocAcceptedSpecificSocket(); + + // Save client socket specific descriptor + acceptedSocket->m_socket = client; + + // Enter proper states and add read watch + acceptedSocket->AddReadWatch(); + acceptedSocket->m_internalState = InternalState_Connected; + + // Set non-blocking mode for new socket + acceptedSocket->SetNonBlocking(); + + // Show info + LogPedantic("Accepted client set up"); + + // return new conneced client socket + return acceptedSocket; + } + + virtual Address GetLocalAddress() const + { + // FIXME: Additional internal state check + + socklen_t length = GetSpecificAddressSize(); + ScopedFree address(static_cast(calloc(static_cast(length), 1))); + + if (getsockname(m_socket, address.Get(), &length) == -1) + ThrowMsg(AbstractSocket::Exception::GetPeerNameFailed, "Failed to get local address"); + + return TranslateAddressSpecificToGeneric(address.Get(), length); + } + + virtual Address GetRemoteAddress() const + { + // FIXME: Additional internal state check + + socklen_t length = GetSpecificAddressSize(); + ScopedFree address(static_cast(calloc(static_cast(length), 1))); + + if (getpeername(m_socket, address.Get(), &length) == -1) + ThrowMsg(AbstractSocket::Exception::GetPeerNameFailed, "Failed to get remote address"); + + return TranslateAddressSpecificToGeneric(address.Get(), length); + } + + virtual BinaryQueueAutoPtr Read(size_t size) + { + if (m_internalState != InternalState_Connected) + ThrowMsg(AbstractSocket::Exception::AcceptFailed, "Invalid socket state, should be 'Connected'"); + + Try + { + // Adjust bytes to be read + size_t bytesToRead = size > DEFAULT_READ_BUFFER_SIZE ? DEFAULT_READ_BUFFER_SIZE : size; + + // Malloc default read buffer size + // It is unmanaged, so it can be then attached directly to binary queue + void *buffer = malloc(bytesToRead); + + if (buffer == NULL) + throw std::bad_alloc(); + + // Receive bytes from socket + ssize_t result = TEMP_FAILURE_RETRY(recv(m_socket, buffer, bytesToRead, 0)); + + if (result > 0) + { + // Succedded to read socket data + BinaryQueueAutoPtr binaryQueue(new BinaryQueue()); + + // Append unmanaged memory + binaryQueue->AppendUnmanaged(buffer, result, &BinaryQueue::BufferDeleterFree, NULL); + + // Return buffer + return binaryQueue; + } + else if (result == 0) + { + // Socket was gracefuly closed + free(buffer); + + // Return empty buffer + return BinaryQueueAutoPtr(new BinaryQueue()); + } + else + { + // Must first save errno value, because it may be altered + int lastErrno = errno; + + // Free buffer + free(buffer); + + // Interpret error result + switch (lastErrno) + { + case EAGAIN: // = EWOULDBLOCK + // + // * The socket's file descriptor is marked O_NONBLOCK and no data is waiting + // to be received; or MSG_OOB is set and no out-of-band data is available + // and either the socket's file descriptor is marked O_NONBLOCK or the socket + // does not support blocking to await out-of-band data. + // + // return null data buffer to indicate no data waiting + // + return BinaryQueueAutoPtr(); + + case EBADF: + // + // * The socket argument is not a valid file descriptor. + // + // This is internal error + // + ThrowMsg(CommonException::InternalError, "Invalid socket descriptor"); + + case ECONNRESET: + // + // * A connection was forcibly closed by a peer. + // + // In result, we can interpret this error as a broken connection + // + Throw(AbstractSocket::Exception::ConnectionBroken); + + case EINTR: + // + // * The recv() function was interrupted by a signal that was caught, before any + // data was available. + // + // No interrupt here is expected, due to fact that we used TEMP_FAILURE_RETRY + // + ThrowMsg(CommonException::InternalError, "Unexpected interrupt occurred"); + + case EINVAL: + // + // * The MSG_OOB flag is set and no out-of-band data is available. + // + // We did not specified OOB data. This is an error. + // + ThrowMsg(CommonException::InternalError, "Unexpected OOB error occurred"); + + case ENOTCONN: + // + // * A receive is attempted on a connection-mode socket that is not connected. + // + // FIXME: Is this proper exception here ? + // + ThrowMsg(CommonException::InternalError, "Socket is not connected"); + + case ENOTSOCK: + // + // * The socket argument does not refer to a socket. + // + ThrowMsg(CommonException::InternalError, "Handle is not a socket"); + + case EOPNOTSUPP: + // + // * The specified flags are not supported for this socket type or protocol. + // + ThrowMsg(CommonException::InternalError, "Socket flags not supported"); + + case ETIMEDOUT: + // + // * The connection timed out during connection establishment, or due to a transmission timeout on active connection. + // + // In result, we can interpret this error as a broken connection + // + Throw(AbstractSocket::Exception::ConnectionBroken); + + case EIO: + // + // * An I/O error occurred while reading from or writing to the file system. + // + // In result, we can interpret this error as a broken connection + // + Throw(AbstractSocket::Exception::ConnectionBroken); + + case ENOBUFS: + // + // * Insufficient resources were available in the system to perform the operation. + // + ThrowMsg(CommonException::InternalError, "Insufficient system resources"); + + case ENOMEM: + // + // * Insufficient memory was available to fulfill the request. + // + ThrowMsg(CommonException::InternalError, "Insufficient system memory"); + + default: + // Some kernel error occurred, should never happen + ThrowMsg(CommonException::InternalError, "Unknown kernel read error returned"); + break; + } + } + } + Catch (CommonException::InternalError) + { + // If any internal error occurred, this is fatal for Write method + // interpret this as WriteError exception and rethrow + ReThrow(AbstractSocket::Exception::ReadFailed); + } + } + + virtual size_t Write(const BinaryQueue &buffer, size_t bufferSize) + { + if (m_internalState != InternalState_Connected) + ThrowMsg(AbstractSocket::Exception::AcceptFailed, "Invalid socket state, should be 'Connected'"); + + Try + { + // Adjust write size + if (bufferSize > buffer.Size()) + bufferSize = buffer.Size(); + + // FIXME: User write visitor to write ! + // WriteVisitor visitor + + ScopedFree flattened(malloc(bufferSize)); + buffer.Flatten(flattened.Get(), bufferSize); + + // Linux: MSG_NOSIGNAL is supported, but it is not an ideal solution + // FIXME: Should we setup signal PIPE ignoring for whole process ? + // In BSD, there is: setsockopt(c, SOL_SOCKET, SO_NOSIGPIPE, (void *)&on, sizeof(on)) + ssize_t result = TEMP_FAILURE_RETRY(send(m_socket, flattened.Get(), bufferSize, MSG_NOSIGNAL)); + + if (result > 0) + { + // Successfuly written some bytes + return static_cast(result); + } + else if (result == 0) + { + // This is abnormal result + ThrowMsg(CommonException::InternalError, "Invalid socket write result, 0 bytes written"); + } + else if (result == -1) + { + // Interpret error result + switch (errno) + { + case EAGAIN: // = EWOULDBLOCK + // + // * The socket's file descriptor is marked O_NONBLOCK and the requested operation would block. + // + // We should wait for writability + // + return 0; + + case EBADF: + // + // * The socket argument is not a valid file descriptor. + // + // This is internal error + // + ThrowMsg(CommonException::InternalError, "Invalid socket descriptor"); + + case ECONNRESET: + // + // * A connection was forcibly closed by a peer. + // + // In result, we can interpret this error as a broken connection + // + Throw(AbstractSocket::Exception::ConnectionBroken); + + case EDESTADDRREQ: + // + // * The socket is not connection-mode and no peer address is set. + // + // FIXME: Is this proper exception here ? + // + ThrowMsg(CommonException::InternalError, "Socket is not connected"); + + case EINTR: + // + // * A signal interrupted send() before any data was transmitted. + // + // No interrupt here is expected, due to fact that we used TEMP_FAILURE_RETRY + // + ThrowMsg(CommonException::InternalError, "Unexpected interrupt occurred"); + + case EMSGSIZE: + // + // * The message is too large to be sent all at once, as the socket requires. + // + // FIXME: Is this proper exception here ? + // + ThrowMsg(CommonException::InternalError, "Socket message is too big"); + + case ENOTCONN: + // + // * The socket is not connected or otherwise has not had the peer pre-specified. + // + // FIXME: Is this proper exception here ? + // + ThrowMsg(CommonException::InternalError, "Socket is not connected"); + + case ENOTSOCK: + // + // * The socket argument does not refer to a socket. + // + ThrowMsg(CommonException::InternalError, "Handle is not a socket"); + + case EOPNOTSUPP: + // + // * The socket argument is associated with a socket that does not support one or more of the values set in flags. + // + ThrowMsg(CommonException::InternalError, "Socket flags not supported"); + + case EPIPE: + // + // * The socket is shut down for writing, or the socket is connection-mode and + // is no longer connected. In the latter case, and if the socket is of type + // SOCK_STREAM, the SIGPIPE signal is generated to the calling thread. + // + // In result, we can interpret this error as a broken connection + // + Throw(AbstractSocket::Exception::ConnectionBroken); + + case EACCES: + // + // * The calling process does not have the appropriate privileges. + // + // Priviledges might have changed. + // In result, we can interpret this error as a broken connection + // + Throw(AbstractSocket::Exception::ConnectionBroken); + + case EIO: + // + // * An I/O error occurred while reading from or writing to the file system. + // + // In result, we can interpret this error as a broken connection + // + Throw(AbstractSocket::Exception::ConnectionBroken); + + case ENETDOWN: + // + // * The local network interface used to reach the destination is down. + // + // In result, we can interpret this error as a broken connection + // + Throw(AbstractSocket::Exception::ConnectionBroken); + + case ENETUNREACH: + // + // * No route to the network is present. + // + // In result, we can interpret this error as a broken connection + // + Throw(AbstractSocket::Exception::ConnectionBroken); + + case ENOBUFS: + // + // * Insufficient resources were available in the system to perform the operation. + // + ThrowMsg(CommonException::InternalError, "Insufficient system resources"); + + default: + // Some kernel error occurred, should never happen + ThrowMsg(CommonException::InternalError, "Unknown kernel write error returned"); + break; + } + } + } + Catch (CommonException::InternalError) + { + // If any internal error occurred, this is fatal for Write method + // interpret this as WriteError exception and rethrow + ReThrow(AbstractSocket::Exception::WriteFailed); + } + + // Does not apply + return 0; + } + + // AbstractWaitableInput + virtual WaitableHandle WaitableReadHandle() const + { + return m_socket; + } + + // AbstractWaitableOutput + virtual WaitableHandle WaitableWriteHandle() const + { + return m_socket; + } +}; + +} +} // namespace DPL + +#endif // DPL_GENERIC_SOCKET_H diff --git a/modules/socket/include/dpl/socket/unix_socket.h b/modules/socket/include/dpl/socket/unix_socket.h new file mode 100644 index 0000000..3d7b9fe --- /dev/null +++ b/modules/socket/include/dpl/socket/unix_socket.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file unix_socket.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file of unix socket + */ +#ifndef DPL_UNIX_SOCKET_H +#define DPL_UNIX_SOCKET_H + +#include +#include + +namespace DPL +{ +namespace Socket +{ + +class UnixSocket + : public GenericSocket +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, CreateFailed) + }; + +protected: + /** + * Translate generic Address to specific socket kernel structure + */ + virtual std::pair TranslateAddressGenericToSpecific(const Address &address) const; + + /** + * Translate specific socket kernel structure to generic Address + */ + virtual Address TranslateAddressSpecificToGeneric(sockaddr *, socklen_t) const; + + /** + * Get specific socket kernel structure size + */ + virtual socklen_t GetSpecificAddressSize() const; + + /** + * Alloc specific implementation of socket from descriptor + */ + virtual UnixSocket *AllocAcceptedSpecificSocket() const; + + /** + * Alloc specific implementation of socket descriptor + */ + virtual int AllocSpecificDescriptor() const; + +public: + UnixSocket(); + + virtual void Bind(const Address &address); +}; + +} +} // namespace DPL + +#endif // DPL_GENERIC_SOCKET_H diff --git a/modules/socket/include/dpl/socket/waitable_input_output_execution_context_support.h b/modules/socket/include/dpl/socket/waitable_input_output_execution_context_support.h new file mode 100644 index 0000000..cfdbac2 --- /dev/null +++ b/modules/socket/include/dpl/socket/waitable_input_output_execution_context_support.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file waitable_input_output_execution_context_support.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the header file for waitable input-output execution context support + */ +#ifndef DPL_WAITABLE_INPUT_OUTPUT_EXECUTION_CONTEXT_SUPPORT_H +#define DPL_WAITABLE_INPUT_OUTPUT_EXECUTION_CONTEXT_SUPPORT_H + +#include +#include +#include + +namespace DPL +{ +namespace Socket +{ + +class WaitableInputOutputExecutionContextSupport + : private WaitableHandleWatchSupport::WaitableHandleListener +{ +public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, AlreadyOpened) + DECLARE_EXCEPTION_TYPE(Base, NotOpened) + DECLARE_EXCEPTION_TYPE(Base, OpenFailed) + DECLARE_EXCEPTION_TYPE(Base, CloseFailed) + }; + +private: + bool m_opened; + AbstractWaitableInputOutput *m_waitableInputOutput; + + // Watch state + bool m_hasReadWatch; + bool m_hasWriteWatch; + + void AddReadWatch(); + void RemoveReadWatch(); + void AddWriteWatch(); + void RemoveWriteWatch(); + + void ReadInput(); + + void CheckedRemoveReadWatch(); + void CheckedRemoveWriteWatch(); + + void CheckedRemoveReadWriteWatch(); + + virtual void OnWaitableHandleEvent(WaitableHandle waitableHandle, WaitMode::Type mode); + +protected: + // Incoming/Outgoing streams + BinaryQueue m_inputStream; + BinaryQueue m_outputStream; + + // Support calbback methods + virtual void OnInputStreamRead() = 0; + virtual void OnInputStreamClosed() = 0; + virtual void OnInputStreamBroken() = 0; + + // Trigger feeding output - after updating output stream + void FeedOutput(); + + // Open/Close destination waitable input-output + void Open(AbstractWaitableInputOutput *waitableInputOutput); + void Close(); + +public: + /** + * Constructor + */ + explicit WaitableInputOutputExecutionContextSupport(); + + /** + * Destructor + */ + virtual ~WaitableInputOutputExecutionContextSupport(); +}; + +} +} // namespace DPL + +#endif // DPL_WAITABLE_INPUT_OUTPUT_EXECUTION_CONTEXT_SUPPORT_H diff --git a/modules/socket/src/generic_socket.cpp b/modules/socket/src/generic_socket.cpp new file mode 100644 index 0000000..a8e2237 --- /dev/null +++ b/modules/socket/src/generic_socket.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file generic_socket.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of generic socket + */ +#include + +// +// Note: +// +// The file here is left blank to enable precompilation +// of templates in corresponding header file. +// Do not remove this file. +// diff --git a/modules/socket/src/unix_socket.cpp b/modules/socket/src/unix_socket.cpp new file mode 100644 index 0000000..f185da3 --- /dev/null +++ b/modules/socket/src/unix_socket.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file unix_socket.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of unix socket + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Socket +{ + +UnixSocket::UnixSocket() +{ +} + +int UnixSocket::AllocSpecificDescriptor() const +{ + LogPedantic("Creating UNIX socket..."); + + // Create new descriptor + int newSocket = socket(AF_UNIX, SOCK_STREAM, 0); + + if (newSocket == -1) + Throw(Exception::CreateFailed); + + LogPedantic("UNIX socket created"); + + // Return new descriptor + return newSocket; +} + +std::pair UnixSocket::TranslateAddressGenericToSpecific(const Address &address) const +{ + // Allocate new socket address structure + sockaddr_un *sockAddress = static_cast(malloc(sizeof(sockaddr_un))); + if (!sockAddress) throw std::bad_alloc(); + + memset(sockAddress, 0, sizeof(sockaddr_un)); + + // Copy address properties + sockAddress->sun_family = AF_UNIX; + strncpy(sockAddress->sun_path, address.GetAddress().c_str(), sizeof(sockAddress->sun_path) - 1); + sockAddress->sun_path[sizeof(sockAddress->sun_path) - 1] = '\0'; // Prevent buffer overflows + + // Set proper address length + socklen_t sockAddressLength = SUN_LEN(sockAddress); + + // Return new translated address + return std::make_pair(reinterpret_cast(sockAddress), sockAddressLength); +} + +Address UnixSocket::TranslateAddressSpecificToGeneric(sockaddr *address, socklen_t) const +{ + // FIXME: Constrain check ? + sockaddr_un *unixAddress = reinterpret_cast(address); + return Address(unixAddress->sun_path); +} + +socklen_t UnixSocket::GetSpecificAddressSize() const +{ + return static_cast(sizeof(sockaddr_un)); +} + +UnixSocket *UnixSocket::AllocAcceptedSpecificSocket() const +{ + return new UnixSocket(); +} + +void UnixSocket::Bind(const Address &address) +{ + // Always remove socket file if any + unlink(address.GetAddress().c_str()); + + // Call base implementation + GenericSocket::Bind(address); + + // Always set proper permissions to the socket file + chmod(address.GetAddress().c_str(), 0777); +} + +} +} // namespace DPL diff --git a/modules/socket/src/waitable_input_output_execution_context_support.cpp b/modules/socket/src/waitable_input_output_execution_context_support.cpp new file mode 100644 index 0000000..7411b49 --- /dev/null +++ b/modules/socket/src/waitable_input_output_execution_context_support.cpp @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file waitable_input_output_execution_context_support.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of waitable input-output execution context support + */ +#include +#include +#include // FIXME: Remove !!! +#include +#include + +namespace DPL +{ +namespace Socket +{ + +namespace // anonymous +{ +const size_t DEFAULT_READ_SIZE = 2048; +} // namespace anonymous + +WaitableInputOutputExecutionContextSupport::WaitableInputOutputExecutionContextSupport() + : m_opened(false), + m_waitableInputOutput(NULL), + m_hasReadWatch(false), + m_hasWriteWatch(false) +{ +} + +WaitableInputOutputExecutionContextSupport::~WaitableInputOutputExecutionContextSupport() +{ + // Ensure support is closed + Close(); +} + +void WaitableInputOutputExecutionContextSupport::Open(AbstractWaitableInputOutput *inputOutput) +{ + if (m_opened) + Throw(Exception::AlreadyOpened); + + LogPedantic("Opening waitable input-output execution context support..."); + + // Save IO handle + m_waitableInputOutput = inputOutput; + + // Register read watch + Assert(m_hasReadWatch == false); + + AddReadWatch(); + m_hasReadWatch = true; + + // Done + m_opened = true; + + LogPedantic("Waitable input-output execution context support opened"); +} + +void WaitableInputOutputExecutionContextSupport::Close() +{ + if (!m_opened) + return; + + LogPedantic("Closing waitable input-output execution context support..."); + + // Remove read and write watches + CheckedRemoveReadWriteWatch(); + + // Set proper state + m_opened = false; + + LogPedantic("Waitable input-output execution context support closed"); +} + +void WaitableInputOutputExecutionContextSupport::AddReadWatch() +{ + WaitableHandleWatchSupport::InheritedContext()->AddWaitableHandleWatch(this, m_waitableInputOutput->WaitableReadHandle(), WaitMode::Read); +} + +void WaitableInputOutputExecutionContextSupport::RemoveReadWatch() +{ + WaitableHandleWatchSupport::InheritedContext()->RemoveWaitableHandleWatch(this, m_waitableInputOutput->WaitableReadHandle(), WaitMode::Read); +} + +void WaitableInputOutputExecutionContextSupport::AddWriteWatch() +{ + WaitableHandleWatchSupport::InheritedContext()->AddWaitableHandleWatch(this, m_waitableInputOutput->WaitableWriteHandle(), WaitMode::Write); +} + +void WaitableInputOutputExecutionContextSupport::RemoveWriteWatch() +{ + WaitableHandleWatchSupport::InheritedContext()->RemoveWaitableHandleWatch(this, m_waitableInputOutput->WaitableWriteHandle(), WaitMode::Write); +} + +void WaitableInputOutputExecutionContextSupport::CheckedRemoveReadWatch() +{ + if (!m_hasReadWatch) + return; + + RemoveReadWatch(); + m_hasReadWatch = false; +} + +void WaitableInputOutputExecutionContextSupport::CheckedRemoveWriteWatch() +{ + if (!m_hasWriteWatch) + return; + + RemoveWriteWatch(); + m_hasWriteWatch = false; +} + +void WaitableInputOutputExecutionContextSupport::CheckedRemoveReadWriteWatch() +{ + // Remove read watch if any + CheckedRemoveReadWatch(); + + // Remove write watch if any + CheckedRemoveWriteWatch(); +} + +void WaitableInputOutputExecutionContextSupport::OnWaitableHandleEvent(WaitableHandle waitableHandle, WaitMode::Type mode) +{ + (void)waitableHandle; + + switch (mode) + { + case WaitMode::Read: + LogPedantic("Read event occurred"); + + // Read and parse bytes + ReadInput(); + + // Done + break; + + case WaitMode::Write: + LogPedantic("Write event occurred"); + + // Push bytes and unregister from write event + FeedOutput(); + + // Unregister write watch only if no more data is available + if (m_outputStream.Empty()) + { + Assert(m_hasWriteWatch == true); + CheckedRemoveWriteWatch(); + } + + // Done + break; + + default: + Assert(0); + break; + } +} + +void WaitableInputOutputExecutionContextSupport::ReadInput() +{ + LogPedantic("Reading input bytes"); + + Try + { + BinaryQueueAutoPtr inputBuffer = m_waitableInputOutput->Read(DEFAULT_READ_SIZE); + + if (inputBuffer.get() == NULL) + { + // No data, should not occur + LogPedantic("WARNING: Spontaneous ReadSocket occurred"); + return; + } + + if (inputBuffer->Empty()) + { + // Connection was closed + OnInputStreamClosed(); + + // Unregister from further event insisting + Assert(m_hasReadWatch == true); + CheckedRemoveReadWriteWatch(); + + // Set proper state + m_opened = false; + + // Done + return; + } + + LogPedantic("Read " << inputBuffer->Size() << " input bytes"); + + // Append all read data + m_inputStream.AppendMoveFrom(*inputBuffer); + } + Catch (AbstractSocket::Exception::ConnectionBroken) // FIXME: Inproper exception abstraction !!! + { + // Some errors occurred while feeding abstract IO + // Interpret connection broken errors, and pass futher other ones + LogPedantic("Abstract IO connection was broken during read"); + + // Signal broken connection + OnInputStreamBroken(); + + // Unregister from further event insisting + Assert(m_hasReadWatch == true); + CheckedRemoveReadWriteWatch(); + + // Set proper state + m_opened = false; + + // Do not continue + return; + } + + // Interpret data + OnInputStreamRead(); +} + +void WaitableInputOutputExecutionContextSupport::FeedOutput() +{ + if (!m_opened) + Throw(Exception::NotOpened); + + // Anything to feed ? + if (m_outputStream.Empty()) + return; + + // OK to feed output + LogPedantic("Feeding output"); + + Try + { + // Try to write some bytes + size_t bytes = m_waitableInputOutput->Write(m_outputStream, m_outputStream.Size()); + + if (bytes < m_outputStream.Size()) + { + // Start exhaustive output feeding if it is blocked and not already started + if (!m_hasWriteWatch) + { + AddWriteWatch(); + m_hasWriteWatch = true; + + LogPedantic("Started exhaustive output feeding"); + } + } + + // Some bytes were written, consume them + m_outputStream.Consume(bytes); + } + Catch (AbstractSocket::Exception::ConnectionBroken) // FIXME: Inproper exception abstraction !!! + { + // Some errors occurred while feeding abstract IO + // Interpret connection broken errors, and pass futher other ones + LogPedantic("Abstract IO connection was broken during write"); + + // Signal broken connection + OnInputStreamBroken(); + + // Unregister from further event insisting + Assert(m_hasReadWatch == true); + CheckedRemoveReadWriteWatch(); + + // Set proper state + m_opened = false; + + // Do not continue + return; + } +} + +} +} // namespace DPL diff --git a/modules/test/config.cmake b/modules/test/config.cmake new file mode 100644 index 0000000..69d5341 --- /dev/null +++ b/modules/test/config.cmake @@ -0,0 +1,38 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file config.cmake +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +SET(DPL_TEST_ENGINE_SOURCES + ${PROJECT_SOURCE_DIR}/modules/test/src/test_results_collector.cpp + ${PROJECT_SOURCE_DIR}/modules/test/src/test_runner.cpp + PARENT_SCOPE +) + + +SET(DPL_TEST_ENGINE_HEADERS + ${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/test_results_collector.h + ${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/test_runner.h + PARENT_SCOPE +) + +SET(DPL_TEST_ENGINE_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/modules/test/include + PARENT_SCOPE +) diff --git a/modules/test/include/dpl/test/test_results_collector.h b/modules/test/include/dpl/test/test_results_collector.h new file mode 100644 index 0000000..6c4e24b --- /dev/null +++ b/modules/test/include/dpl/test/test_results_collector.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_results_collector.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief Header file with declaration of TestResultsCollectorBase + */ + +#ifndef DPL_TEST_RESULTS_COLLECTOR_H +#define DPL_TEST_RESULTS_COLLECTOR_H + +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Test +{ + +class TestResultsCollectorBase; +typedef DPL::SharedPtr + TestResultsCollectorBasePtr; + +class TestResultsCollectorBase + : private DPL::Noncopyable +{ + public: + typedef TestResultsCollectorBase* (*CollectorConstructorFunc)(); + typedef std::list TestCaseIdList; + struct FailStatus + { + enum Type + { + NONE, + FAILED, + IGNORED, + TODO, + INTERNAL + }; + }; + + virtual ~TestResultsCollectorBase() {} + + virtual bool Configure() { return true; } + virtual void Start() { } + virtual void Finish() { } + virtual void CollectCurrentTestGroupName(const std::string& /*groupName*/) {} + + virtual void CollectTestsCasesList(const TestCaseIdList& /*list*/) {} + virtual void CollectResult(const std::string& id, + const std::string& description, + const FailStatus::Type status = FailStatus::NONE, + const std::string& reason = "") = 0; + virtual std::string CollectorSpecificHelp() const { return ""; } + virtual bool ParseCollectorSpecificArg (const std::string& /*arg*/) + { + return false; + } + + static TestResultsCollectorBase* Create(const std::string& name); + static void RegisterCollectorConstructor(const std::string& name, + CollectorConstructorFunc constructor); + static std::vector GetCollectorsNames(); + + private: + typedef std::map ConstructorsMap; + static ConstructorsMap m_constructorsMap; +}; + +} +} + +#endif /* DPL_TEST_RESULTS_COLLECTOR_H */ diff --git a/modules/test/include/dpl/test/test_runner.h b/modules/test/include/dpl/test/test_runner.h new file mode 100644 index 0000000..ab1a626 --- /dev/null +++ b/modules/test/include/dpl/test/test_runner.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_runner.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief This file is the header file of test runner + */ +#ifndef DPL_TEST_RUNNER_H +#define DPL_TEST_RUNNER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DPL +{ +namespace Test +{ +class TestRunner +{ + TestResultsCollectorBasePtr m_collector; + std::string m_collectorName; + std::string m_startTestId; + +public: + typedef void (*TestCase)(); + +private: + struct TestCaseStruct + { + std::string name; + TestCase proc; + + bool operator <(const TestCaseStruct &other) const + { + return name < other.name; + } + + bool operator ==(const TestCaseStruct &other) const + { + return name == other.name; + } + + TestCaseStruct(const std::string &n, TestCase p) + : name(n), + proc(p) + { + } + }; + + typedef std::list TestCaseStructList; + typedef std::map TestCaseGroupMap; + TestCaseGroupMap m_testGroups; + + typedef std::set SelectedTestNameSet; + SelectedTestNameSet m_selectedTestNamesSet; + typedef std::set SelectedTestGroupSet; + SelectedTestGroupSet m_selectedTestGroupSet; + std::string m_currentGroup; + + DPL::Atomic m_totalAssertions; + + void Banner(); + void InvalidArgs(); + void Usage(); + + enum Status { FAILED, TODO, IGNORED, PASS }; + + Status RunTestCase(const TestCaseStruct& testCase); + + void RunTests(); + +public: + class TestFailed + { + private: + std::string m_message; + + public: + TestFailed() + { + } + + //! \brief Failed test message creator + //! + //! \param[in] aTest string for tested expression + //! \param[in] aFile source file name + //! \param[in] aLine source file line + //! \param[in] aMessage error message + TestFailed(const char* aTest, const char* aFile, int aLine, const std::string &aMessage); + + std::string GetMessage() const + { + return m_message; + } + }; + + class ToDo + { + private: + std::string m_message; + + public: + ToDo() + { + } + + ToDo(const std::string &message) + : m_message(message) + { + } + + std::string GetMessage() const + { + return m_message; + } + }; + + class Ignored + { + private: + std::string m_message; + + public: + Ignored() + { + } + + Ignored(const std::string &message) + : m_message(message) + { + } + + std::string GetMessage() const + { + return m_message; + } + }; + + void MarkAssertion(); + + void RegisterTest(const char *testName, TestCase proc); + void InitGroup(const char* name); + + int ExecTestRunner(int argc, char *argv[]); + typedef std::vector ArgsList; + int ExecTestRunner(const ArgsList& args); +}; + +typedef DPL::Singleton TestRunnerSingleton; + +} +} // namespace DPL + +#define RUNNER_TEST_GROUP_INIT(GroupName) \ + static int Static##GroupName##Init() \ + { \ + DPL::Test::TestRunnerSingleton::Instance().InitGroup(#GroupName);\ + return 0; \ + } \ + const int DPL_UNUSED Static##GroupName##InitVar = \ + Static##GroupName##Init(); + +#define RUNNER_TEST(Proc) \ + void Proc(); \ + static int Static##Proc##Init() \ + { \ + DPL::Test::TestRunnerSingleton::Instance().RegisterTest(#Proc, &Proc); \ + return 0; \ + } \ + const int DPL_UNUSED Static##Proc##InitVar = Static##Proc##Init(); \ + void Proc() + +//! \brief Returns base name for path + +#define RUNNER_ASSERT_MSG(test, message) \ +do \ +{ \ + DPL::Test::TestRunnerSingleton::Instance().MarkAssertion(); \ + \ + if (!(test)) \ + { \ + std::ostringstream assertMsg; \ + assertMsg << message; \ + throw DPL::Test::TestRunner::TestFailed(#test, __FILE__, __LINE__, assertMsg.str()); \ + } \ +} while (0) + +#define RUNNER_ASSERT(test) RUNNER_ASSERT_MSG(test, "") + +#define RUNNER_FAIL RUNNER_ASSERT(false) + +#define RUNNER_TODO_MSG(message) do { std::ostringstream assertMsg; assertMsg << message; throw DPL::Test::TestRunner::ToDo(assertMsg.str()); } while (0) + +#define RUNNER_IGNORED_MSG(message) do { std::ostringstream assertMsg; assertMsg << message; throw DPL::Test::TestRunner::Ignored(assertMsg.str()); } while (0) + +#endif // DPL_TEST_RUNNER_H diff --git a/modules/test/src/test_results_collector.cpp b/modules/test/src/test_results_collector.cpp new file mode 100644 index 0000000..07a6a69 --- /dev/null +++ b/modules/test/src/test_results_collector.cpp @@ -0,0 +1,529 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_results_collector.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief Implementation file some concrete TestResulstsCollector + */ + +#include +#include +#include +#include +#include + +#include +#include + +namespace DPL +{ +namespace Test +{ + +namespace +{ +const char *DEFAULT_HTML_FILE_NAME = "index.html"; + +class Statistic +{ + public: + Statistic() : + m_failed(0), + m_ignored(0), + m_todo(0), + m_passed(0), + m_count(0) + { + } + + void AddTest(TestResultsCollectorBase::FailStatus::Type type) + { + ++m_count; + switch (type) { + case TestResultsCollectorBase::FailStatus::INTERNAL: + case TestResultsCollectorBase::FailStatus::FAILED: ++m_failed; break; + case TestResultsCollectorBase::FailStatus::IGNORED: ++m_ignored; break; + case TestResultsCollectorBase::FailStatus::TODO: ++m_todo; break; + case TestResultsCollectorBase::FailStatus::NONE: ++m_passed; break; + default: + Assert(false && "Bad FailStatus"); + } + } + + size_t GetTotal() const { return m_count; } + size_t GetPassed() const { return m_passed; } + size_t GetSuccesed() const { return m_passed; } + size_t GetFailed() const { return m_failed; } + size_t GetTODO() const { return m_todo; } + size_t GetIgnored() const { return m_ignored; } + float GetPassedOrIgnoredPercend() const + { + float passIgnoredPercent = + 100.0f * (static_cast(m_passed) + + static_cast(m_ignored)) + / static_cast(m_count); + return passIgnoredPercent; + } + + private: + size_t m_failed; + size_t m_ignored; + size_t m_todo; + size_t m_passed; + size_t m_count; +}; + +class ConsoleCollector + : public TestResultsCollectorBase +{ + public: + static TestResultsCollectorBase* Constructor(); + + private: + ConsoleCollector() {} + + virtual void CollectCurrentTestGroupName(const std::string& name) + { + printf("Starting group %s\n", name.c_str()); + m_currentGroup = name; + } + + virtual void Finish() + { + using namespace DPL::Colors::Text; + + // Show result + FOREACH(group, m_groupsStats) { + PrintStats(group->first, group->second); + } + PrintStats("All tests together", m_stats); + } + + virtual void CollectResult(const std::string& id, + const std::string& /*description*/, + const FailStatus::Type status = FailStatus::NONE, + const std::string& reason = "") + { + using namespace DPL::Colors::Text; + std::string tmp = "'" + id + "' ..."; + + printf("Running test case %-60s", tmp.c_str()); + switch(status) { + case TestResultsCollectorBase::FailStatus::NONE: + printf("[%s%s%s]\n", BOLD_GREEN_BEGIN, " OK ", BOLD_GREEN_END); break; + case TestResultsCollectorBase::FailStatus::FAILED: + PrintfErrorMessage( " FAILED ", reason, true); break; + case TestResultsCollectorBase::FailStatus::IGNORED: + PrintfIgnoredMessage("Ignored ", reason, true); break; + case TestResultsCollectorBase::FailStatus::TODO: + PrintfTODOMessage( " TODO ", reason, true); break; + case TestResultsCollectorBase::FailStatus::INTERNAL: + PrintfErrorMessage( "INTERNAL", reason, true); break; + default: + Assert(false && "Bad status"); + } + m_stats.AddTest(status); + m_groupsStats[m_currentGroup].AddTest(status); + } + + void PrintfErrorMessage(const char* type, + const std::string& message, + bool verbosity) + { + using namespace DPL::Colors::Text; + if (verbosity) { + printf("[%s%s%s] %s%s%s\n", + BOLD_RED_BEGIN, + type, + BOLD_RED_END, + BOLD_YELLOW_BEGIN, + message.c_str(), + BOLD_YELLOW_END); + } else { + printf("[%s%s%s]\n", + BOLD_RED_BEGIN, + type, + BOLD_RED_END); + } + } + + void PrintfTODOMessage(const char* type, + const std::string& message, + bool verbosity) + { + using namespace DPL::Colors::Text; + if (verbosity) { + printf("[%s%s%s] %s%s%s\n", + BOLD_WHITE_BEGIN, + type, + BOLD_WHITE_END, + BOLD_GOLD_BEGIN, + message.c_str(), + BOLD_GOLD_END); + } else { + printf("[%s%s%s]\n", + BOLD_WHITE_BEGIN, + type, + BOLD_WHITE_END); + } + } + + void PrintfIgnoredMessage(const char* type, + const std::string& message, + bool verbosity) + { + using namespace DPL::Colors::Text; + if (verbosity) { + printf("[%s%s%s] %s%s%s\n", + CYAN_BEGIN, + type, + CYAN_END, + BOLD_GOLD_BEGIN, + message.c_str(), + BOLD_GOLD_END); + } else { + printf("[%s%s%s]\n", + CYAN_BEGIN , + type, + CYAN_END); + } + } + + void PrintStats(const std::string& title, const Statistic& stats) + { + using namespace DPL::Colors::Text; + printf("\n%sResults [%s]: %s\n", BOLD_GREEN_BEGIN, title.c_str(), BOLD_GREEN_END); + printf("%s%s%3d%s\n", CYAN_BEGIN, "Total tests: ", stats.GetTotal(), CYAN_END); + printf("%s%s%3d%s\n", CYAN_BEGIN, "Succeeded or ignored: ", stats.GetPassed() + stats.GetIgnored(), CYAN_END); + printf(" %s%s%3d%s\n", CYAN_BEGIN, "Succeeded: ", stats.GetPassed(), CYAN_END); + printf(" %s%s%3d%s\n", CYAN_BEGIN, "Ignored: ", stats.GetIgnored(), CYAN_END); + printf("%s%s%3d%s\n", CYAN_BEGIN, "Failed: ", stats.GetFailed(), CYAN_END); + printf("%s%s%3d%s\n", CYAN_BEGIN, "Todo: ", stats.GetTODO(), CYAN_END); + printf("%s%s%3.0f%%%s\n", CYAN_BEGIN, "Succeeded or ignored %: ", stats.GetPassedOrIgnoredPercend(), CYAN_END); + } + + Statistic m_stats; + std::map m_groupsStats; + std::string m_currentGroup; +}; + + +TestResultsCollectorBase* ConsoleCollector::Constructor() +{ + return new ConsoleCollector(); +} + + +class HtmlCollector + : public TestResultsCollectorBase +{ + public: + static TestResultsCollectorBase* Constructor(); + + private: + HtmlCollector() : m_filename(DEFAULT_HTML_FILE_NAME) {} + + virtual void CollectCurrentTestGroupName(const std::string& name) + { + fprintf(m_fp.Get(),"Starting group %s", name.c_str()); + m_currentGroup = name; + } + + virtual bool Configure() + { + m_fp.Reset(fopen (m_filename.c_str(), "w")); + if (!m_fp) { + LogPedantic("Could not open file " << m_filename << " for writing"); + return false; + } + return true; + } + virtual std::string CollectorSpecificHelp() const + { + return "--file= - name of file for output\n" + " default - index.html\n"; + } + + virtual void Start() + { + Assert(!!m_fp && "File handle must not be null"); + fprintf(m_fp.Get(), + "\n"); + fprintf(m_fp.Get(), + "\n"); + fprintf(m_fp.Get(), "\n"); + fprintf(m_fp.Get(), "
\n");
+        fprintf(m_fp.Get(), "\n");
+    }
+
+    virtual void Finish()
+    {
+        using namespace DPL::Colors::Html;
+        // Show result
+        FOREACH(group, m_groupsStats) {
+            PrintStats(group->first, group->second);
+        }
+        PrintStats("All tests together", m_stats);
+        fprintf(m_fp.Get(), "\n");
+        fprintf(m_fp.Get(), "
\n"); + fprintf(m_fp.Get(), "\n"); + fprintf(m_fp.Get(), "\n"); + } + + virtual bool ParseCollectorSpecificArg(const std::string& arg) + { + const std::string argname = "--file="; + if (0 == arg.find(argname)) { + m_filename = arg.substr(argname.size()); + return true; + } else { + return false; + } + } + + virtual void CollectResult(const std::string& id, + const std::string& /*description*/, + const FailStatus::Type status = FailStatus::NONE, + const std::string& reason = "") + { + using namespace DPL::Colors::Html; + std::string tmp = "'" + id + "' ..."; + + fprintf(m_fp.Get(), "Running test case %-100s", tmp.c_str()); + switch(status) { + case TestResultsCollectorBase::FailStatus::NONE: + fprintf(m_fp.Get(), "[%s%s%s]\n", BOLD_GREEN_BEGIN, " OK ", BOLD_GREEN_END); break; + case TestResultsCollectorBase::FailStatus::FAILED: + PrintfErrorMessage( " FAILED ", reason, true); break; + case TestResultsCollectorBase::FailStatus::IGNORED: + PrintfIgnoredMessage("Ignored ", reason, true); break; + case TestResultsCollectorBase::FailStatus::TODO: + PrintfTODOMessage( " TODO ", reason, true); break; + case TestResultsCollectorBase::FailStatus::INTERNAL: + PrintfErrorMessage( "INTERNAL", reason, true); break; + default: + Assert(false && "Bad status"); + } + m_groupsStats[m_currentGroup].AddTest(status); + m_stats.AddTest(status); + } + + void PrintfErrorMessage(const char* type, + const std::string& message, + bool verbosity) + { + using namespace DPL::Colors::Html; + if (verbosity) { + fprintf(m_fp.Get(), + "[%s%s%s] %s%s%s\n", + BOLD_RED_BEGIN, + type, + BOLD_RED_END, + BOLD_YELLOW_BEGIN, + message.c_str(), + BOLD_YELLOW_END); + } else { + fprintf(m_fp.Get(), + "[%s%s%s]\n", + BOLD_RED_BEGIN, + type, + BOLD_RED_END); + } + } + + void PrintfTODOMessage(const char* type, + const std::string& message, + bool verbosity) + { + using namespace DPL::Colors::Html; + if (verbosity) { + fprintf(m_fp.Get(), + "[%s%s%s] %s%s%s\n", + BOLD_WHITE_BEGIN, + type, + BOLD_WHITE_END, + BOLD_GOLD_BEGIN, + message.c_str(), + BOLD_GOLD_END); + } else { + fprintf(m_fp.Get(), + "[%s%s%s]\n", + BOLD_WHITE_BEGIN, + type, + BOLD_WHITE_END); + } + } + + void PrintfIgnoredMessage(const char* type, + const std::string& message, + bool verbosity) + { + using namespace DPL::Colors::Html; + + if (verbosity) { + fprintf(m_fp.Get(), + "[%s%s%s] %s%s%s\n", + CYAN_BEGIN, + type, + CYAN_END, + BOLD_GOLD_BEGIN, + message.c_str(), + BOLD_GOLD_END); + } else { + fprintf(m_fp.Get(), + "[%s%s%s]\n", + CYAN_BEGIN , + type, + CYAN_END); + } + } + + void PrintStats(const std::string& name, const Statistic& stats) + { + using namespace DPL::Colors::Html; + fprintf(m_fp.Get(), "\n%sResults [%s]:%s\n", BOLD_GREEN_BEGIN, name.c_str(), BOLD_GREEN_END); + fprintf(m_fp.Get(), "%s%s%3d%s\n", CYAN_BEGIN, "Total tests: ", stats.GetTotal(), CYAN_END); + fprintf(m_fp.Get(), "%s%s%3d%s\n", CYAN_BEGIN, "Succeeded or ignored: ", stats.GetPassed() + stats.GetIgnored(), CYAN_END); + fprintf(m_fp.Get(), " %s%s%3d%s\n", CYAN_BEGIN, "Succeeded: ", stats.GetPassed(), CYAN_END); + fprintf(m_fp.Get(), " %s%s%3d%s\n", CYAN_BEGIN, "Ignored: ", stats.GetIgnored(), CYAN_END); + fprintf(m_fp.Get(), "%s%s%3d%s\n", CYAN_BEGIN, "Failed: ", stats.GetFailed(), CYAN_END); + fprintf(m_fp.Get(), "%s%s%3d%s\n", CYAN_BEGIN, "Todo: ", stats.GetTODO(), CYAN_END); + fprintf(m_fp.Get(), "%s%s%3.0f%%%s\n", CYAN_BEGIN, "Succeeded or ignored %: ", stats.GetPassedOrIgnoredPercend(), CYAN_END); + } + + std::string m_filename; + ScopedFClose m_fp; + Statistic m_stats; + std::string m_currentGroup; + std::map m_groupsStats; +}; + +TestResultsCollectorBase* HtmlCollector::Constructor() +{ + return new HtmlCollector(); +} + +class CSVCollector + : public TestResultsCollectorBase +{ + public: + static TestResultsCollectorBase* Constructor(); + + private: + CSVCollector() {} + + virtual void Start() { + printf("GROUP;ID;RESULT;REASON\n"); + } + + virtual void CollectCurrentTestGroupName(const std::string& name) + { + m_currentGroup = name; + } + + virtual void CollectResult(const std::string& id, + const std::string& /*description*/, + const FailStatus::Type status = FailStatus::NONE, + const std::string& reason = "") + { + std::string statusMsg = ""; + switch(status) { + case TestResultsCollectorBase::FailStatus::NONE: statusMsg = "OK"; break; + case TestResultsCollectorBase::FailStatus::FAILED: statusMsg = "FAILED"; break; + case TestResultsCollectorBase::FailStatus::IGNORED: statusMsg = "IGNORED"; break; + case TestResultsCollectorBase::FailStatus::TODO: statusMsg = "TODO"; break; + case TestResultsCollectorBase::FailStatus::INTERNAL: statusMsg = "FAILED"; break; + default: + Assert(false && "Bad status"); + } + printf("%s;%s;%s;%s\n", + m_currentGroup.c_str(), + id.c_str(), + statusMsg.c_str(), + reason.c_str()); + } + + std::string m_currentGroup; +}; + + +TestResultsCollectorBase* CSVCollector::Constructor() +{ + return new CSVCollector(); +} + +} + +void TestResultsCollectorBase::RegisterCollectorConstructor( + const std::string& name, + TestResultsCollectorBase::CollectorConstructorFunc func) +{ + Assert(m_constructorsMap.find(name) == m_constructorsMap.end()); + m_constructorsMap[name] = func; +} + +TestResultsCollectorBase* TestResultsCollectorBase::Create( + const std::string& name) +{ + ConstructorsMap::iterator found = m_constructorsMap.find(name); + if (found != m_constructorsMap.end()) + return found->second(); + else + return NULL; +} + +std::vector TestResultsCollectorBase::GetCollectorsNames() +{ + std::vector list; + FOREACH(it, m_constructorsMap) + { + list.push_back(it->first); + } + return list; +} + +TestResultsCollectorBase::ConstructorsMap TestResultsCollectorBase::m_constructorsMap; + +namespace +{ +static int RegisterCollectorConstructors(); +static const int RegisterHelperVariable = RegisterCollectorConstructors(); +int RegisterCollectorConstructors() +{ + (void)RegisterHelperVariable; + + TestResultsCollectorBase::RegisterCollectorConstructor( + "text", + &ConsoleCollector::Constructor); + TestResultsCollectorBase::RegisterCollectorConstructor( + "html", + &HtmlCollector::Constructor); + TestResultsCollectorBase::RegisterCollectorConstructor( + "csv", + &CSVCollector::Constructor); + + return 0; +} + +} + +} +} diff --git a/modules/test/src/test_runner.cpp b/modules/test/src/test_runner.cpp new file mode 100644 index 0000000..8097c23 --- /dev/null +++ b/modules/test/src/test_runner.cpp @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_runner.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of test runner + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +IMPLEMENT_SINGLETON(DPL::Test::TestRunner) + +namespace DPL +{ +namespace Test +{ + +namespace // anonymous +{ + +std::string BaseName(std::string aPath) +{ + ScopedFree path(strdup(aPath.c_str())); + if (NULL == path.Get()) + { + throw std::bad_alloc(); + } + char* baseName = basename(path.Get()); + std::string retValue = baseName; + return retValue; +} + +} // namespace anonymous + +//! \brief Failed test message creator +//! +//! \param[in] aTest string for tested expression +//! \param[in] aFile source file name +//! \param[in] aLine source file line +//! \param[in] aMessage error message +TestRunner::TestFailed::TestFailed(const char* aTest, + const char* aFile, + int aLine, + const std::string &aMessage) +{ + std::ostringstream assertMsg; + assertMsg << "[" << BaseName(aFile) << ":" << aLine + << "] Assertion failed (" + << aTest << ") " << aMessage; + m_message = assertMsg.str(); +} + +void TestRunner::RegisterTest(const char *testName, TestCase proc) +{ + m_testGroups[m_currentGroup].push_back(TestCaseStruct(testName, proc)); +} + +void TestRunner::InitGroup(const char* name) +{ + m_currentGroup = name; +} + + +TestRunner::Status TestRunner::RunTestCase(const TestCaseStruct& testCase) +{ + try + { + testCase.proc(); + } + catch (const TestFailed &e) + { + // Simple test failure + m_collector->CollectResult(testCase.name, + "", + TestResultsCollectorBase::FailStatus::FAILED, + e.GetMessage()); + return FAILED; + } + catch (const Ignored &e) + { + // Simple test have to be implemented + m_collector->CollectResult(testCase.name, + "", + TestResultsCollectorBase::FailStatus::IGNORED, + e.GetMessage()); + + return IGNORED; + } + catch (const ToDo &e) + { + // Simple test have to be implemented + m_collector->CollectResult(testCase.name, + "", + TestResultsCollectorBase::FailStatus::TODO, + e.GetMessage()); + + return TODO; + } + catch (const DPL::Exception &e) + { + // DPL exception failure + m_collector->CollectResult(testCase.name, + "", + TestResultsCollectorBase::FailStatus::INTERNAL, + "DPL exception:" + + e.GetMessage()); + + return FAILED; + } + catch (const std::exception &) + { + // std exception failure + m_collector->CollectResult(testCase.name, + "", + TestResultsCollectorBase::FailStatus::INTERNAL, + "std exception"); + + return FAILED; + } + catch (...) + { + // Unknown exception failure + m_collector->CollectResult(testCase.name, + "", + TestResultsCollectorBase::FailStatus::INTERNAL, + "unknown exception"); + + return FAILED; + } + + m_collector->CollectResult(testCase.name, + "", + TestResultsCollectorBase::FailStatus::NONE); + // Everything OK + return PASS; +} + +void TestRunner::RunTests() +{ + using namespace DPL::Colors::Text; + + Banner(); + m_collector->Start(); + + fprintf(stderr, "%s%s%s\n", GREEN_BEGIN, "Running tests...", GREEN_END); + FOREACH(group, m_testGroups) { + TestCaseStructList list = group->second; + if (!list.empty()) { + m_collector->CollectCurrentTestGroupName(group->first); + list.sort(); + + for (TestCaseStructList::const_iterator iterator = list.begin(); + iterator != list.end(); + ++iterator) + { + TestCaseStruct test = *iterator; + if (m_startTestId == test.name) + m_startTestId = ""; + + if (m_startTestId.empty()) { + RunTestCase(test); + } + } + } + } + + m_collector->Finish(); + // Finished + fprintf(stderr, "%s%s%s\n\n", GREEN_BEGIN, "Finished", GREEN_END); +} + +void TestRunner::Banner() +{ + using namespace DPL::Colors::Text; + fprintf(stderr, + "%s%s%s\n", + BOLD_GREEN_BEGIN, + "DPL tests runner", + BOLD_GREEN_END); + fprintf(stderr, + "%s%s%s%s\n\n", + GREEN_BEGIN, + "Build: ", + __TIMESTAMP__, + GREEN_END); +} + +void TestRunner::InvalidArgs() +{ + using namespace DPL::Colors::Text; + fprintf(stderr, + "%s%s%s\n", + BOLD_RED_BEGIN, + "Invalid parameters!", + BOLD_RED_END); +} + +void TestRunner::Usage() +{ + fprintf(stderr, "Usage: runner [options]\n\n"); + fprintf(stderr, "Output type:\n"); + fprintf(stderr, " --output=\n"); + fprintf(stderr, " possible output types:\n"); + FOREACH (type, TestResultsCollectorBase::GetCollectorsNames()) { + fprintf(stderr, " --output=%s\n", type->c_str()); + } + fprintf(stderr, "Other parameters:\n"); + fprintf(stderr, + " --regexp='regexp'\t Only selected tests" + " which names match regexp run\n\n"); + fprintf(stderr, " --start=\tStart from concrete test id"); + fprintf(stderr, " --group=\t Run tests only from one group\n"); + fprintf(stderr, " --list\t Show a list of Test IDs\n"); + fprintf(stderr, " --listgroups\t Show a list of Test Group names \n"); + fprintf(stderr, " --listingroup=\t Show a list of Test IDS in one group\n"); + fprintf(stderr, " --help\t This help\n\n"); + if (m_collector) { + fprintf(stderr, "Output %s has specific args:\n", m_collectorName.c_str()); + fprintf(stderr, "%s\n", m_collector->CollectorSpecificHelp().c_str()); + } + fprintf(stderr, "For bug reporting, please write to:\n"); + fprintf(stderr, "\n"); +} + +int TestRunner::ExecTestRunner(int argc, char *argv[]) +{ + std::vector args; + for (int i = 0; i < argc; ++i) + { + args.push_back(argv[i]); + } + return ExecTestRunner(args); +} + +void TestRunner::MarkAssertion() +{ + ++m_totalAssertions; +} + +int TestRunner::ExecTestRunner(const ArgsList& value) +{ + ArgsList args = value; + // Parse command line + if (args.size() == 1) + { + InvalidArgs(); + Usage(); + return -1; + } + + args.erase(args.begin()); + + bool showHelp = false; + + // Parse each argument + FOREACH(it, args) + { + std::string arg = *it; + const std::string regexp = "--regexp="; + const std::string output = "--output="; + const std::string groupId = "--group="; + const std::string listCmd = "--list"; + const std::string startCmd = "--start="; + const std::string listGroupsCmd = "--listgroups"; + const std::string listInGroup = "--listingroup="; + + if (m_collector && m_collector->ParseCollectorSpecificArg(arg)) continue; + else if (arg.find(startCmd) == 0) + { + arg.erase(0, startCmd.length()); + FOREACH(group, m_testGroups) { + FOREACH(tc, group->second) { + if (tc->name == arg) { + m_startTestId = arg; + break; + } + } + if (!m_startTestId.empty()) break; + } + if (!m_startTestId.empty()) continue; + InvalidArgs(); + fprintf(stderr, "Start test id has not been found\n"); + Usage(); + return 0; + } + else if (arg.find(groupId) == 0) + { + arg.erase(0, groupId.length()); + TestCaseGroupMap::iterator found = m_testGroups.find(arg); + if (found != m_testGroups.end()) { + std::string name = found->first; + TestCaseStructList newList = found->second; + m_testGroups.clear(); + m_testGroups[name] = newList; + } else { + fprintf(stderr, "Group %s not found\n", arg.c_str()); + InvalidArgs(); + Usage(); + return -1; + } + } + else if (arg == listCmd) + { + FOREACH(group, m_testGroups) { + FOREACH(test, group->second) { + printf("ID:%s:%s\n", group->first.c_str(), test->name.c_str()); + } + } + return 0; + } + else if (arg == listGroupsCmd) + { + FOREACH(group, m_testGroups) { + printf("GR:%s\n", group->first.c_str()); + } + return 0; + } + else if (arg.find(listInGroup) == 0) + { + arg.erase(0, listInGroup.length()); + FOREACH(test, m_testGroups[arg]) { + printf("ID:%s\n", test->name.c_str()); + } + return 0; + } + else if (arg == "--help") + showHelp = true; + else if (arg.find(output) == 0) + { + arg.erase(0, output.length()); + m_collector.Reset(TestResultsCollectorBase::Create(arg)); + if (!m_collector) { + InvalidArgs(); + Usage(); + return -1; + } else { + m_collectorName = arg; + } + } + else if (arg.find(regexp) == 0) + { + arg.erase(0, regexp.length()); + if (arg.length() == 0) + { + InvalidArgs(); + Usage(); + return -1; + } + + if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') + { + arg.erase(0); + arg.erase(arg.length() - 1); + } + + if (arg.length() == 0) + { + InvalidArgs(); + Usage(); + return -1; + } + + pcrecpp::RE re(arg.c_str()); + FOREACH(group, m_testGroups) { + TestCaseStructList newList; + FOREACH(iterator, group->second) + { + if (re.PartialMatch(iterator->name)) + { + newList.push_back(*iterator); + } + } + group->second = newList; + } + } + else + { + InvalidArgs(); + Usage(); + return -1; + } + } + + // Show help + if (showHelp) + { + Usage(); + return 0; + } + + if (!m_collector) + m_collector.Reset(TestResultsCollectorBase::Create("text")); + + if (!m_collector->Configure()) { + fprintf(stderr, "Could not configure selected output"); + return 0; + } + + // Run tests + RunTests(); + + return 0; +} + +} +} // namespace DPL diff --git a/modules/utils/config.cmake b/modules/utils/config.cmake new file mode 100644 index 0000000..96efba7 --- /dev/null +++ b/modules/utils/config.cmake @@ -0,0 +1,50 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# @file config.cmake +# @author Soyoung Kim(sy037.kim@samsung.com) +# @version 1.0 +# @brief +# + +SET(DPL_UTILS_SOURCES + ${PROJECT_SOURCE_DIR}/modules/utils/src/file_utils.cpp + ${PROJECT_SOURCE_DIR}/modules/utils/src/folder_size.cpp + ${PROJECT_SOURCE_DIR}/modules/utils/src/mime_type_utils.cpp + ${PROJECT_SOURCE_DIR}/modules/utils/src/warp_iri.cpp + ${PROJECT_SOURCE_DIR}/modules/utils/src/widget_version.cpp + ${PROJECT_SOURCE_DIR}/modules/utils/src/wrt_global_settings_internal.cpp + ${PROJECT_SOURCE_DIR}/modules/utils/src/wrt_global_settings.cpp + ${PROJECT_SOURCE_DIR}/modules/utils/src/wrt_utility.cpp + PARENT_SCOPE +) + + +SET(DPL_UTILS_HEADERS + ${PROJECT_SOURCE_DIR}/modules/utils/include/file_utils.h + ${PROJECT_SOURCE_DIR}/modules/utils/include/folder_size.h + ${PROJECT_SOURCE_DIR}/modules/utils/include/mime_type_utils.h + ${PROJECT_SOURCE_DIR}/modules/utils/include/warp_iri.h + ${PROJECT_SOURCE_DIR}/modules/utils/include/widget_version.h + ${PROJECT_SOURCE_DIR}/modules/utils/include/wrt_global_settings.h + ${PROJECT_SOURCE_DIR}/modules/utils/include/wrt_global_settings_internal.h + ${PROJECT_SOURCE_DIR}/modules/utils/include/wrt_utility.h + PARENT_SCOPE +) + +SET(DPL_UTILS_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/modules/utils/include + PARENT_SCOPE +) diff --git a/modules/utils/include/file_utils.h b/modules/utils/include/file_utils.h new file mode 100644 index 0000000..77e4aa3 --- /dev/null +++ b/modules/utils/include/file_utils.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file file_utils.h + * @author Bartosz Janiak (b.janiak@samsung.com) + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + */ + +#ifndef FILEUTILS_H +#define FILEUTILS_H + +#include +#include + +#include +#include + +namespace FileUtils { +DECLARE_EXCEPTION_TYPE(DPL::Exception, RemoveDirectoryException) + +bool FileExists(const DPL::String& absolutePath); + +/** + * Creates specified path recursively. + * @param path Path to create (e.g. /tmp/dir1/dir2). + * @param mode Mode for the non-existing parts of the path (e.g. 0755). + * @throw DPL::CommonException::::InternalError If sth bad happens. + */ +void MakePath(const std::string& path, + mode_t mode); + +/** + * Removes specified directory recursively. + * @param path Full path to directory to remove. + * @throw FileUtils::DirectoryRemoveException If an error occured. + */ +void RemoveDir(const std::string& path); +}; + +#endif diff --git a/modules/utils/include/folder_size.h b/modules/utils/include/folder_size.h new file mode 100644 index 0000000..db51de9 --- /dev/null +++ b/modules/utils/include/folder_size.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * + * @file folder_size.h + * @author Jaroslaw Osmanski (j.osmanski@samsung.com) + * @version 1.0 + * @brief Declaration for function calculating directory size + */ + +#ifndef SRC_COMMON_FOLDER_SIZE_H_ +#define SRC_COMMON_FOLDER_SIZE_H_ + +#include + +#include + +namespace Utils { + +size_t getFolderSize(const std::string& path); + +DPL::String fromFileSizeString(size_t fileSize); + +} + +#endif /* SRC_COMMON_FOLDER_SIZE_H_ */ diff --git a/modules/utils/include/mime_type_utils.h b/modules/utils/include/mime_type_utils.h new file mode 100644 index 0000000..b536c20 --- /dev/null +++ b/modules/utils/include/mime_type_utils.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MIME_TYPE_UTILS_H +#define MIME_TYPE_UTILS_H + +#include +#include + +class MimeTypeUtils +{ + public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, InvalidFileName) + }; + + private: + //TODO use hash_map if possible + static const std::set& getMimeTypesSupportedForIcon(); + static const std::set& getMimeTypesSupportedForStartFile(); + + typedef std::map FileIdentificationMap; + + static DPL::String getFileNameFromPath(const DPL::String& path); + static const FileIdentificationMap& getFileIdentificationMap(); + static DPL::String stripMimeParameters(const DPL::String& mimeType); + + public: + typedef std::map MimeAttributes; + static bool isValidIcon(const DPL::String& path); + static bool isValidStartFile(const DPL::String& path, + const DPL::OptionalString& providedMimeType); + static bool isMimeTypeSupportedForStartFile(const DPL::String& mimeType); + static bool isMimeTypeSupportedForIcon(const DPL::String& mimeType); + static MimeAttributes getMimeAttributes(const DPL::String& mimeType); + ///implements 9.1.10. (Rule for Identifying the Media Type of a File) + ///from W3C packaging specification + static DPL::String identifyFileMimeType(const DPL::String& path); +}; + +#endif /* MIME_TYPE_UTILS_H */ + diff --git a/modules/utils/include/warp_iri.h b/modules/utils/include/warp_iri.h new file mode 100644 index 0000000..901fd15 --- /dev/null +++ b/modules/utils/include/warp_iri.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _WARPIRI_H_ +#define _WARPIRI_H_ + +#include + +#include +#include + +class WarpIRI +{ + static const unsigned int UNKNOWN_PORT = 0; + public: + WarpIRI(); + + void set(const char *iri, + bool domain); + void set(const DPL::String &iristring, + bool domain); + + /* It also checks port and schema */ + bool isSubDomain(const WarpIRI &second) const; + bool isAccessDefinition() const; + // KW bool isIRIValid() const; + bool getSubDomain() const; + + /* This is debug function */ + // KW void print() const { + // KW LogInfo("P:" << m_port << " S:" << m_schema); + // KW for (size_t i = 0; i < m_host.size(); ++i) + // KW LogInfo(" " << m_host[i]); + // KW } + + static bool isIRISchemaIgnored(const char *iri); + + bool operator ==(const WarpIRI &other) const + { + return m_domain == other.m_domain && + m_host == other.m_host && + m_schema == other.m_schema && + m_port == other.m_port && + m_isAccessDefinition == other.m_isAccessDefinition && + m_isIRIValid == other.m_isIRIValid; + } + + private: + unsigned int getPort(const DPL::String &schema) const; + + bool m_domain; + std::vector m_host; + DPL::String m_schema; + unsigned int m_port; + bool m_isAccessDefinition; + bool m_isIRIValid; +}; + +#endif // _WarpIRI_H_ diff --git a/modules/utils/include/widget_version.h b/modules/utils/include/widget_version.h new file mode 100644 index 0000000..5f40e72 --- /dev/null +++ b/modules/utils/include/widget_version.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_version.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Header file for widget version + */ +#ifndef WIDGET_VERSION_H +#define WIDGET_VERSION_H + +#include +#include +#include + +/* + * Note: This class also support non-WAC compliant version numbers + * + * WAC Waikiki Beta Release Core Specification: Widget Runtime + * 10 Dec 2010 + * + * WL-3370 The WRT MUST process widget packages as an update when received under the following conditions: + * + * - the Widget Id matches the Widget Id of an installed widget + * - the Widget version number is greater (as a compared string) than that of the installed widget, or no version + * information was provided for the installed widget + * + * To ensure that a string comparison of widget versions can reliably determine which version is an updated widget, + * WAC will mandate a specific version string format for WAC widgets. All widgets coming through the WAC channel + * will be required to have version strings in this format. Side-loaded widgets may have any format and, in this + * case, there is no requirement that the WRT support version detection for update of these widgets. + * + * The widget version format is the rec-version-tag grammar as described in [Widget Packaging]: + * + * rec-version-tag = 1*DIGIT "." 1*DIGIT [ "." 1*DIGIT] *[ 1*ALPHA / SP / 1*DIGIT ] + * + * Examples of rec-version-tag: + * + * 1.0 + * 1.10.1 beta1 + * 1.02.12 RC1 + * + * WL-3371 The WRT MUST use the following widget version comparison algorithm to compare WAC widget version strings: + * + * - prepare the version strings for comparison: + * - all leading zeros are discarded + * - the optional *[ 1*ALPHA / SP / 1*DIGIT ] part, if present, is discarded + * - the resulting numbers are then in the format major.minor[.micro] + * - Version A = Amajor.Aminor[.Amicro] is equal to Version B = Bmajor.Bminor[.Bmicro] if and only if: + * - Amajor Bmajor + * - Aminor Bminor + * - both Amicro and Bmicro are present and Amicro == Bmicro; or both Amicro and Bmicro are absent. + * - Version A = Amajor.Aminor[.Amicro] is greater than Version B = Bmajor.Bminor[.Bmicro] if and only if: + * - Amajor > Bmajor; or + * - Amajor Bmajor && Aminor > Bminor; or + * - Amajor Bmajor && Aminor == Bminor && both Amicro and Bmicro are present and Amicro > Bmicro; or Bmicro is absent. + */ +class WidgetVersion +{ + private: + bool m_isWac; + DPL::String m_raw; + + DPL::String m_major; + DPL::String m_minor; + DPL::Optional m_micro; + DPL::Optional m_optional; + + void WacCertify(const DPL::String &major, + const DPL::String &minor, + const DPL::Optional µ, + const DPL::Optional &optional); + + public: + explicit WidgetVersion(const DPL::String &str = DPL::String()); + WidgetVersion(const DPL::String &major, + const DPL::String &minor, + const DPL::Optional µ, + const DPL::Optional &optional); + + bool IsWac() const; + const DPL::String &Raw() const; + + const DPL::String &Major() const; + const DPL::String &Minor() const; + const DPL::Optional &Micro() const; + const DPL::Optional &Optional() const; +}; + +bool operator<(const WidgetVersion &left, + const WidgetVersion &right); +bool operator<=(const WidgetVersion &left, + const WidgetVersion &right); +bool operator>(const WidgetVersion &left, + const WidgetVersion &right); +bool operator>=(const WidgetVersion &left, + const WidgetVersion &right); +bool operator==(const WidgetVersion &left, + const WidgetVersion &right); +bool operator!=(const WidgetVersion &left, + const WidgetVersion &right); +std::ostream & operator<<(std::ostream& stream, + const WidgetVersion& version); + +#endif // WIDGET_VERSION_H diff --git a/modules/utils/include/wrt_global_settings.h b/modules/utils/include/wrt_global_settings.h new file mode 100644 index 0000000..9a51cd1 --- /dev/null +++ b/modules/utils/include/wrt_global_settings.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file wrt_global_settings.h + * @version 0.6 + * @author Pawel Sikorski(p.sikorski@samsung.com) + * @brief Header file for global predefined wrt setting + */ + +#ifndef WRT_COMMON_GLOBAL_SETTINGS_H_ +#define WRT_COMMON_GLOBAL_SETTINGS_H_ + +namespace GlobalSettings { +//TODO description +bool GetPopupsEnabledFlag(); +} + +#endif /* WRT_COMMON_GLOBAL_SETTINGS_H_ */ diff --git a/modules/utils/include/wrt_global_settings_internal.h b/modules/utils/include/wrt_global_settings_internal.h new file mode 100644 index 0000000..ac57956 --- /dev/null +++ b/modules/utils/include/wrt_global_settings_internal.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file wrt_global_settings_internal.h + * @version 0.6 + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @brief Header file for global predefined wrt setting - internal interface + */ + +#ifndef WRT_SRC_COMMON_WRT_GLOBAL_SETTINGS_INTERNAL_H_ +#define WRT_SRC_COMMON_WRT_GLOBAL_SETTINGS_INTERNAL_H_ + +namespace GlobalSettings { +struct IGlobalSettingsFunctions +{ + bool (*getPopupsEnabledFlag)(); +}; + +void SetPredefinedGlobalSettings(IGlobalSettingsFunctions functions); +} + +#endif //WRT_SRC_COMMON_WRT_GLOBAL_SETTINGS_INTERNAL_H_ diff --git a/modules/utils/include/wrt_utility.h b/modules/utils/include/wrt_utility.h new file mode 100644 index 0000000..30f8114 --- /dev/null +++ b/modules/utils/include/wrt_utility.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file wrt_utility.h + * @version 0.6 + * @author Wei Dong(d.wei@samsung.com) + * @author Ma Quan(jason.ma@samsung.com) + * @brief Header file of widget manager common functions + */ + +#ifndef _WRT_UTILITY_H_ +#define _WRT_UTILITY_H_ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef MAX_WIDGET_PATH_LENGTH +#define MAX_WIDGET_PATH_LENGTH 1024 +#endif + +/** + * File options + */ +enum +{ + WRT_FILEUTILS_PRESERVE_STATUS = 1, + WRT_FILEUTILS_PRESERVE_SYMLINKS = 2, + WRT_FILEUTILS_RECUR = 4, + WRT_FILEUTILS_FORCE = 8, + WRT_FILEUTILS_INTERACTIVE = 16 +}; + +/** + * Combine the parentPath and fileName into a new absolute file name. + * + * @param[out] absolutePath + * @param[in] parentPath + * @param[in] fileName + * + * @return if success, return true; or return false. + */ +bool _WrtUtilSetAbsolutePath(char* absolutePath, + const char* parentPath, + const char* fileName); + +/** + * Change the string to bool value,no case sensitive, e.x., "true" or "1" to ture; "False" or "0" to false. + * + * @param[in] value + * @param[out] result + * + * @return if success, return true; or return false. + */ +// KW bool _WrtUtilConvertStrToBool(char* value, bool *result); + +/** + * Get the dir path and file name from the full path. e.x., "/opt/lib/filename" as fullPath, "/opt/lib/" as dirName, "filename" as fileName. + * it's necessary to free *dirName or *fileName if either of them is not NULL. + * + * @param[in] fullPath + * @param[out] dirName + * @param[out] fileName + * + * @return + */ +void _WrtUtilGetDirAndFileName(const char* fullPath, + char** dirName, + char** fileName); + +#if 0 +/** + * Change the provided string into lower case, caller should allocate the memory of source and dest. + * + * @param[in] source the source string to be changed + * @param[out] dest the dest string with lower case + * + * return if success, return true, or return false. + */ +bool _WrtUtilStringToLower(const char* source, + char* dest); +#endif + +/** + * Compare two string, no case sensitive. + * + * @param[in] srcStr + * @param[in] destStr + * + * return return true if the two strings are identical, or return false. + */ +// KW bool _WrtUtilStringCmp(const char* srcStr, const char* destStr); + +/** + * This function is used to make a directory. + * it's neccessary to free the returned dir path. + * + * @param[in] path Specified the directory path + * @param[in] mode Operation mode the you want to set + * @param[in] flags WRT_FILEUTILS_RECUR if you want to make parent's directory recusively, + * WRT_FILEUTILS_NONE if not. + * + * @return TRUE on success or FALSE on failure. + */ +bool _WrtMakeDir (const char *path, + long mode, + int flags); + +/** + * This function is used to change to specified directory. + * If the directory does not exist, it will create it directly. + * + * @param[in] path Specified the directory path + * + * @return TRUE on success or FALSE on failure. + */ +bool _WrtUtilChangeDir(const char* path); + +/** + * This function is used to remove a directory from the file system. + * + * @param[in] path Specified the directory path + * + * @return TRUE on success or FALSE on failure. + */ +bool _WrtUtilRemoveDir(const char* path); + +// KW /** +// KW * This function is used to make a temp directory in root directory. +// KW * +// KW * @param[in] root Specified the root directory +// KW * +// KW * @return if fails, return NULL, else return the temp path. +// KW * it's necessary to free the returned memory space. +// KW */ +// KW char* _WrtUtilMakeTempDir(const char* root); + +/** + * This function is used to convert a string to lowercase. + * + * @param[in] str the string need to be converted. + * @param[in] lowerStr the converted string. + * + * @return TRUE on success or FALSE on failure. + */ +bool _WrtUtilStringToLower(const char* str, + char** lowerStr); + +#ifdef __cplusplus +} +#endif + +#endif //_WRT_UTILITY_H_ + diff --git a/modules/utils/src/file_utils.cpp b/modules/utils/src/file_utils.cpp new file mode 100644 index 0000000..097fd4a --- /dev/null +++ b/modules/utils/src/file_utils.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file file_utils.cpp + * @author Bartosz Janiak (b.janiak@samsung.com) + * @version 1.0 + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace { +int try_mkdir(const char* path, + mode_t mode) +{ + struct stat st; + int err = 0; + + if (::stat(path, &st) != 0) { + if (::mkdir(path, mode) != 0) { + err = -1; + } + } else if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + err = -1; + } + + return err; +} + +int mkpath(const char* path, + mode_t mode) +{ + char* copy = ::strdup(path); + if (NULL == copy) { + return -1; + } + + int err = 0; + char* ptr = copy; + char* slash = NULL; + + while ((0 == err) && (NULL != (slash = ::strchr(ptr, '/')))) { + if (slash != ptr) { + *slash = '\0'; + err = try_mkdir(copy, mode); + *slash = '/'; + } + ptr = slash + 1; + } + + if (0 == err) { + err = try_mkdir(path, mode); + } + + ::free(copy); + return err; +} + +int RmNode(const char* path); + +int RmDir(const char* path) +{ + DIR* dir = ::opendir(path); + if (NULL == dir) { + return -1; + } + + struct dirent* entry = NULL; + do { + errno = 0; + if (NULL != (entry = ::readdir(dir))) { + if (!::strncmp(entry->d_name, ".", 1) || + !::strncmp(entry->d_name, "..", 2)) { + continue; + } + std::string fullPath = WrtDB::PathBuilder(path) + .Append(entry->d_name) + .GetFullPath(); + if (RmNode(fullPath.c_str()) != 0) { + int error = errno; + TEMP_FAILURE_RETRY(::closedir(dir)); + errno = error; + return -1; + } + } + } + while (NULL != entry); + + int error = errno; + if (TEMP_FAILURE_RETRY(::closedir(dir)) != 0) { + return -1; + } + errno = error; + + return (errno == 0 ? ::rmdir(path) : -1); +} + +int RmNode(const char* path) +{ + struct stat st; + if (::lstat(path, &st) != 0) { + return -1; + } + return (S_ISDIR(st.st_mode) ? RmDir(path) : ::unlink(path)); +} +} + +namespace FileUtils { +bool FileExists(const DPL::String& absolutePath) +{ + struct stat statStruct; + if (stat(DPL::ToUTF8String(absolutePath).c_str(), &statStruct) == 0) { + return S_ISREG(statStruct.st_mode); + } else { + return false; + } +} + +void MakePath(const std::string& path, + mode_t mode) +{ + if (mkpath(path.c_str(), mode) == -1) { + ThrowMsg(DPL::CommonException::InternalError, "Cannot make path"); + } +} + +void RemoveDir(const std::string& path) +{ + if (RmDir(path.c_str()) != 0) { + ThrowMsg(RemoveDirectoryException, DPL::GetErrnoString()); + } +} +} diff --git a/modules/utils/src/folder_size.cpp b/modules/utils/src/folder_size.cpp new file mode 100644 index 0000000..87646f7 --- /dev/null +++ b/modules/utils/src/folder_size.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * + * @file folder_size.cpp + * @author Jaroslaw Osmanski (j.osmanski@samsung.com) + * @version 1.0 + * @brief Implementation for function calculating directory size + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace Utils { +namespace { + +size_t getObjectSize(const std::string& path) +{ + struct stat tmp; + + if (stat(path.c_str(), &tmp) == -1) { + LogError("Failed to open file" << path); + return 0; + } + //it is not a file nor a directory + //not counting + if (!S_ISDIR(tmp.st_mode) && !S_ISREG(tmp.st_mode)) { + LogWarning("Not a regular file nor a directory: " << path); + return 0; + } + return tmp.st_size; +} +} + +size_t getFolderSize(const std::string& path) +{ + size_t size = 0; + + DIR *dir; + std::vector localDirs; + if ((dir=opendir(path.c_str())) == NULL) { + LogError("Cannot open dir " << path); + return 0; + } + struct dirent* el; + while ((el = readdir(dir)) != 0) { + if (strcmp(el->d_name, ".") == 0 || strcmp(el->d_name, "..") == 0) { + continue; + } + struct stat tmp; + std::string local = path + el->d_name; + if (stat(local.c_str(), &tmp) == -1) { + LogError("Failed to open file " << local); + char* errstring = strerror(errno); + LogError("Reason: " << errstring); + continue; + } + + size += getObjectSize(local); + if (S_ISDIR(tmp.st_mode)) { + localDirs.push_back(local + "/"); + } + } + + closedir(dir); + + FOREACH (localDir, localDirs) { + size += getFolderSize(*localDir); + } + + return size; +} + + + + + +namespace { +#define DECLARE_PREFIX_STRUCT(name) \ +struct Prefix##name \ +{ \ + static std::string get() \ + { \ + return std::string(#name); \ + } \ +}; \ + +DECLARE_PREFIX_STRUCT(B) +DECLARE_PREFIX_STRUCT(KB) +DECLARE_PREFIX_STRUCT(MB) +DECLARE_PREFIX_STRUCT(GB) + +#undef DECLARE_PREFIX_STRUCT + + +const int stepSize = 1024; +template +struct Pre; + +template +struct Pre +{ + static const double value = Pre::value * stepSize; + static std::string printSize(double fileSize) + { + if(fileSize >= Pre::value) { + double now = fileSize / Pre::value; + std::ostringstream outputStream; + outputStream.setf(std::ios::fixed, std::ios::floatfield); + outputStream.precision(2); + outputStream << now << Postfix::get(); + return outputStream.str(); + } else { + return Pre::printSize(fileSize); + } + + } +}; + +template<> +struct Pre<> +{ + static const double value; + static std::string printSize(double /*fileSize*/) + { + return "0B"; + } + +}; +const double Pre<>::value = 1.0; + +typedef Pre FolderSizeToStringType; +} //anonymous namespace + + +DPL::String fromFileSizeString(size_t fileSize) +{ + + std::string output = + FolderSizeToStringType::printSize(static_cast(fileSize)); + return DPL::FromUTF8String(output); +} + +} // end of namespace Utils diff --git a/modules/utils/src/mime_type_utils.cpp b/modules/utils/src/mime_type_utils.cpp new file mode 100644 index 0000000..988c1a3 --- /dev/null +++ b/modules/utils/src/mime_type_utils.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include + +#include + +const std::set& MimeTypeUtils::getMimeTypesSupportedForIcon() +{ + static std::set set; + DPL::String (*s)(const std::string&) = DPL::FromASCIIString; + if (set.empty()) { + set.insert(s("image/gif")); + set.insert(s("image/png")); + set.insert(s("image/vnd.microsoft.icon")); + set.insert(s("image/svg+xml")); + set.insert(s("image/jpeg")); + } + return set; +} + +const std::set& MimeTypeUtils::getMimeTypesSupportedForStartFile() +{ + static std::set set; + DPL::String (*s)(const std::string&) = DPL::FromASCIIString; + if (set.empty()) { + set.insert(s("text/html")); + set.insert(s("application/xhtml+xml")); + } + return set; +} + +bool MimeTypeUtils::isMimeTypeSupportedForStartFile(const DPL::String& mimeType) +{ + return getMimeTypesSupportedForStartFile().count(stripMimeParameters( + mimeType)) > 0; +} + +const MimeTypeUtils::FileIdentificationMap& MimeTypeUtils:: + getFileIdentificationMap() +{ + static FileIdentificationMap map; + DPL::String (*s)(const std::string&) = DPL::FromASCIIString; + if (map.empty()) { + map[s(".html")] = s("text/html"); + map[s(".htm")] = s("text/html"); + map[s(".css")] = s("text/css"); + map[s(".js")] = s("application/javascript"); + map[s(".xml")] = s("application/xml"); + map[s(".txt")] = s("text/plain"); + map[s(".wav")] = s("audio/x-wav"); + map[s(".xhtml")] = s("application/xhtml+xml"); + map[s(".xht")] = s("application/xhtml+xml"); + map[s(".gif")] = s("image/gif"); + map[s(".png")] = s("image/png"); + map[s(".ico")] = s("image/vnd.microsoft.icon"); + map[s(".svg")] = s("image/svg+xml"); + map[s(".jpg")] = s("image/jpeg"); + } + return map; +} + +bool MimeTypeUtils::isMimeTypeSupportedForIcon(const DPL::String& mimeType) +{ + return getMimeTypesSupportedForIcon().count(stripMimeParameters(mimeType)) + > 0; +} + +DPL::String MimeTypeUtils::stripMimeParameters(const DPL::String& mimeType) +{ + size_t parametersStart = mimeType.find_first_of(L';'); + if (parametersStart != DPL::String::npos) { + return mimeType.substr(0, parametersStart); + } else { + return mimeType; + } +} + +MimeTypeUtils::MimeAttributes MimeTypeUtils::getMimeAttributes( + const DPL::String& mimeType) +{ + MimeAttributes attributes; + std::vector tokens; + DPL::Tokenize(mimeType, L";=", std::back_inserter(tokens)); + for (unsigned int i = 1; i < tokens.size(); i += 2) { + attributes[tokens[i]] = tokens[i + 1]; + } + return attributes; +} + +bool MimeTypeUtils::isValidIcon(const DPL::String& path) +{ + return getMimeTypesSupportedForIcon().count(identifyFileMimeType(path)) > 0; +} + +bool MimeTypeUtils::isValidStartFile(const DPL::String& path, + const DPL::OptionalString& providedMimeType) +{ + DPL::String mimeType = (!!providedMimeType) ? stripMimeParameters( + *providedMimeType) : identifyFileMimeType(path); + return getMimeTypesSupportedForStartFile().count(mimeType) > 0; +} + +DPL::String MimeTypeUtils::getFileNameFromPath(const DPL::String& path) +{ + size_t lastSlashPos = path.find_last_of(L'/'); + return path.substr(lastSlashPos + 1); +} + +DPL::String MimeTypeUtils::identifyFileMimeType(const DPL::String& path) +{ + DPL::String name = getFileNameFromPath(path); //step 4 + + if (name.size() == 0) { + ThrowMsg(Exception::InvalidFileName, "Path should contain a file name."); + } + + size_t lastFullStop = name.find_last_of(L'.'); + if (lastFullStop != 0 && lastFullStop != DPL::String::npos) { //step 5 + DPL::String extension = name.substr(lastFullStop); //step 6 & 7 + if (extension.size() > 0) { //step 8 + //step 9 + std::transform(extension.begin(), extension.end(), + extension.begin(), ::towlower); + FileIdentificationMap::const_iterator it = + getFileIdentificationMap().find(extension); + if (it != getFileIdentificationMap().end()) { + return it->second; + } + } + } + //TODO step 10 - sniff + return DPL::FromASCIIString("application/sniff"); +} diff --git a/modules/utils/src/warp_iri.cpp b/modules/utils/src/warp_iri.cpp new file mode 100644 index 0000000..a296ea5 --- /dev/null +++ b/modules/utils/src/warp_iri.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file have been implemented in compliance with W3C WARP SPEC. + * but there are some patent issue between W3C WARP SPEC and APPLE. + * so if you want to use this file, refer to the README file in root directory + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace { +// All schemes which are supported by external application should be ignored +// by WARP engine. +// +// Warp specification require from iri to have host element. File protocol +// does not contain host element so it's always denied by warp. +// Unfortunatly all widgets are using file protocol to load its data from +// hard drive. What's why we cannot check any iri with file schema. + +const char *IRI_IGNORED_SCHEME[] = { "file://", "widget://", "tel:", "sms:", + "mmsto:", "mailto:", "data:", "blob:", 0 }; + +const DPL::String SCHEMA_HTTP = DPL::FromUTF8String("http"); +const DPL::String SCHEMA_HTTPS = DPL::FromUTF8String("https"); +const DPL::String SCHEMA_FTP = DPL::FromUTF8String("ftp"); +} // namespace anonymous + +// This will create AutoPtr deleter for iri_struct. +// Deleter must be in the same namespace as definition of AutoPtr. +namespace ValidationCore { +VC_DECLARE_DELETER(iri_struct, iri_destroy) +} + +WarpIRI::WarpIRI() : + m_domain(false), + m_port(UNKNOWN_PORT), + m_isAccessDefinition(false), + m_isIRIValid(false) +{ +} + +void WarpIRI::set(const char *p_iri, + bool domain) +{ + if (!p_iri) { + m_isAccessDefinition = m_isIRIValid = false; + return; + } + + m_domain = domain; + m_isAccessDefinition = true; + m_isIRIValid = true; + m_host.clear(); + + if (strcmp(p_iri, "*") == 0) { + return; + } + + ValidationCore::AutoPtr iri(iri_parse(p_iri)); + + if (!iri.get()) { + LogError("Error in iri_parse!"); + m_isIRIValid = false; + m_isAccessDefinition = false; + return; + } + + if (iri->scheme == NULL || iri->host == NULL) { + m_isIRIValid = false; + m_isAccessDefinition = false; + return; + } + + // all of this must be NULL in WARP definition + if (iri->user || iri->path || iri->query || iri->anchor) { + m_isAccessDefinition = false; + } + + m_schema = DPL::FromASCIIString(std::string(iri->scheme)); + m_port = static_cast(iri->port); + + if (m_port == 0) { + m_port = getPort(m_schema); + if (m_port == UNKNOWN_PORT) { + m_isAccessDefinition = false; + return; + } + } + + DPL::String str = DPL::FromASCIIString(std::string(iri->host)); + + std::string utf8host = iri->host; + std::list hostTokenList; + DPL::Tokenize(utf8host, ".", std::front_inserter(hostTokenList)); + + if (SCHEMA_HTTP == m_schema || SCHEMA_HTTPS == m_schema) { + FOREACH(i, hostTokenList) { + char *output = NULL; + int rc = idna_to_ascii_8z(i->c_str(), + &output, + IDNA_USE_STD3_ASCII_RULES); + + if (IDNA_SUCCESS != rc) { + LogWarning("libidn error: " << rc << " " << + idna_strerror((Idna_rc)rc)); + m_isIRIValid = false; + m_isAccessDefinition = false; + } else { + std::string token(output); + std::transform(token.begin(), + token.end(), + token.begin(), + ::tolower); + m_host.push_back(DPL::FromUTF8String(token)); + } + free(output); + } + } else { + FOREACH(i, hostTokenList){ + m_host.push_back(DPL::FromUTF8String(*i)); + } + } +} + +void WarpIRI::set(const DPL::String &iristring, + bool domain) +{ + set(DPL::ToUTF8String(iristring).c_str(), domain); +} + +unsigned int WarpIRI::getPort(const DPL::String &schema) const +{ + unsigned int port = UNKNOWN_PORT; + if (schema == SCHEMA_HTTP) { + port = 80; + } else if (schema == SCHEMA_HTTPS) { + port = 443; + } else if (schema == SCHEMA_FTP) { + port = 21; + } + return port; +} + +bool WarpIRI::isSubDomain(const WarpIRI &second) const +{ + if (!m_isAccessDefinition || !second.m_isIRIValid) { return false; } + if (m_schema != second.m_schema) { return false; } + if (m_port != second.m_port) { return false; } + + size_t size = m_host.size() < second.m_host.size() ? + m_host.size() : second.m_host.size(); + + if (m_host.size() > second.m_host.size()) { + return false; + } + + if (!m_domain && (m_host.size() != second.m_host.size())) { + return false; + } + + for (size_t i = 0; i < size; ++i) { + if (DPL::StringCompare(m_host[i], second.m_host[i])) { + return false; + } + } + return true; +} + +bool WarpIRI::isAccessDefinition() const +{ + return m_isAccessDefinition; +} + +// KW bool WarpIRI::isIRIValid() const { +// KW return m_isIRIValid; +// KW } + +bool WarpIRI::getSubDomain() const +{ + return m_domain; +} + +bool WarpIRI::isIRISchemaIgnored(const char *iri) +{ + for (int i = 0; IRI_IGNORED_SCHEME[i]; ++i) { + if (0 == + strncmp(iri, IRI_IGNORED_SCHEME[i], + strlen(IRI_IGNORED_SCHEME[i]))) { + return true; + } + } + return false; +} diff --git a/modules/utils/src/widget_version.cpp b/modules/utils/src/widget_version.cpp new file mode 100644 index 0000000..0bf91c1 --- /dev/null +++ b/modules/utils/src/widget_version.cpp @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file widget_version.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief Implementation file for widget version + */ +#include +#include +#include +#include +#include + +namespace // anonymous +{ +size_t WAC_CERTIFY_MANDATORY_PART_LOW_COUNT = 2; +size_t WAC_CERTIFY_MANDATORY_PART_HIGH_COUNT = 3; +size_t WAC_CERTIFY_MANDATORY_PART_MAJOR_INDEX = 0; +size_t WAC_CERTIFY_MANDATORY_PART_MINOR_INDEX = 1; +size_t WAC_CERTIFY_MANDATORY_PART_MICRO_INDEX = 2; +DPL::String::value_type WAC_CERTIFY_MANDATORY_VS_OPTIONAL_SPLIT_CHAR = L' '; +DPL::String::value_type WAC_CERTIFY_MANDATORY_NUMBER_PART_SPLIT_CHAR = L'.'; +DPL::String::value_type LEADING_ZERO_CHAR = L'0'; + +// +// [ABNF] +// Augmented BNF for Syntax Specifications: ABNF. RFC5234. D. Crocker and P. Overell. January 2008. +// +// ALPHA = %x41-5A / %x61-7A +inline bool IsAlpha(int c) +{ + return (c >= 0x41 && c <= 0x5A) || + (c >= 0x61 && c <= 0x7A); +} + +// DIGIT = %x30-39 +inline bool IsDigit(int c) +{ + return c >= 0x30 && c <= 0x39; +} + +// SP = %x20 +inline bool IsSp(int c) +{ + return c == 0x20; +} + +DPL::String RemoveLeadingZeroes(const DPL::String &str) +{ + Assert(!str.empty()); + + if (str[0] != LEADING_ZERO_CHAR) { + return str; + } + + size_t pos = 0; + + while (pos + 1 < str.size() && str[pos + 1] == LEADING_ZERO_CHAR) { + ++pos; + } + + return str.substr(pos); +} + +// operator < +bool NumberLessOperator(const DPL::String &left, + const DPL::String &right) +{ + // Assume: No leading zeroes + if (left.size() < right.size()) { + return true; + } + + if (left.size() > right.size()) { + return false; + } + + // Now: left.size() == right.size() + for (ssize_t i = static_cast(left.size()) - 1; i >= 0; --i) { + if (left[i] < right[i]) { + return true; + } + + if (left[i] > right[i]) { + return false; + } + } + + // Equal + return false; +} + +bool WacCertifyNumberString(const DPL::String &str) +{ + for (DPL::String::const_iterator i = str.begin(); i != str.end(); ++i) { + if (!IsDigit(*i)) { + return false; + } + } + + return true; +} + +bool WacCertifyAlphaNumberStringSpace(const DPL::String &str) +{ + for (DPL::String::const_iterator i = str.begin(); i != str.end(); ++i) { + if (!IsDigit(*i) && !IsAlpha(*i) && !IsSp(*i)) { + return false; + } + } + + return true; +} +} // anonymous + +WidgetVersion::WidgetVersion(const DPL::String &str) : + m_isWac(false), + m_raw(str) +{ + LogDebug("Parsing version string: " << str); + + // Split optional an mandatory parts + size_t optionalPartPosition = str.find( + WAC_CERTIFY_MANDATORY_VS_OPTIONAL_SPLIT_CHAR); + + DPL::String mandatoryPart; + DPL::Optional optionalPart; + + if (optionalPartPosition == DPL::String::npos) { + mandatoryPart = str; + } else { + mandatoryPart = str.substr(0, optionalPartPosition); + optionalPart = str.substr(optionalPartPosition + 1, DPL::String::npos); + } + + LogDebug("Mandatory part is: " << mandatoryPart); + LogDebug("Optional part is: " << optionalPart); + + // Split string and construct version + std::vector parts; + DPL::Tokenize(mandatoryPart, + WAC_CERTIFY_MANDATORY_NUMBER_PART_SPLIT_CHAR, + std::back_inserter(parts), + false); + + LogDebug("Tokenized mandatory parts: " << parts.size()); + + if (parts.size() != WAC_CERTIFY_MANDATORY_PART_LOW_COUNT && + parts.size() != WAC_CERTIFY_MANDATORY_PART_HIGH_COUNT) { + return; + } + + DPL::String major; + DPL::String minor; + DPL::Optional micro; + + // Certify for Wac + major = parts[WAC_CERTIFY_MANDATORY_PART_MAJOR_INDEX]; + minor = parts[WAC_CERTIFY_MANDATORY_PART_MINOR_INDEX]; + + if (parts.size() == WAC_CERTIFY_MANDATORY_PART_HIGH_COUNT) { + micro = parts[WAC_CERTIFY_MANDATORY_PART_MICRO_INDEX]; + } + + WacCertify(major, minor, micro, optionalPart); +} + +WidgetVersion::WidgetVersion(const DPL::String &major, + const DPL::String &minor, + const DPL::Optional µ, + const DPL::Optional &optional) : + m_isWac(false) +{ + // Create Raw version + m_raw += major; + m_raw += WAC_CERTIFY_MANDATORY_NUMBER_PART_SPLIT_CHAR; + m_raw += minor; + m_raw += WAC_CERTIFY_MANDATORY_NUMBER_PART_SPLIT_CHAR; + + if (!!micro) { + m_raw += *micro; + } + + if (!!optional) { + m_raw += WAC_CERTIFY_MANDATORY_VS_OPTIONAL_SPLIT_CHAR; + m_raw += *optional; + } + + // Certify for Wac + WacCertify(major, minor, micro, optional); +} + +void WidgetVersion::WacCertify(const DPL::String &major, + const DPL::String &minor, + const DPL::Optional µ, + const DPL::Optional &optional) +{ + LogDebug("Certyfing..."); + + LogDebug("Major candidate: " << major); + LogDebug("Minor candidate: " << minor); + LogDebug("Micro candidate: " << micro); + LogDebug("Optional candidate: " << optional); + + // Check strings + if (major.empty() || !WacCertifyNumberString(major)) { + LogDebug("Major version not certified!"); + return; + } + + if (minor.empty() || !WacCertifyNumberString(minor)) { + LogDebug("Minor version not certified!"); + return; + } + + if (!!micro && (micro->empty() || !WacCertifyNumberString(*micro))) { + LogDebug("Microversion not certified!"); + return; + } + + if (!!optional && + (optional->empty() || !WacCertifyAlphaNumberStringSpace(*optional))) { + LogDebug("Optional version not certified!"); + return; + } + + // OK + m_major = major; + m_minor = minor; + m_micro = micro; + m_optional = optional; + + m_isWac = true; + + LogDebug("Certified."); +} + +bool WidgetVersion::IsWac() const +{ + return m_isWac; +} + +const DPL::String &WidgetVersion::Raw() const +{ + return m_raw; +} + +const DPL::String &WidgetVersion::Major() const +{ + return m_major; +} + +const DPL::String &WidgetVersion::Minor() const +{ + return m_minor; +} + +const DPL::Optional &WidgetVersion::Micro() const +{ + return m_micro; +} + +const DPL::Optional &WidgetVersion::Optional() const +{ + return m_optional; +} + +bool operator<(const WidgetVersion &left, + const WidgetVersion &right) +{ + Assert( + left.IsWac() && right.IsWac() && + "Only WAC version strings are comparable!"); + + if (NumberLessOperator(RemoveLeadingZeroes(left.Major()), + RemoveLeadingZeroes(right.Major()))) { return true; } + if (NumberLessOperator(RemoveLeadingZeroes(right.Major()), + RemoveLeadingZeroes(left.Major()))) { return false; } + + if (NumberLessOperator(RemoveLeadingZeroes(left.Minor()), + RemoveLeadingZeroes(right.Minor()))) { return true; } + if (NumberLessOperator(RemoveLeadingZeroes(right.Minor()), + RemoveLeadingZeroes(left.Minor()))) { return false; } + + if (!!left.Micro() && !!right.Micro() && + NumberLessOperator(RemoveLeadingZeroes(*left.Micro()), + RemoveLeadingZeroes(*right.Micro()))) { return true; } + if (!left.Micro() && !!right.Micro()) { return true; } + + return false; +} + +bool operator<=(const WidgetVersion &left, + const WidgetVersion &right) +{ + Assert( + left.IsWac() && right.IsWac() && + "Only WAC version strings are comparable!"); + + return (left == right) || (left < right); +} + +bool operator>(const WidgetVersion &left, + const WidgetVersion &right) +{ + Assert( + left.IsWac() && right.IsWac() && + "Only WAC version strings are comparable!"); + + return !(left <= right); +} + +bool operator>=(const WidgetVersion &left, + const WidgetVersion &right) +{ + Assert( + left.IsWac() && right.IsWac() && + "Only WAC version strings are comparable!"); + + return (left == right) || (left > right); +} + +bool operator==(const WidgetVersion &left, + const WidgetVersion &right) +{ + Assert( + left.IsWac() && right.IsWac() && + "Only WAC version strings are comparable!"); + + return RemoveLeadingZeroes(left.Major()) == + RemoveLeadingZeroes(right.Major()) && // Major are equal + RemoveLeadingZeroes(left.Minor()) == + RemoveLeadingZeroes(right.Minor()) && // and Minor are equal + ( // and ... + (!!left.Micro() && !!right.Micro() && + RemoveLeadingZeroes(*left.Micro()) == + RemoveLeadingZeroes(*right.Micro())) || // Both Micro exist and are equal + (!left.Micro() && !right.Micro()) // or both Micro do not exist + ); +} + +bool operator!=(const WidgetVersion &left, + const WidgetVersion &right) +{ + Assert( + left.IsWac() && right.IsWac() && + "Only WAC version strings are comparable!"); + + return !(left == right); +} + +std::ostream & operator<<(std::ostream& stream, + const WidgetVersion& version) +{ + stream << version.Raw(); + return stream; +} diff --git a/modules/utils/src/wrt_global_settings.cpp b/modules/utils/src/wrt_global_settings.cpp new file mode 100644 index 0000000..8695e8a --- /dev/null +++ b/modules/utils/src/wrt_global_settings.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file wrt_global_settings.cpp + * @version 1.0 + * @author Pawel Sikorski(p.sikorski@samsung.com) + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @brief runtime + */ +#include +#include +#include +#include +#include + +namespace { +bool GetPopupsEnabledFlag() +{ + //TODO : env var. will be removed after UX guide for POWDER is enabled. + const char *env = getenv("WRT_POPUP_ENABLE"); + if (env && 0 == strcmp(env, "1")) { + return true; + } else { + return false; + } +} + +static bool initializeGlobalSettings(); + +static bool initHelper = initializeGlobalSettings(); + +bool initializeGlobalSettings() +{ + (void)initHelper; + LogDebug("Initializing globall settings"); + GlobalSettings::IGlobalSettingsFunctions functions; + functions.getPopupsEnabledFlag = &GetPopupsEnabledFlag; + GlobalSettings::SetPredefinedGlobalSettings(functions); + return false; +} +} diff --git a/modules/utils/src/wrt_global_settings_internal.cpp b/modules/utils/src/wrt_global_settings_internal.cpp new file mode 100644 index 0000000..7940cd1 --- /dev/null +++ b/modules/utils/src/wrt_global_settings_internal.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file wrt_global_settings_internal.cpp + * @version 0.6 + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + */ + +#include +#include +#include +#include + +namespace GlobalSettings { +static IGlobalSettingsFunctions globalSettingsFunctions; + +// INTERNAL +void SetPredefinedGlobalSettings(IGlobalSettingsFunctions functions) +{ + globalSettingsFunctions = functions; + LogDebug("Global settings are set"); +} + +// PUBLIC +bool GetPopupsEnabledFlag() +{ + Assert(globalSettingsFunctions.getPopupsEnabledFlag && + "Global settings are unset"); + return globalSettingsFunctions.getPopupsEnabledFlag(); +} +} //namespace GlobalSettings diff --git a/modules/utils/src/wrt_utility.cpp b/modules/utils/src/wrt_utility.cpp new file mode 100644 index 0000000..f7ecd7d --- /dev/null +++ b/modules/utils/src/wrt_utility.cpp @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file wrt_utility.cpp + * @version 0.6 + * @author Wei Dong(d.wei@samsung.com) + * @author Ma Quan(jason.ma@samsung.com) + * @brief This file implemented some common functions for widget manager + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +bool _WrtUtilSetAbsolutePath(char* absolutePath, + const char* parentPath, + const char* fileName) +{ + int len; + if (NULL == absolutePath || NULL == parentPath || NULL == fileName) { + return false; + } + len = strlen(parentPath); + if (len > 0) { + // not check the valid of parentPath and fileName. + if (parentPath[len - 1] == '/') { + snprintf(absolutePath, + MAX_WIDGET_PATH_LENGTH, + "%s%s", + parentPath, + fileName); + } else { + snprintf(absolutePath, + MAX_WIDGET_PATH_LENGTH, + "%s/%s", + parentPath, + fileName); + } + } else { + LogDebug("The parent path is null"); + return false; + } + + //some widget use Windows notation. We need to change '\' to '/' + for (int i = 0; absolutePath[i] != 0; ++i) { + if (absolutePath[i] == '\\') { + absolutePath[i] = '/'; + } + } + + return true; +} + +// KW bool _WrtUtilConvertStrToBool(char* value, bool *result) +// KW { +// KW bool ret = false; +// KW if (NULL == value || NULL == result) +// KW { +// KW return ret; +// KW } +// KW +// KW char* source = value; +// KW char* changed = (char*)malloc(strlen(value) + 1); +// KW if (NULL == changed) +// KW { +// KW return ret; +// KW } +// KW memset(changed, 0, strlen(value) + 1); +// KW +// KW char* cur = changed; +// KW while(*source) +// KW { +// KW *cur++ = tolower(*source++); +// KW } +// KW if (!strcmp(changed,"false") || !strcmp(changed,"0")) +// KW { +// KW *result = false; +// KW ret = true; +// KW } +// KW else if(!strcmp(changed,"true") || !strcmp(changed,"1")) +// KW { +// KW *result = true; +// KW ret = true; +// KW } +// KW free(changed); +// KW +// KW return ret; +// KW } + +void _WrtUtilGetDirAndFileName(const char* fullPath, + char** dirName, + char** fileName) +{ + int length = 0; + int index = 0; + if (NULL == fullPath || (NULL == dirName && NULL == fileName)) { + return; + } + + length = strlen(fullPath); + for (index = length - 1; index >= 0; index--) { + if ('/' == fullPath[index]) { + if (index == length - 1) { + LogDebug(" Warning: The end of directroy is '/'! "); + if (dirName) { + *dirName = (char*)malloc(sizeof(char) * (length + 1)); + if (*dirName != NULL) { + memset(*dirName, 0, sizeof(char) * (length + 1)); + strncpy(*dirName, fullPath, length); + } + } + return; + } + break; + } + } + if (index >= 0) { + if (dirName) { + int dirName_len = index + 2; + + *dirName = (char*)malloc(sizeof(char) * dirName_len); + if (*dirName != NULL) { + memset(*dirName, 0, sizeof(char) * dirName_len); + strncpy(*dirName, fullPath, dirName_len - 1); + } + } + + if (fileName) { + int fileName_len = length - index; + + *fileName = (char*)malloc(sizeof(char) * fileName_len); + if (*fileName != NULL) { + memset(*fileName, 0, sizeof(char) * fileName_len); + strncpy(*fileName, &fullPath[index + 1], fileName_len - 1); + } + } + } else { + if (fileName) { + *fileName = (char*)malloc(sizeof(char) * (length + 1)); + if (*fileName != NULL) { + memset(*fileName, 0, sizeof(char) * (length + 1)); + strncpy(*fileName, fullPath, length); + } + } + } +} + +// KW bool _WrtUtilStringCmp(const char* srcStr, const char* destStr) +// KW { +// KW bool ret = false; +// KW char* strString = NULL; +// KW char* destString = NULL; +// KW +// KW if (NULL == srcStr || NULL == destStr ) +// KW { +// KW return ret; +// KW } +// KW +// KW _WrtUtilStringToLower(srcStr, &strString); +// KW _WrtUtilStringToLower(destStr,&destString); +// KW +// KW if(!strcmp(strString, destString)) +// KW { +// KW ret = true; +// KW } +// KW +// KW free(strString); +// KW free(destString); +// KW +// KW return ret; +// KW } + +// check it deeply later. +bool _WrtMakeDir (const char *path, + long mode, + int flags) +{ + if (NULL == path) { + return false; + } + + const int defaultMode = 0777; + if (!(flags & WRT_FILEUTILS_RECUR)) { + if (mkdir(path, defaultMode) < 0) { + LogDebug("Failed to make dir " << path); + return false; + } + if (mode != -1 && chmod(path, mode) < 0) { + LogDebug("Failed to chmod"); + remove(path); + return false; + } + } else { + struct stat st; + if (stat(path, &st) < 0 && errno == ENOENT) { + bool ret; + char *buf = NULL; + char *parent = NULL; + // mode_t mask; + + // mask = umask (0); + // umask (mask); + + buf = strdup(path); + parent = dirname(buf); + //ret = _WrtMakeDir(parent, (defaultMode & ~mask) | 0300, WRT_FILEUTILS_RECUR); + ret = _WrtMakeDir(parent, (defaultMode) | 0300, WRT_FILEUTILS_RECUR); + free(buf); + + if ((!ret) || (!_WrtMakeDir(path, mode, 0))) { + LogDebug("Failed to _WrtMakeDir"); + return false; + } + } + } + + return true; +} + +bool _WrtUtilChangeDir(const char* path) +{ + if (NULL == path) { + return false; + } + if (-1 == chdir(path)) { + if (ENOENT == errno) { + if (!_WrtMakeDir(path, 0664, WRT_FILEUTILS_RECUR)) { + return false; + } + if (-1 == chdir(path)) { + remove(path); + return false; + } + } else { + return false; + } + } + + return true; +} + +bool _WrtUtilRemoveDir(const char* path) +{ + DIR* dir = NULL; + struct dirent* ptr = NULL; + char childPath[MAX_WIDGET_PATH_LENGTH + 1] = { 0 }; + if (path == NULL) { + LogWarning("Path is null"); + return false; + } + dir = opendir(path); + if (NULL != dir) { + while ((ptr = readdir(dir)) != NULL) { + if ((!strcmp(ptr->d_name, ".")) || (!strcmp(ptr->d_name, ".."))) { + continue; + } + int len = strlen(path); + if (path[len - 1] == '/') { + snprintf(childPath, + MAX_WIDGET_PATH_LENGTH, + "%s%s", + path, + ptr->d_name); + } else { + snprintf(childPath, + MAX_WIDGET_PATH_LENGTH, + "%s/%s", + path, + ptr->d_name); + } + if (ptr->d_type == DT_DIR) { + if (!_WrtUtilRemoveDir(childPath)) { + closedir(dir); + return false; + } + } else { + if (unlink(childPath) != 0) { + closedir(dir); + LogWarning("Failed to remove file " << childPath); + return false; + } + } + } + closedir(dir); + } else if (errno == ENOTDIR) { + if (unlink(path) != 0) { + LogWarning("Failed to remove file " << path); + return false; + } + return true; + } else if (errno == ENOENT) { //not exist + LogWarning("Cannot remove non existent directory " << path); + return true; + } else { + LogWarning("Can't remove directory " << path); + return false; + } + if (rmdir(path) != 0) { + LogWarning("Removing directory failed :" << path << " errno: " << errno); + return false; + } + + return true; +} + +bool +_WrtUtilStringToLower(const char* str, + char** lowerStr) +{ + if (!str || !lowerStr) { + return true; + } + + char* cur = NULL; + int length = strlen(str); + + *lowerStr = (char*)malloc(length + 1); + + if (!(*lowerStr)) { + return false; + } + + memset(*lowerStr, 0, length + 1); + strncpy(*lowerStr, str, length); + + cur = *lowerStr; + + while (*str != '\0') { + *cur++ = tolower(*str++); + //cur++; + } + + return true; +} + diff --git a/modules/vcore/CMakeLists.txt b/modules/vcore/CMakeLists.txt new file mode 100644 index 0000000..8c492f4 --- /dev/null +++ b/modules/vcore/CMakeLists.txt @@ -0,0 +1,30 @@ +#DB vcore +ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_BINARY_DIR}/modules/vcore/src/database_checksum_vcore.h + COMMAND ${CMAKE_SOURCE_DIR}/modules/vcore/src/orm/gen_db_md5.sh + ARGS ${CMAKE_BINARY_DIR}/modules/vcore/src/database_checksum_vcore.h + ${CMAKE_SOURCE_DIR}/modules/vcore/src/orm/vcore_db + DEPENDS ${CMAKE_SOURCE_DIR}/modules/vcore/src/orm/vcore_db + ${CMAKE_SOURCE_DIR}/modules/vcore/src/orm/gen_db_md5.sh + COMMENT "Generating VCORE database checksum" + ) + +ADD_CUSTOM_COMMAND( OUTPUT .vcore.db + COMMAND rm -f ${CMAKE_CURRENT_BINARY_DIR}/.vcore.db + COMMAND gcc -Wall -include ${CMAKE_BINARY_DIR}/modules/vcore/src/database_checksum_vcore.h -I${PROJECT_SOURCE_DIR}/modules/db/include/ -I${PROJECT_SOURCE_DIR}/modules/vcore/src/orm -E ${PROJECT_SOURCE_DIR}/modules/vcore/src/orm/vcore_db_sql_generator.h | grep --invert-match "^#" > ${CMAKE_CURRENT_BINARY_DIR}/vcore_db.sql + COMMAND sqlite3 ${CMAKE_CURRENT_BINARY_DIR}/.vcore.db ".read ${CMAKE_CURRENT_BINARY_DIR}/vcore_db.sql" || rm -f ${CMAKE_CURRENT_BINARY_DIR}/.vcore.db + DEPENDS ${CMAKE_BINARY_DIR}/modules/vcore/src/database_checksum_vcore.h ${PROJECT_SOURCE_DIR}/modules/vcore/src/orm/vcore_db_sql_generator.h ${PROJECT_SOURCE_DIR}/modules/vcore/src/orm/vcore_db + ) + +ADD_CUSTOM_COMMAND( OUTPUT .vcore.db-journal + COMMAND touch + ARGS ${CMAKE_CURRENT_BINARY_DIR}/.vcore.db-journal + ) + +ADD_CUSTOM_TARGET(Sqlite3DbVCORE ALL DEPENDS .vcore.db .vcore.db-journal) + +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/vcore_db.sql + DESTINATION share/wrt-engine/ + ) + +ADD_SUBDIRECTORY(src) diff --git a/modules/vcore/src/CMakeLists.txt b/modules/vcore/src/CMakeLists.txt new file mode 100644 index 0000000..5d56cda --- /dev/null +++ b/modules/vcore/src/CMakeLists.txt @@ -0,0 +1,132 @@ +INCLUDE(FindPkgConfig) + +PKG_CHECK_MODULES(VCORE_DEPS + cert-svc + ecore + appcore-efl + libxml-2.0 + libsoup-2.4 + openssl + xmlsec1 + tapi + REQUIRED) + +SET(VCORE_DIR + ${PROJECT_SOURCE_DIR}/modules/vcore + ) + +SET(VCORE_SRC_DIR + ${VCORE_DIR}/src/vcore + ) + +SET(VCORE_SOURCES + ${VCORE_SRC_DIR}/Base64.cpp + ${VCORE_SRC_DIR}/CachedCRL.cpp + ${VCORE_SRC_DIR}/CachedOCSP.cpp + ${VCORE_SRC_DIR}/Certificate.cpp + ${VCORE_SRC_DIR}/CertificateCacheDAO.cpp + ${VCORE_SRC_DIR}/CertificateCollection.cpp + ${VCORE_SRC_DIR}/CertificateConfigReader.cpp + ${VCORE_SRC_DIR}/CertificateLoader.cpp + ${VCORE_SRC_DIR}/CertificateVerifier.cpp + ${VCORE_SRC_DIR}/Config.cpp + ${VCORE_SRC_DIR}/CRL.cpp + ${VCORE_SRC_DIR}/Database.cpp + ${VCORE_SRC_DIR}/DeveloperModeValidator.cpp + ${VCORE_SRC_DIR}/OCSP.cpp + ${VCORE_SRC_DIR}/OCSPCertMgrUtil.cpp + ${VCORE_SRC_DIR}/OCSPUtil.c + ${VCORE_SRC_DIR}/ReferenceValidator.cpp + ${VCORE_SRC_DIR}/RevocationCheckerBase.cpp + ${VCORE_SRC_DIR}/SaxReader.cpp + ${VCORE_SRC_DIR}/SignatureFinder.cpp + ${VCORE_SRC_DIR}/SignatureReader.cpp + ${VCORE_SRC_DIR}/SignatureValidator.cpp + ${VCORE_SRC_DIR}/SoupMessageSendBase.cpp + ${VCORE_SRC_DIR}/SoupMessageSendSync.cpp + ${VCORE_SRC_DIR}/SoupMessageSendAsync.cpp + ${VCORE_SRC_DIR}/VerificationStatus.cpp + ${VCORE_SRC_DIR}/ValidatorFactories.cpp + ${VCORE_SRC_DIR}/VCore.cpp + ${VCORE_SRC_DIR}/XmlsecAdapter.cpp + ) + +SET(VCORE_INCLUDES + ${PROJECT_SOURCE_DIR}/modules/core/include + ${PROJECT_SOURCE_DIR}/modules/log/include + ${PROJECT_SOURCE_DIR}/modules/db/include + ${VCORE_DEPS_INCLUDE_DIRS} + ${VCORE_SRC_DIR} + ${VCORE_DIR}/src + ${VCORE_DIR}/src/orm + ${VCORE_DIR}/src/legacy + ${CMAKE_BINARY_DIR}/modules/vcore/src + ) + +ADD_DEFINITIONS(${VCORE_DEPS_CFLAGS}) +ADD_DEFINITIONS(${VCORE_DEPS_CFLAGS_OTHER}) +ADD_DEFINITIONS("-DSEPARATED_SINGLETON_IMPLEMENTATION") + +INCLUDE_DIRECTORIES(${VCORE_INCLUDES}) + +ADD_LIBRARY(${TARGET_VCORE_LIB} SHARED ${VCORE_SOURCES}) +SET_TARGET_PROPERTIES(${TARGET_VCORE_LIB} PROPERTIES + SOVERSION ${VERSION}) + +ADD_DEPENDENCIES(${TARGET_VCORE_LIB} Sqlite3DbWTF) + +SET_TARGET_PROPERTIES(${TARGET_VCORE_LIB} PROPERTIES + COMPILE_FLAGS -fPIC) + +TARGET_LINK_LIBRARIES(${TARGET_VCORE_LIB} + ${VCORE_DEPS_LIBRARIES} + ${TARGET_DPL_EFL} + ${TARGET_DPL_DB_EFL} + ) + +INSTALL(TARGETS ${TARGET_VCORE_LIB} + DESTINATION lib + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ + ) + +INSTALL(FILES + ${VCORE_SRC_DIR}/Base64.h + ${VCORE_SRC_DIR}/CachedCRL.h + ${VCORE_SRC_DIR}/CachedOCSP.h + ${VCORE_SRC_DIR}/Certificate.h + ${VCORE_SRC_DIR}/CertificateCacheDAO.h + ${VCORE_SRC_DIR}/CertificateCollection.h + ${VCORE_SRC_DIR}/CertificateConfigReader.h + ${VCORE_SRC_DIR}/CertificateLoader.h + ${VCORE_SRC_DIR}/CertificateStorage.h + ${VCORE_SRC_DIR}/CertificateVerifier.h + ${VCORE_SRC_DIR}/CertStoreType.h + ${VCORE_SRC_DIR}/Config.h + ${VCORE_SRC_DIR}/CRL.h + ${VCORE_SRC_DIR}/Database.h + ${VCORE_SRC_DIR}/DeveloperModeValidator.h + ${VCORE_SRC_DIR}/IAbstractResponseCache.h + ${VCORE_SRC_DIR}/OCSP.h + ${VCORE_SRC_DIR}/OCSPCertMgrUtil.h + ${VCORE_SRC_DIR}/ParserSchema.h + ${VCORE_SRC_DIR}/ReferenceValidator.h + ${VCORE_SRC_DIR}/RevocationCheckerBase.h + ${VCORE_SRC_DIR}/SaxReader.h + ${VCORE_SRC_DIR}/scoped_gpointer.h + ${VCORE_SRC_DIR}/SignatureData.h + ${VCORE_SRC_DIR}/SignatureFinder.h + ${VCORE_SRC_DIR}/SignatureReader.h + ${VCORE_SRC_DIR}/SignatureValidator.h + ${VCORE_SRC_DIR}/SoupMessageSendBase.h + ${VCORE_SRC_DIR}/SoupMessageSendSync.h + ${VCORE_SRC_DIR}/SoupMessageSendAsync.h + ${VCORE_SRC_DIR}/SSLContainers.h + ${VCORE_SRC_DIR}/VerificationStatus.h + ${VCORE_SRC_DIR}/ValidatorCommon.h + ${VCORE_SRC_DIR}/ValidatorFactories.h + ${VCORE_SRC_DIR}/VCore.h + ${VCORE_SRC_DIR}/XmlsecAdapter.h + DESTINATION include/dpl-efl/dpl/vcore/vcore + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ + ) + diff --git a/modules/vcore/src/orm/DESCRIPTION b/modules/vcore/src/orm/DESCRIPTION new file mode 100644 index 0000000..7d25d0d --- /dev/null +++ b/modules/vcore/src/orm/DESCRIPTION @@ -0,0 +1 @@ +Scripts required to create vcoredatabase. diff --git a/modules/vcore/src/orm/gen_db_md5.sh b/modules/vcore/src/orm/gen_db_md5.sh new file mode 100755 index 0000000..a81d5f7 --- /dev/null +++ b/modules/vcore/src/orm/gen_db_md5.sh @@ -0,0 +1,5 @@ +#!/bin/sh +CHECKSUM=`cat ${2} ${3} 2>/dev/null | md5sum 2>/dev/null | cut -d\ -f1 2>/dev/null` +echo "#define DB_CHECKSUM DB_VERSION_${CHECKSUM}" > ${1} +echo "#define DB_CHECKSUM_STR \"DB_VERSION_${CHECKSUM}\"" >> ${1} + diff --git a/modules/vcore/src/orm/orm_generator_vcore.h b/modules/vcore/src/orm/orm_generator_vcore.h new file mode 100644 index 0000000..862bc80 --- /dev/null +++ b/modules/vcore/src/orm/orm_generator_vcore.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ORM_GENERATOR_VCORE_H +#define ORM_GENERATOR_VCORE_H + +#define ORM_GENERATOR_DATABASE_NAME vcore_db_definitions +#include +#undef ORM_GENERATOR_DATABASE_NAME + +#endif // ORM_GENERATOR_VCORE_H diff --git a/modules/vcore/src/orm/vcore_db b/modules/vcore/src/orm/vcore_db new file mode 100644 index 0000000..71080da --- /dev/null +++ b/modules/vcore/src/orm/vcore_db @@ -0,0 +1,20 @@ +SQL( + PRAGMA foreign_keys = ON; + BEGIN TRANSACTION; +) +CREATE_TABLE(OCSPResponseStorage) + COLUMN_NOT_NULL(cert_chain, TEXT, primary key) + COLUMN(end_entity_check, INT,) + COLUMN(ocsp_status, INT,) + COLUMN(next_update_time, BIGINT,) +CREATE_TABLE_END() + +CREATE_TABLE(CRLResponseStorage) + COLUMN_NOT_NULL(distribution_point,TEXT, primary key) + COLUMN_NOT_NULL(crl_body, TEXT,) + COLUMN(next_update_time, BIGINT,) +CREATE_TABLE_END() + +SQL( + COMMIT; +) diff --git a/modules/vcore/src/orm/vcore_db_definitions b/modules/vcore/src/orm/vcore_db_definitions new file mode 100644 index 0000000..61018c4 --- /dev/null +++ b/modules/vcore/src/orm/vcore_db_definitions @@ -0,0 +1,6 @@ +DATABASE_START(vcore) + +#include "vcore_db" +#include "version_db" + +DATABASE_END() diff --git a/modules/vcore/src/orm/vcore_db_sql_generator.h b/modules/vcore/src/orm/vcore_db_sql_generator.h new file mode 100644 index 0000000..76f0448 --- /dev/null +++ b/modules/vcore/src/orm/vcore_db_sql_generator.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//Do not include this file directly! It is used only for SQL code generation. + +#include + +#include "vcore_db_definitions" diff --git a/modules/vcore/src/orm/version_db b/modules/vcore/src/orm/version_db new file mode 100644 index 0000000..7e20d8d --- /dev/null +++ b/modules/vcore/src/orm/version_db @@ -0,0 +1,5 @@ +SQL( + BEGIN TRANSACTION; + CREATE TABLE DB_CHECKSUM (version INT); + COMMIT; +) diff --git a/modules/vcore/src/vcore/Base64.cpp b/modules/vcore/src/vcore/Base64.cpp new file mode 100644 index 0000000..b772178 --- /dev/null +++ b/modules/vcore/src/vcore/Base64.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "Base64.h" + +namespace ValidationCore { +Base64Encoder::Base64Encoder() : + m_b64(0), + m_bmem(0), + m_finalized(false) +{ +} + +void Base64Encoder::append(const std::string &data) +{ + if (m_finalized) { + LogWarning("Already finalized."); + ThrowMsg(Exception::AlreadyFinalized, "Already finalized"); + } + + if (!m_b64) { + reset(); + } + BIO_write(m_b64, data.c_str(), data.size()); +} + +void Base64Encoder::finalize() +{ + if (m_finalized) { + LogWarning("Already finalized."); + ThrowMsg(Exception::AlreadyFinalized, "Already finalized."); + } + m_finalized = true; + BIO_flush(m_b64); +} + +std::string Base64Encoder::get() +{ + if (!m_finalized) { + LogWarning("Not finalized"); + ThrowMsg(Exception::NotFinalized, "Not finalized"); + } + BUF_MEM *bptr = 0; + BIO_get_mem_ptr(m_b64, &bptr); + if (bptr == 0) { + LogError("Bio internal error"); + ThrowMsg(Exception::InternalError, "Bio internal error"); + } + + if (bptr->length > 0) { + return std::string(bptr->data, bptr->length - 1); + } + return std::string(); +} + +void Base64Encoder::reset() +{ + m_finalized = false; + BIO_free_all(m_b64); + m_b64 = BIO_new(BIO_f_base64()); + m_bmem = BIO_new(BIO_s_mem()); + if (!m_b64 || !m_bmem) { + LogError("Error during allocation memory in BIO"); + ThrowMsg(Exception::InternalError, + "Error during allocation memory in BIO"); + } + m_b64 = BIO_push(m_b64, m_bmem); +} + +Base64Encoder::~Base64Encoder() +{ + BIO_free_all(m_b64); +} + +Base64Decoder::Base64Decoder() : + m_finalized(false) +{ +} + +void Base64Decoder::append(const std::string &data) +{ + if (m_finalized) { + LogWarning("Already finalized."); + ThrowMsg(Exception::AlreadyFinalized, "Already finalized."); + } + m_input.append(data); +} + +static bool whiteCharacter(char a) +{ + if (a == '\n') { return true; } + return false; +} + +bool Base64Decoder::finalize() +{ + if (m_finalized) { + LogWarning("Already finalized."); + ThrowMsg(Exception::AlreadyFinalized, "Already finalized."); + } + + m_finalized = true; + + m_input.erase(std::remove_if(m_input.begin(), + m_input.end(), + whiteCharacter), + m_input.end()); + + for (size_t i = 0; i tmp(strdup(m_input.c_str())); + m_input.clear(); + + bmem = BIO_new_mem_buf(tmp.Get(), len); + + if (!bmem) { + BIO_free(b64); + LogError("Internal error in BIO"); + ThrowMsg(Exception::InternalError, "Internal error in BIO"); + } + + bmem = BIO_push(b64, bmem); + + if (!bmem) { + BIO_free(b64); + LogError("Internal error in BIO"); + ThrowMsg(Exception::InternalError, "Internal error in BIO"); + } + + int readlen = BIO_read(bmem, buffer.Get(), len); + m_output.clear(); + + bool status = true; + + if (readlen > 0) { + m_output.append(buffer.Get(), readlen); + } else { + status = false; + } + + BIO_free_all(bmem); + return status; +} + +std::string Base64Decoder::get() const +{ + if (!m_finalized) { + LogWarning("Not finalized."); + ThrowMsg(Exception::NotFinalized, "Not finalized"); + } + return m_output; +} + +void Base64Decoder::reset() +{ + m_finalized = false; + m_input.clear(); + m_output.clear(); +} +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/Base64.h b/modules/vcore/src/vcore/Base64.h new file mode 100644 index 0000000..520662e --- /dev/null +++ b/modules/vcore/src/vcore/Base64.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _BASE64_H_ +#define _BASE64_H_ + +#include +#include +#include + +struct bio_st; +typedef bio_st BIO; + +namespace ValidationCore { +class Base64Encoder : public DPL::Noncopyable +{ + public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, InternalError) + DECLARE_EXCEPTION_TYPE(Base, NotFinalized) + DECLARE_EXCEPTION_TYPE(Base, AlreadyFinalized) + }; + Base64Encoder(); + void append(const std::string &data); + void finalize(); + std::string get(); + void reset(); + ~Base64Encoder(); + + private: + BIO *m_b64; + BIO *m_bmem; + bool m_finalized; +}; + +class Base64Decoder : public DPL::Noncopyable +{ + public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, InternalError) + DECLARE_EXCEPTION_TYPE(Base, NotFinalized) + DECLARE_EXCEPTION_TYPE(Base, AlreadyFinalized) + }; + Base64Decoder(); + void append(const std::string &data); + + /* + * Function will return false when BIO_read fails + * (for example: when string was not in base64 format). + */ + bool finalize(); + std::string get() const; + void reset(); + ~Base64Decoder() + { + } + + private: + std::string m_input; + std::string m_output; + bool m_finalized; +}; +} // namespace ValidationCore + +#endif diff --git a/modules/vcore/src/vcore/CRL.cpp b/modules/vcore/src/vcore/CRL.cpp new file mode 100644 index 0000000..98643df --- /dev/null +++ b/modules/vcore/src/vcore/CRL.cpp @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @author Piotr Marcinkiewicz(p.marcinkiew@samsung.com) + * @version 0.1 + * @file CRL.h + * @brief Routines for certificate validation over CRL + */ + +#include "CRL.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "Base64.h" +#include "Certificate.h" +#include "SoupMessageSendSync.h" +#include "CertificateCacheDAO.h" + +namespace { +const char *CRL_LOOKUP_DIR_1 = "/usr/share/cert-svc/ca-certs/code-signing/wac"; +const char *CRL_LOOKUP_DIR_2 = "/opt/share/cert-svc/certs/code-signing/wac"; +} //anonymous namespace + +namespace ValidationCore { + +CRL::StringList CRL::getCrlUris(const CertificatePtr &argCert) +{ + StringList result = argCert->getCrlUris(); + + if (!result.empty()) { + return result; + } + LogInfo("No distribution points found. Getting from CA cert."); + X509_STORE_CTX *ctx = createContext(argCert); + X509_OBJECT obj; + + //Try to get distribution points from CA certificate + int retVal = X509_STORE_get_by_subject(ctx, X509_LU_X509, + X509_get_issuer_name(argCert-> + getX509()), + &obj); + X509_STORE_CTX_free(ctx); + if (0 >= retVal) { + LogError("No dedicated CA certificate available"); + return result; + } + CertificatePtr caCert(new Certificate(obj.data.x509)); + X509_OBJECT_free_contents(&obj); + return caCert->getCrlUris(); +} + +CRL::CRL() +{ + LogInfo("CRL storage initialization."); + m_store = X509_STORE_new(); + if (!m_store) { + LogError("Failed to create new store."); + ThrowMsg(CRLException::StorageError, + "Not possible to create new store."); + } + m_lookup = X509_STORE_add_lookup(m_store, X509_LOOKUP_hash_dir()); + if (!m_lookup) { + cleanup(); + LogError("Failed to add hash dir lookup"); + ThrowMsg(CRLException::StorageError, + "Not possible to add hash dir lookup."); + } + // Add hash dir pathname for CRL checks + bool retVal = X509_LOOKUP_add_dir(m_lookup, + CRL_LOOKUP_DIR_1, X509_FILETYPE_PEM) == 1; + retVal &= retVal && (X509_LOOKUP_add_dir(m_lookup, CRL_LOOKUP_DIR_1, + X509_FILETYPE_ASN1) == 1); + retVal &= retVal && (X509_LOOKUP_add_dir(m_lookup, CRL_LOOKUP_DIR_2, + X509_FILETYPE_PEM) == 1); + retVal &= retVal && (X509_LOOKUP_add_dir(m_lookup, CRL_LOOKUP_DIR_2, + X509_FILETYPE_ASN1) == 1); + if (!retVal) { + LogError("Failed to add lookup dir for PEM files."); + cleanup(); + ThrowMsg(CRLException::StorageError, + "Failed to add lookup dir for PEM files."); + } + LogInfo("CRL storage initialization complete."); +} + +CRL::~CRL() +{ + cleanup(); +} + +void CRL::cleanup() +{ + LogInfo("Free CRL storage"); + // STORE is responsible for LOOKUP release + // X509_LOOKUP_free(m_lookup); + X509_STORE_free(m_store); +} + +CRL::RevocationStatus CRL::checkCertificate(const CertificatePtr &argCert) +{ + RevocationStatus retStatus = {false, false}; + int retVal = 0; + StringList crlUris = getCrlUris(argCert); + FOREACH(it, crlUris) { + CRLDataPtr crl = getCRL(*it); + if (!crl) { + LogDebug("CRL not found for URI: " << *it); + continue; + } + X509_CRL *crlInternal = convertToInternal(crl); + + //Check date + if (X509_CRL_get_nextUpdate(crlInternal)) { + retVal = X509_cmp_current_time( + X509_CRL_get_nextUpdate(crlInternal)); + retStatus.isCRLValid = retVal > 0; + } else { + // If nextUpdate is not set assume it is actual. + retStatus.isCRLValid = true; + } + LogInfo("CRL valid: " << retStatus.isCRLValid); + X509_REVOKED rev; + rev.serialNumber = X509_get_serialNumber(argCert->getX509()); + // sk_X509_REVOKED_find returns index if serial number is found on list + retVal = sk_X509_REVOKED_find(crlInternal->crl->revoked, &rev); + X509_CRL_free(crlInternal); + retStatus.isRevoked = retVal != -1; + LogInfo("CRL revoked: " << retStatus.isRevoked); + + if (!retStatus.isRevoked && isOutOfDate(crl)) { + LogDebug("Certificate is not Revoked, but CRL is outOfDate."); + continue; + } + + return retStatus; + } + // If there is no CRL for any of URIs it means it's not possible to + // tell anything about revocation status but it's is not an error. + return retStatus; +} + +CRL::RevocationStatus CRL::checkCertificateChain(CertificateCollection + certChain) +{ + if (!certChain.sort()) { + LogError("Certificate list doesn't create chain."); + ThrowMsg(CRLException::InvalidParameter, + "Certificate list doesn't create chain."); + } + + RevocationStatus ret; + ret.isCRLValid = true; + ret.isRevoked = false; + const CertificateList &certList = certChain.getChain(); + FOREACH(it, certList) { + if (!(*it)->isRootCert()) { + LogInfo("Certificate common name: " << *((*it)->getCommonName())); + RevocationStatus certResult = checkCertificate(*it); + ret.isCRLValid &= certResult.isCRLValid; + ret.isRevoked |= certResult.isRevoked; + if (ret.isCRLValid && !ret.isRevoked) { + addToStorage(*it); + } + if (ret.isRevoked) { + return ret; + } + } + } + return ret; +} + +VerificationStatus CRL::checkEndEntity(CertificateCollection &chain) +{ + if (!chain.sort() && !chain.empty()) { + LogInfo("Could not find End Entity certificate. " + "Collection does not form chain."); + return VERIFICATION_STATUS_ERROR; + } + CertificateList::const_iterator iter = chain.begin(); + RevocationStatus stat = checkCertificate(*iter); + if (stat.isRevoked) { + return VERIFICATION_STATUS_REVOKED; + } + if (stat.isCRLValid) { + return VERIFICATION_STATUS_GOOD; + } + return VERIFICATION_STATUS_ERROR; +} + +void CRL::addToStorage(const CertificatePtr &argCert) +{ + X509_STORE_add_cert(m_store, argCert->getX509()); +} + +bool CRL::isOutOfDate(const CRLDataPtr &crl) const { + X509_CRL *crlInternal = convertToInternal(crl); + + bool result = false; + if (X509_CRL_get_nextUpdate(crlInternal)) { + if (0 > X509_cmp_current_time(X509_CRL_get_nextUpdate(crlInternal))) { + result = true; + } else { + result = false; + } + } else { + result = true; + } + X509_CRL_free(crlInternal); + return result; +} + +bool CRL::updateList(const CertificatePtr &argCert, + const UpdatePolicy updatePolicy) +{ + LogInfo("Update CRL for certificate"); + + // Retrieve distribution points + StringList crlUris = getCrlUris(argCert); + FOREACH(it, crlUris) { + // Try to get CRL from database + LogInfo("Getting CRL for URI: " << *it); + + bool downloaded = false; + + CRLDataPtr crl; + + // If updatePolicy == UPDATE_ON_DEMAND we dont care + // about data in cache. New crl must be downloaded. + if (updatePolicy == UPDATE_ON_EXPIRED) { + crl = getCRL(*it); + } + + if (!!crl && isOutOfDate(crl)) { + LogDebug("Crl out of date - downloading."); + crl = downloadCRL(*it); + downloaded = true; + } + + if (!crl) { + LogDebug("Crl not found in cache - downloading."); + crl = downloadCRL(*it); + downloaded = true; + } + + if (!crl) { + LogDebug("Failed to obtain CRL. URL: " << *it); + continue; + } + + if (!!crl && isOutOfDate(crl)) { + LogError("CRL out of date. Broken URL: " << *it); + } + + // Make X509 internal structure + X509_CRL *crlInternal = convertToInternal(crl); + + //Check if CRL is signed + if (!verifyCRL(crlInternal, argCert)) { + LogError("Failed to verify CRL. URI: " << crl->uri); + X509_CRL_free(crlInternal); + return false; + } + X509_CRL_free(crlInternal); + + if (downloaded) { + updateCRL(crl); + } + return true; + } + + return false; +} + +void CRL::addToStore(const CertificateCollection &collection) +{ + FOREACH(it, collection){ + addToStorage(*it); + } +} + +bool CRL::updateList(const CertificateCollection &collection, + UpdatePolicy updatePolicy) +{ + bool failed = false; + + FOREACH(it, collection){ + failed |= !updateList(*it, updatePolicy); + } + + return !failed; +} + +bool CRL::verifyCRL(X509_CRL *crl, + const CertificatePtr &cert) +{ + X509_OBJECT obj; + X509_STORE_CTX *ctx = createContext(cert); + + /* get issuer certificate */ + int retVal = X509_STORE_get_by_subject(ctx, X509_LU_X509, + X509_CRL_get_issuer(crl), &obj); + X509_STORE_CTX_free(ctx); + if (0 >= retVal) { + LogError("Unknown CRL issuer certificate!"); + return false; + } + + /* extract public key and verify signature */ + EVP_PKEY *pkey = X509_get_pubkey(obj.data.x509); + X509_OBJECT_free_contents(&obj); + if (!pkey) { + LogError("Failed to get issuer's public key."); + return false; + } + retVal = X509_CRL_verify(crl, pkey); + EVP_PKEY_free(pkey); + if (0 > retVal) { + LogError("Failed to verify CRL."); + return false; + } else if (0 == retVal) { + LogError("CRL is invalid"); + return false; + } + LogInfo("CRL is valid."); + return true; +} + +bool CRL::isPEMFormat(const CRLDataPtr &crl) const +{ + const char *pattern = "-----BEGIN X509 CRL-----"; + std::string content(crl->buffer, crl->length); + if (content.find(pattern) != std::string::npos) { + LogInfo("CRL is in PEM format."); + return true; + } + LogInfo("CRL is in DER format."); + return false; +} + +X509_CRL *CRL::convertToInternal(const CRLDataPtr &crl) const +{ + //At this point it's not clear does crl have DER or PEM format + X509_CRL *ret = NULL; + if (isPEMFormat(crl)) { + BIO *bmem = BIO_new_mem_buf(crl->buffer, crl->length); + if (!bmem) { + LogError("Failed to allocate memory in BIO"); + ThrowMsg(CRLException::InternalError, + "Failed to allocate memory in BIO"); + } + ret = PEM_read_bio_X509_CRL(bmem, NULL, NULL, NULL); + BIO_free_all(bmem); + } else { + //If it's not PEM it must be DER format + std::string content(crl->buffer, crl->length); + const unsigned char *buffer = + reinterpret_cast(crl->buffer); + ret = d2i_X509_CRL(NULL, &buffer, crl->length); + } + if (!ret) { + LogError("Failed to convert to internal structure"); + ThrowMsg(CRLException::InternalError, + "Failed to convert to internal structure"); + } + return ret; +} + +X509_STORE_CTX *CRL::createContext(const CertificatePtr &argCert) +{ + X509_STORE_CTX *ctx; + ctx = X509_STORE_CTX_new(); + if (!ctx) { + ThrowMsg(CRLException::StorageError, "Failed to create new context."); + } + X509_STORE_CTX_init(ctx, m_store, argCert->getX509(), NULL); + return ctx; +} + +CRL::CRLDataPtr CRL::downloadCRL(const std::string &uri) +{ + using namespace SoupWrapper; + + char *cport = 0, *chost = 0,*cpath = 0; + int use_ssl = 0; + + if (!OCSP_parse_url(const_cast(uri.c_str()), + &chost, + &cport, + &cpath, + &use_ssl)) + { + LogWarning("Error in OCSP_parse_url"); + return CRLDataPtr(); + } + + std::string host = chost; + if (cport) { + host += ":"; + host += cport; + } + + free(cport); + free(chost); + free(cpath); + + SoupMessageSendSync message; + message.setHost(uri); + message.setHeader("Host", host); + + if (SoupMessageSendSync::REQUEST_STATUS_OK != message.sendSync()) { + LogWarning("Error in sending network request."); + return CRLDataPtr(); + } + + SoupMessageSendBase::MessageBuffer mBuffer = message.getResponse(); + return CRLDataPtr(new CRLData(mBuffer,uri)); +} + +CRL::CRLDataPtr CRL::getCRL(const std::string &uri) const +{ + CRLCachedData cachedCrl; + cachedCrl.distribution_point = uri; + if (!CertificateCacheDAO::getCRLResponse(&cachedCrl)) { + LogInfo("CRL not present in database. URI: " << uri); + return CRLDataPtr(); + } + + std::string body = cachedCrl.crl_body; + + LogInfo("CRL found in database."); + //TODO: remove when ORM::blob available + //Encode buffer to base64 format to store in database + + Base64Decoder decoder; + decoder.append(body); + if (!decoder.finalize()) { + LogError("Failed to decode base64 format."); + ThrowMsg(CRLException::StorageError, "Failed to decode base64 format."); + } + std::string crlBody = decoder.get(); + + DPL::ScopedArray bodyBuffer(new char[crlBody.length()]); + crlBody.copy(bodyBuffer.Get(), crlBody.length()); + return CRLDataPtr(new CRLData(bodyBuffer.Release(), crlBody.length(), + uri)); +} + +void CRL::updateCRL(const CRLDataPtr &crl) +{ + //TODO: remove when ORM::blob available + //Encode buffer to base64 format to store in database + Base64Encoder encoder; + if (!crl || !crl->buffer) { + ThrowMsg(CRLException::InternalError, "CRL buffer is empty"); + } + encoder.append(std::string(crl->buffer, crl->length)); + encoder.finalize(); + std::string b64CRLBody = encoder.get(); + + time_t nextUpdateTime = 0; + X509_CRL *crlInternal = convertToInternal(crl); + + if (X509_CRL_get_nextUpdate(crlInternal)) { + asn1TimeToTimeT(X509_CRL_get_nextUpdate(crlInternal), + &nextUpdateTime); + } + + X509_CRL_free(crlInternal); + //Update/insert crl body + CertificateCacheDAO::setCRLResponse(crl->uri,b64CRLBody,nextUpdateTime); +} +} // ValidationCore diff --git a/modules/vcore/src/vcore/CRL.h b/modules/vcore/src/vcore/CRL.h new file mode 100644 index 0000000..9ba64bb --- /dev/null +++ b/modules/vcore/src/vcore/CRL.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @author Piotr Marcinkiewicz(p.marcinkiew@samsung.com) + * @version 0.4 + * @file CRL.h + * @brief Routines for certificate validation over CRL + */ + +#ifndef WRT_ENGINE_SRC_VALIDATION_CORE_ENGINE_CRL_H_ +#define WRT_ENGINE_SRC_VALIDATION_CORE_ENGINE_CRL_H_ + +#include +#include +#include +#include + +#include "Certificate.h" +#include "CertificateCollection.h" +#include "SoupMessageSendBase.h" +#include "VerificationStatus.h" + +namespace ValidationCore { +namespace CRLException { +DECLARE_EXCEPTION_TYPE(DPL::Exception, CRLException) +DECLARE_EXCEPTION_TYPE(CRLException, StorageError) +DECLARE_EXCEPTION_TYPE(CRLException, DownloadFailed) +DECLARE_EXCEPTION_TYPE(CRLException, InternalError) +DECLARE_EXCEPTION_TYPE(CRLException, InvalidParameter) +} + +class CachedCRL; // forward declaration + +class CRL +{ + protected: + X509_STORE *m_store; + X509_LOOKUP *m_lookup; + + class CRLData : DPL::Noncopyable + { + public: + //TODO: change to SharedArray when available + char *buffer; + size_t length; + std::string uri; + + CRLData(char* _buffer, + size_t _length, + const std::string &_uri) : + buffer(_buffer), + length(_length), + uri(_uri) + { + } + + CRLData(const SoupWrapper::SoupMessageSendBase::MessageBuffer &mBuff, + const std::string &mUri) + : uri(mUri) + { + buffer = new char[mBuff.size()]; + length = mBuff.size(); + memcpy(buffer, &mBuff[0], mBuff.size()); + } + + ~CRLData() + { + LogInfo("Delete buffer"); + delete[] buffer; + } + }; + typedef DPL::SharedPtr CRLDataPtr; + typedef std::list StringList; + + CRLDataPtr getCRL(const std::string &uri) const; + CRLDataPtr downloadCRL(const std::string &uri); + X509_STORE_CTX *createContext(const CertificatePtr &argCert); + void updateCRL(const CRLDataPtr &crl); + X509_CRL *convertToInternal(const CRLDataPtr &crl) const; + StringList getCrlUris(const CertificatePtr &argCert); + bool isPEMFormat(const CRLDataPtr &crl) const; + bool verifyCRL(X509_CRL *crl, + const CertificatePtr &cert); + void addToStorage(const CertificatePtr &argCert); + void cleanup(); + bool isOutOfDate(const CRLDataPtr &crl) const; + + friend class CachedCRL; + public: + enum UpdatePolicy + { + UPDATE_ON_EXPIRED, /**< Download and update CRL only when next update + date has expired */ + UPDATE_ON_DEMAND /**< Download and update CRL regardless next update + date */ + }; + + struct RevocationStatus + { + bool isCRLValid; /**< True when CRL was valid during + certificate validation */ + bool isRevoked; /**< True when certificate is revoked */ + }; + + CRL(); + ~CRL(); + + /** + * @brief Checks if given certificate is revoked. + * + * @details This function doesn't update CRL list. If related CRL + * is out of date the #isCRLValid return parameter is set to false. + * + * @param[in] argCert The certificate to check against revocation. + * @return RevocationStatus.isRevoked True when certificate is revoked, + * false otherwise. + * RevocationStatus.isCRLValid True if related CRL has not expired, + * false otherwise. + */ + RevocationStatus checkCertificate(const CertificatePtr &argCert); + + /** + * @brief Checks if any certificate from certificate chain is revoked. + * + * @details This function doesn't update CRL lists. If any of related + * CRL is out of date the #isCRLValid parameter is set to true. + * This function adds valid certificates from the chain to internal storage + * map so they'll be available in further check operations for current + * CRL object. + * + * @param[in] argCert The certificate chain to check against revocation. + * @return RevocationStatus.isRevoked True when any from certificate chain + * is revoked, false otherwise. + * RevocationStatus.isCRLValid True if all of related CRLs has + * not expired, false otherwise. + */ + RevocationStatus checkCertificateChain(CertificateCollection certChain); + + VerificationStatus checkEndEntity(CertificateCollection &chain); + + /** + * @brief Updates CRL related with given certificate. + * + * @details This function updates CRL list related with given certificate. + * If CRL related with given certificate is not stored in database + * then this function will download CRL and store it in database. + * + * @param[in] argCert The certificate for which the CRL will be updated + * @param[in] updatePolicy Determine when CRL will be downloaded and updated + * @return True when CRL for given certificate was updated successfully, + * false otherwise. + */ + bool updateList(const CertificatePtr &argCert, + const UpdatePolicy updatePolicy); + + /** + * @brief Updates CRL related with given certificates. + * + * @details This function updates CRL lists related with given certificates. + * If CRL related with given certificate is not stored in database + * then this function will download CRL and store it in database. + * + * @param[in] collection The certificate collection for which the CRL will + * be updated + * @param[in] updatePolicy Determine when CRL will be downloaded and updated + * @return True when CRL for given certificate was updated successfully, + * false otherwise. + */ + bool updateList(const CertificateCollection &collection, + const UpdatePolicy updatePolisy); + + /** + * @brief Add certificate to known certificates store. + * + * @param[in] collection The certificate collection which will be + * added to known certificate store. + */ + void addToStore(const CertificateCollection &collection); +}; +} // ValidationCore + +#endif //ifndef WRT_ENGINE_SRC_VALIDATION_CORE_ENGINE_CRL_H_ diff --git a/modules/vcore/src/vcore/CachedCRL.cpp b/modules/vcore/src/vcore/CachedCRL.cpp new file mode 100644 index 0000000..08448bf --- /dev/null +++ b/modules/vcore/src/vcore/CachedCRL.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * @file CachedCRL.cpp + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 0.1 + * @brief Cached CRL class implementation + */ + +#include +#include + +#include +#include +#include + +#include "CRL.h" +#include "CachedCRL.h" +#include "Certificate.h" +#include "CertificateCacheDAO.h" + +namespace ValidationCore { + +const time_t CachedCRL::CRL_minTimeValid = 3600; // one hour in seconds + +const time_t CachedCRL::CRL_maxTimeValid = 3600 * 24 * 7; // one week in seconds + +const time_t CachedCRL::CRL_refreshBefore = 3600; // one hour in seconds + +VerificationStatus CachedCRL::check(const CertificateCollection &certs) +{ + CRL crl; + bool allValid = true; + // we dont check CRL validity since + // we may use crl for longer time + // in smart cache than in regular CRL class (time clamping) + crl.addToStore(certs); + FOREACH(cert, certs){ + CRL::StringList crlUris = crl.getCrlUris(*cert); + FOREACH(uri, crlUris) { + allValid = allValid && updateCRLForUri(*uri,false); + } + } + if (!allValid) { + // problems with CRL validity + LogDebug("Some CRLs not valid"); + } + CRL::RevocationStatus stat = crl.checkCertificateChain(certs); + if (stat.isRevoked) { + LogDebug("Status REVOKED"); + return VERIFICATION_STATUS_REVOKED; + } + LogDebug("Status GOOD"); + return VERIFICATION_STATUS_GOOD; +} + +VerificationStatus CachedCRL::checkEndEntity(CertificateCollection &certs) +{ + if (certs.empty()) { + LogError("Collection empty. This should never happen."); + LogDebug("Status ERROR"); + return VERIFICATION_STATUS_ERROR; + } + if (!certs.sort()) { + LogError("Could not find End Entity certificate. " + "Collection does not form chain."); + LogDebug("Status ERROR"); + return VERIFICATION_STATUS_ERROR; + } + CRL crl; + bool allValid = true; + // we dont check CRL validity since + // we may use crl for longer time + // in smart cache than in regular CRL class (time clamping) + crl.addToStore(certs); + CertificateList::const_iterator icert = certs.begin(); + if (icert != certs.end()) { + CRL::StringList crlUris = crl.getCrlUris(*icert); + FOREACH(uri, crlUris) { + allValid = allValid && updateCRLForUri(*uri,false); + } + } + if (!allValid) { + // problems with CRL validity + LogDebug("Some CRLs not valid"); + } + CertificateList::const_iterator iter = certs.begin(); + CRL::RevocationStatus stat = crl.checkCertificate(*iter); + if (stat.isRevoked) { + LogDebug("Status REVOKED"); + return VERIFICATION_STATUS_REVOKED; + } + LogDebug("Status GOOD"); + return VERIFICATION_STATUS_GOOD; +} + +void CachedCRL::updateCache() +{ + CRLCachedDataList list; + CertificateCacheDAO::getCRLResponseList(&list); + FOREACH(db_crl, list) { + updateCRLForUri(db_crl->distribution_point, true); + } +} + +bool CachedCRL::updateCRLForUri(const std::string & uri, bool useExpiredShift) +{ + CRLCachedData cachedCRL; + cachedCRL.distribution_point = uri; + time_t now; + time(&now); + if (useExpiredShift) { + now += CRL_refreshBefore; + } + if (CertificateCacheDAO::getCRLResponse(&cachedCRL)) { + if (now < cachedCRL.next_update_time) { + LogDebug("Cached CRL still valid for: " << uri); + return true; + } + } + // need to download new CRL + CRL crl; + CRL::CRLDataPtr list = crl.downloadCRL(uri); + if (!list) { + LogWarning("Could not retreive CRL from " << uri); + return false; + } + crl.updateCRL(list); + CertificateCacheDAO::getCRLResponse(&cachedCRL); // save it the way CRL does + cachedCRL.next_update_time = + getNextUpdateTime(now,cachedCRL.next_update_time); + CertificateCacheDAO::setCRLResponse(cachedCRL.distribution_point, + cachedCRL.crl_body, + cachedCRL.next_update_time); + return true; +} + +time_t CachedCRL::getNextUpdateTime(time_t now, time_t response_validity) +{ + time_t min = now + CRL_minTimeValid; + time_t max = now + CRL_maxTimeValid; + if (response_validity < min) { + return min; + } + if (response_validity > max) { + return max; + } + return response_validity; +} + +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/CachedCRL.h b/modules/vcore/src/vcore/CachedCRL.h new file mode 100644 index 0000000..65e6509 --- /dev/null +++ b/modules/vcore/src/vcore/CachedCRL.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * @file CachedCRL.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 0.1 + * @brief Header file for smart cached CRL class + */ + +#ifndef _SRC_VALIDATION_CORE_CACHED_CRL_ +#define _SRC_VALIDATION_CORE_CACHED_CRL_ + +#include "CRL.h" +#include "IAbstractResponseCache.h" + +namespace ValidationCore { + +class CachedCRL : public IAbstractResponseCache { + public: + // cache can't be refreshed more frequently than CRL_minTimeValid + static const time_t CRL_minTimeValid; + + // to be even more secure, cache will be refreshed for certificate at least + // after CRL_maxTimeValid from last response + static const time_t CRL_maxTimeValid; + + // upon cache refresh, responses that will be invalid in CRL_refreshBefore + // seconds will be refreshed + static const time_t CRL_refreshBefore; + + VerificationStatus check(const CertificateCollection &certs); + VerificationStatus checkEndEntity(CertificateCollection &certs); + void updateCache(); + + CachedCRL() + { + } + virtual ~CachedCRL() + { + } + + private: + + // updates CRL cache for distributor URI + // useExpiredShift ==true should be used in cron/global cache update + // since it updates all CRLs that will be out of date in next + // CRL_refreshBefore seconds + bool updateCRLForUri(const std::string & uri, + bool useExpiredShift); + time_t getNextUpdateTime(time_t now, time_t response_validity); +}; + +} // namespace ValidationCore + +#endif /* _SRC_VALIDATION_CORE_CACHED_CRL_ */ diff --git a/modules/vcore/src/vcore/CachedOCSP.cpp b/modules/vcore/src/vcore/CachedOCSP.cpp new file mode 100644 index 0000000..21a8122 --- /dev/null +++ b/modules/vcore/src/vcore/CachedOCSP.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * @file CachedOCSP.cpp + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 0.1 + * @brief Cached OCSP class implementation + */ + +#include +#include + +#include +#include +#include + +#include "OCSP.h" +#include "CachedOCSP.h" +#include "Certificate.h" +#include "CertificateCacheDAO.h" + +namespace ValidationCore { + +const time_t CachedOCSP::OCSP_minTimeValid = 3600; // one hour in seconds + +const time_t CachedOCSP::OCSP_maxTimeValid = + 3600 * 24 * 7; // one week in seconds + +const time_t CachedOCSP::OCSP_refreshBefore = 3600; // one hour in seconds + +VerificationStatus CachedOCSP::check(const CertificateCollection &certs) +{ + OCSPCachedStatus db_status; + time_t now; + time(&now); + + db_status.cert_chain = certs.toBase64String(); + db_status.end_entity_check = false; + + if (CertificateCacheDAO::getOCSPStatus(&db_status)) { + LogDebug("Found cache entry for OCSP"); + if (now < db_status.next_update_time) { + LogDebug("Cache response valid"); + return db_status.ocsp_status; + } + } + + // here we need to get OCSP result and add/update cache + OCSP ocsp; + CertificateList list = certs.getChain(); + ocsp.setTrustedStore(list); + + VerificationStatusSet statusSet = ocsp.validateCertificateList(list); + db_status.ocsp_status = statusSet.convertToStatus(); + db_status.next_update_time = ocsp.getResponseValidity(); + CertificateCacheDAO::setOCSPStatus(db_status.cert_chain, + db_status.ocsp_status, + db_status.end_entity_check, + getNextUpdateTime( + now, + db_status.next_update_time)); + return db_status.ocsp_status; +} + +VerificationStatus CachedOCSP::checkEndEntity(CertificateCollection &certs) +{ + OCSPCachedStatus db_status; + time_t now; + time(&now); + + db_status.cert_chain = certs.toBase64String(); + db_status.end_entity_check = true; + + if (CertificateCacheDAO::getOCSPStatus(&db_status)) { + LogDebug("Found cache entry for OCSP"); + if (now < db_status.next_update_time) { + LogDebug("Cache response valid"); + return db_status.ocsp_status; + } + } + + // here we need to send request via OCSP and add/update cache + CertificateList clst; + getCertsForEndEntity(certs, &clst); + + OCSP ocsp; + ocsp.setTrustedStore(certs.getCertificateList()); + + const char *defResponderURI = getenv(OCSP::DEFAULT_RESPONDER_URI_ENV); + + if (defResponderURI) { + ocsp.setUseDefaultResponder(true); + ocsp.setDefaultResponder(defResponderURI); + } + + VerificationStatusSet statusSet = ocsp.validateCertificateList(clst); + db_status.ocsp_status = statusSet.convertToStatus(); + db_status.next_update_time = ocsp.getResponseValidity(); + + CertificateCacheDAO::setOCSPStatus(db_status.cert_chain, + db_status.ocsp_status, + db_status.end_entity_check, + getNextUpdateTime( + now, + db_status.next_update_time)); + + return db_status.ocsp_status; +} + +void CachedOCSP::updateCache() +{ + time_t now; + time(&now); + now += OCSP_refreshBefore; + OCSPCachedStatusList list; + CertificateCacheDAO::getOCSPStatusList(&list); + FOREACH(db_status, list) { + if (now >= db_status->next_update_time) { + // this response needs to be refreshed + // TODO: check result? update even errors? need to check RFC + CertificateCollection col; + col.load(db_status->cert_chain); + + OCSP ocsp; + CertificateList chain = col.getChain(); + ocsp.setTrustedStore(chain); + + VerificationStatusSet statusSet; + + if (db_status->end_entity_check) { + CertificateList clst; + getCertsForEndEntity(col, &clst); + statusSet = ocsp.validateCertificateList(clst); + } else { + statusSet = ocsp.validateCertificateList(chain); + } + + db_status->ocsp_status = statusSet.convertToStatus(); + db_status->next_update_time = ocsp.getResponseValidity(); + + CertificateCacheDAO::setOCSPStatus(db_status->cert_chain, + db_status->ocsp_status, + db_status->end_entity_check, + db_status->next_update_time); + } + } +} + +void CachedOCSP::getCertsForEndEntity( + const CertificateCollection &certs, CertificateList* clst) +{ + if (NULL == clst) { + LogError("NULL pointer"); + return; + } + + if (certs.isChain() && certs.size() >= 2) { + CertificateList::const_iterator icert = certs.begin(); + clst->push_back(*icert); + ++icert; + clst->push_back(*icert); + } +} + +time_t CachedOCSP::getNextUpdateTime(time_t now, time_t response_validity) +{ + long min = now + OCSP_minTimeValid; + long max = now + OCSP_maxTimeValid; + if (response_validity < min) { + return min; + } + if (response_validity > max) { + return max; + } + return response_validity; +} + +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/CachedOCSP.h b/modules/vcore/src/vcore/CachedOCSP.h new file mode 100644 index 0000000..517e49f --- /dev/null +++ b/modules/vcore/src/vcore/CachedOCSP.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * @file CachedOCSP.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 0.1 + * @brief Header file for smart cached OCSP class + */ + +#ifndef _SRC_VALIDATION_CORE_CACHED_OCSP_ +#define _SRC_VALIDATION_CORE_CACHED_OCSP_ + +#include "OCSP.h" +#include "IAbstractResponseCache.h" + +namespace ValidationCore { + +class CachedOCSP : public IAbstractResponseCache { + public: + // cache can't be refreshed more frequently than OCSP_minTimeValid + static const time_t OCSP_minTimeValid; + + // to be even more secure, cache will be refreshed for certificate at least + // after OCSP_minTimeValid from last response + static const time_t OCSP_maxTimeValid; + + // upon cache refresh, responses that will be invalid in OCSP_refreshBefore + // seconds will be refreshed + static const time_t OCSP_refreshBefore; + + VerificationStatus check(const CertificateCollection &certs); + VerificationStatus checkEndEntity(CertificateCollection &certs); + void updateCache(); + + CachedOCSP() + { + } + virtual ~CachedOCSP() + { + } + + private: + + void getCertsForEndEntity(const CertificateCollection &certs, + CertificateList* clst); + time_t getNextUpdateTime(time_t now, time_t response_validity); +}; + +} // namespace ValidationCore + +#endif /* _SRC_VALIDATION_CORE_CACHED_OCSP_ */ diff --git a/modules/vcore/src/vcore/CertStoreType.h b/modules/vcore/src/vcore/CertStoreType.h new file mode 100644 index 0000000..7cf6232 --- /dev/null +++ b/modules/vcore/src/vcore/CertStoreType.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#ifndef _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_CERTSTORETYPE_H_ +#define _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_CERTSTORETYPE_H_ + +namespace ValidationCore { +namespace CertStoreId { +typedef unsigned int Type; + +// RootCA certificates for developer mode. +const Type DEVELOPER = 1; +// RootCA certificates for author signatures. +const Type WAC_PUBLISHER = 1 << 1; +// RootCA certificates for wac-signed widgets. +const Type WAC_ROOT = 1 << 2; +// RootCA certificates for wac-members ie. operators, manufacturers. +const Type WAC_MEMBER = 1 << 3; + +class Set +{ + public: + Set() : + m_certificateStorage(0) + { + } + + void add(Type second) + { + m_certificateStorage |= second; + } + + bool contains(Type second) const + { + return static_cast(m_certificateStorage & second); + } + + bool isEmpty() const + { + return m_certificateStorage == 0; + } + + private: + Type m_certificateStorage; +}; +} // namespace CertStoreId +} // namespace ValidationCore + +#endif // _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_CERTSTORETYPE_H_ diff --git a/modules/vcore/src/vcore/Certificate.cpp b/modules/vcore/src/vcore/Certificate.cpp new file mode 100644 index 0000000..9ec2868 --- /dev/null +++ b/modules/vcore/src/vcore/Certificate.cpp @@ -0,0 +1,493 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#include "Certificate.h" + +#include + +#include +#include + +#include + +namespace ValidationCore { + +int asn1TimeToTimeT(ASN1_TIME *t, + time_t *res) +{ + struct tm tm; + int offset; + + (*res) = 0; + if (!ASN1_TIME_check(t)) { + return -1; + } + + memset(&tm, 0, sizeof(tm)); + +#define g2(p) (((p)[0] - '0') * 10 + (p)[1] - '0') + if (t->type == V_ASN1_UTCTIME) { + Assert(t->length > 12); + + /* this code is copied from OpenSSL asn1/a_utctm.c file */ + tm.tm_year = g2(t->data); + if (tm.tm_year < 50) { + tm.tm_year += 100; + } + tm.tm_mon = g2(t->data + 2) - 1; + tm.tm_mday = g2(t->data + 4); + tm.tm_hour = g2(t->data + 6); + tm.tm_min = g2(t->data + 8); + tm.tm_sec = g2(t->data + 10); + if (t->data[12] == 'Z') { + offset = 0; + } else { + Assert(t->length > 16); + + offset = g2(t->data + 13) * 60 + g2(t->data + 15); + if (t->data[12] == '-') { + offset = -offset; + } + } + tm.tm_isdst = -1; + } else { + Assert(t->length > 14); + + tm.tm_year = g2(t->data) * 100 + g2(t->data + 2); + tm.tm_mon = g2(t->data + 4) - 1; + tm.tm_mday = g2(t->data + 6); + tm.tm_hour = g2(t->data + 8); + tm.tm_min = g2(t->data + 10); + tm.tm_sec = g2(t->data + 12); + if (t->data[14] == 'Z') { + offset = 0; + } else { + Assert(t->length > 18); + + offset = g2(t->data + 15) * 60 + g2(t->data + 17); + if (t->data[14] == '-') { + offset = -offset; + } + } + tm.tm_isdst = -1; + } +#undef g2 + (*res) = timegm(&tm) - offset * 60; + return 0; +} + +int asn1GeneralizedTimeToTimeT(ASN1_GENERALIZEDTIME *tm, + time_t *res) +{ + /* + * This code is based on following assumption: + * from openssl/a_gentm.c: + * GENERALIZEDTIME is similar to UTCTIME except the year is + * represented as YYYY. This stuff treats everything as a two digit + * field so make first two fields 00 to 99 + */ + const int DATE_BUFFER_LENGTH = 15; // YYYYMMDDHHMMSSZ + + if (NULL == res || NULL == tm) { + LogError("NULL pointer"); + return -1; + } + + if (DATE_BUFFER_LENGTH != tm->length || NULL == tm->data) { + LogError("Invalid ASN1_GENERALIZEDTIME"); + return -1; + } + + struct tm time_s; + if (sscanf ((char*)tm->data, + "%4d%2d%2d%2d%2d%2d", + &time_s.tm_year, + &time_s.tm_mon, + &time_s.tm_mday, + &time_s.tm_hour, + &time_s.tm_min, + &time_s.tm_sec) < 6) + { + LogError("Could not extract time data from ASN1_GENERALIZEDTIME"); + return -1; + } + + time_s.tm_year -= 1900; + time_s.tm_mon -= 1; + time_s.tm_isdst = 0; // UTC + time_s.tm_gmtoff = 0; // UTC + time_s.tm_zone = NULL; // UTC + + *res = mktime(&time_s); + + return 0; +} + +Certificate::Certificate(X509 *cert) +{ + Assert(cert); + m_x509 = X509_dup(cert); + if (!m_x509) { + LogWarning("Internal Openssl error in d2i_X509 function."); + ThrowMsg(Exception::OpensslInternalError, + "Internal Openssl error in d2i_X509 function."); + } +} + +Certificate::Certificate(cert_svc_mem_buff &buffer) +{ + Assert(buffer.data); + const unsigned char *ptr = buffer.data; + m_x509 = d2i_X509(NULL, &ptr, buffer.size); + if (!m_x509) { + LogWarning("Internal Openssl error in d2i_X509 function."); + ThrowMsg(Exception::OpensslInternalError, + "Internal Openssl error in d2i_X509 function."); + } +} + +Certificate::Certificate(const std::string &der, + Certificate::FormType form) +{ + Assert(der.size()); + + int size; + const unsigned char *ptr; + std::string tmp; + + if (FORM_BASE64 == form) { + Base64Decoder base64; + base64.reset(); + base64.append(der); + base64.finalize(); + tmp = base64.get(); + ptr = reinterpret_cast(tmp.c_str()); + size = static_cast(tmp.size()); + } else { + ptr = reinterpret_cast(der.c_str()); + size = static_cast(der.size()); + } + + m_x509 = d2i_X509(NULL, &ptr, size); + if (!m_x509) { + LogError("Internal Openssl error in d2i_X509 function."); + ThrowMsg(Exception::OpensslInternalError, + "Internal Openssl error in d2i_X509 function."); + } +} + +Certificate::~Certificate() +{ + X509_free(m_x509); +} + +X509* Certificate::getX509(void) const +{ + return m_x509; +} + +std::string Certificate::getDER(void) const +{ + unsigned char *rawDer = NULL; + int size = i2d_X509(m_x509, &rawDer); + if (!rawDer || size <= 0) { + LogError("i2d_X509 failed"); + ThrowMsg(Exception::OpensslInternalError, + "i2d_X509 failed"); + } + + std::string output(reinterpret_cast(rawDer), size); + OPENSSL_free(rawDer); + return output; +} + +std::string Certificate::getBase64(void) const +{ + Base64Encoder base64; + base64.reset(); + base64.append(getDER()); + base64.finalize(); + return base64.get(); +} + +bool Certificate::isSignedBy(const CertificatePtr &parent) const +{ + if (!parent) { + LogDebug("Invalid certificate parameter."); + return false; + } + return 0 == X509_NAME_cmp(X509_get_subject_name(parent->m_x509), + X509_get_issuer_name(m_x509)); +} + +Certificate::Fingerprint Certificate::getFingerprint( + Certificate::FingerprintType type) const +{ + size_t fingerprintlength = EVP_MAX_MD_SIZE; + unsigned char fingerprint[EVP_MAX_MD_SIZE]; + Fingerprint raw; + + if (type == FINGERPRINT_MD5) { + if (!X509_digest(m_x509, EVP_md5(), fingerprint, &fingerprintlength)) { + LogError("MD5 digest counting failed!"); + ThrowMsg(Exception::OpensslInternalError, + "MD5 digest counting failed!"); + } + } + + if (type == FINGERPRINT_SHA1) { + if (!X509_digest(m_x509, EVP_sha1(), fingerprint, + &fingerprintlength)) + { + LogError("SHA1 digest counting failed"); + ThrowMsg(Exception::OpensslInternalError, + "SHA1 digest counting failed!"); + } + } + + raw.resize(fingerprintlength); // improve performance + std::copy(fingerprint, fingerprint + fingerprintlength, raw.begin()); + + return raw; +} + +DPL::OptionalString Certificate::getField(FieldType type, + int fieldNid) const +{ + X509_NAME *subjectName = NULL; + switch (type) { + case FIELD_ISSUER: + subjectName = X509_get_issuer_name(m_x509); + break; + case FIELD_SUBJECT: + subjectName = X509_get_subject_name(m_x509); + break; + default: + Assert("Invalid type"); + } + + if (!subjectName) { + LogError("Error during subject name extraction."); + ThrowMsg(Exception::OpensslInternalError, + "Error during subject name extraction."); + } + + X509_NAME_ENTRY *subjectEntry = NULL; + + DPL::Optional < DPL::String > output; + + int entryCount = X509_NAME_entry_count(subjectName); + + for (int i = 0; i < entryCount; ++i) { + subjectEntry = X509_NAME_get_entry(subjectName, + i); + + if (!subjectEntry) { + continue; + } + + int nid = OBJ_obj2nid( + static_cast( + X509_NAME_ENTRY_get_object(subjectEntry))); + + if (nid != fieldNid) { + continue; + } + + ASN1_STRING* pASN1Str = subjectEntry->value; + + unsigned char* pData = NULL; + int nLength = ASN1_STRING_to_UTF8(&pData, + pASN1Str); + + if (nLength < 0) { + LogError("Reading field error."); + ThrowMsg(Exception::OpensslInternalError, + "Reading field error."); + } + + std::string strEntry(reinterpret_cast(pData), + nLength); + output = DPL::FromUTF8String(strEntry); + OPENSSL_free(pData); + } + return output; +} + +DPL::OptionalString Certificate::getCommonName(FieldType type) const +{ + return getField(type, NID_commonName); +} + +DPL::OptionalString Certificate::getCountryName(FieldType type) const +{ + return getField(type, NID_countryName); +} + +DPL::OptionalString Certificate::getStateOrProvinceName(FieldType type) const +{ + return getField(type, NID_stateOrProvinceName); +} + +DPL::OptionalString Certificate::getLocalityName(FieldType type) const +{ + return getField(type, NID_localityName); +} + +DPL::OptionalString Certificate::getOrganizationName(FieldType type) const +{ + return getField(type, NID_organizationName); +} + +DPL::OptionalString Certificate::getOrganizationalUnitName(FieldType type) const +{ + return getField(type, NID_organizationalUnitName); +} + +DPL::OptionalString Certificate::getOCSPURL() const +{ + // TODO verify this code + DPL::OptionalString retValue; + AUTHORITY_INFO_ACCESS *aia = static_cast( + X509_get_ext_d2i(m_x509, + NID_info_access, + NULL, + NULL)); + + // no AIA extension in the cert + if (NULL == aia) { + return retValue; + } + + int count = sk_ACCESS_DESCRIPTION_num(aia); + + for (int i = 0; i < count; ++i) { + ACCESS_DESCRIPTION* ad = sk_ACCESS_DESCRIPTION_value(aia, i); + + if (OBJ_obj2nid(ad->method) == NID_ad_OCSP && + ad->location->type == GEN_URI) + { + void* data = ASN1_STRING_data(ad->location->d.ia5); + retValue = DPL::OptionalString(DPL::FromUTF8String( + static_cast(data))); + + break; + } + } + sk_ACCESS_DESCRIPTION_free(aia); + return retValue; +} + +Certificate::AltNameSet Certificate::getAlternativeNameDNS() const +{ + AltNameSet set; + + GENERAL_NAME *namePart = NULL; + + STACK_OF(GENERAL_NAME)* san = + static_cast( + X509_get_ext_d2i(m_x509,NID_subject_alt_name,NULL,NULL)); + + while (sk_GENERAL_NAME_num(san) > 0) { + namePart = sk_GENERAL_NAME_pop(san); + if (GEN_DNS == namePart->type) { + std::string temp = + reinterpret_cast(ASN1_STRING_data(namePart->d.dNSName)); + DPL::String altDNSName = DPL::FromASCIIString(temp); + set.insert(altDNSName); + LogDebug("FOUND GEN_DNS: " << temp); + } else { + LogDebug("FOUND GEN TYPE ID: " << namePart->type); + } + } + return set; +} + +time_t Certificate::getNotAfter() const +{ + ASN1_TIME *time = X509_get_notAfter(m_x509); + if (!time) { + LogError("Reading Not After error."); + ThrowMsg(Exception::OpensslInternalError, "Reading Not After error."); + } + time_t output; + if (asn1TimeToTimeT(time, &output)) { + LogError("Converting ASN1_time to time_t error."); + ThrowMsg(Exception::OpensslInternalError, + "Converting ASN1_time to time_t error."); + } + return output; +} + +bool Certificate::isRootCert() +{ + // based on that root certificate has the same subject as issuer name + return isSignedBy(this->SharedFromThis()); +} + +std::list +Certificate::getCrlUris() const +{ + std::list result; + + STACK_OF(DIST_POINT)* distPoints = + static_cast( + X509_get_ext_d2i( + getX509(), + NID_crl_distribution_points, + NULL, + NULL)); + if (!distPoints) { + LogDebug("No distribution points in certificate."); + return result; + } + + int count = sk_DIST_POINT_num(distPoints); + for (int i = 0; i < count; ++i) { + DIST_POINT* point = sk_DIST_POINT_value(distPoints, i); + if (!point) { + LogError("Failed to get distribution point."); + continue; + } + if (point->distpoint != NULL && + point->distpoint->name.fullname != NULL) + { + int countName = + sk_GENERAL_NAME_num(point->distpoint->name.fullname); + for (int j = 0; j < countName; ++j) { + GENERAL_NAME* name = sk_GENERAL_NAME_value( + point->distpoint->name.fullname, j); + if (name != NULL && GEN_URI == name->type) { + char *crlUri = + reinterpret_cast(name->d.ia5->data); + if (!crlUri) { + LogError("Failed to get URI."); + continue; + } + result.push_back(crlUri); + } + } + } + } + sk_DIST_POINT_pop_free(distPoints, DIST_POINT_free); + return result; +} +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/Certificate.h b/modules/vcore/src/vcore/Certificate.h new file mode 100644 index 0000000..de7636d --- /dev/null +++ b/modules/vcore/src/vcore/Certificate.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#ifndef _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_CERTIFICATE_H_ +#define _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_CERTIFICATE_H_ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace ValidationCore { + +// from OpenSSL asn1/a_utctm.c code +int asn1TimeToTimeT(ASN1_TIME *t, + time_t *res); + + +int asn1GeneralizedTimeToTimeT(ASN1_GENERALIZEDTIME *tm, + time_t *res); + +class Certificate; + +typedef DPL::SharedPtr CertificatePtr; +typedef std::list CertificateList; + +class Certificate : public DPL::EnableSharedFromThis +{ + public: + typedef std::vector Fingerprint; + typedef DPL::String AltName; + typedef std::set AltNameSet; + + enum FingerprintType + { + FINGERPRINT_MD5, + FINGERPRINT_SHA1 + }; + enum FieldType + { + FIELD_ISSUER, + FIELD_SUBJECT + }; + + enum FormType + { + FORM_DER, + FORM_BASE64 + }; + + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, OpensslInternalError) + }; + + explicit Certificate(X509 *cert); + + explicit Certificate(cert_svc_mem_buff &buffer); + + explicit Certificate(const std::string &der, + FormType form = FORM_DER); + + ~Certificate(); + + // It returns pointer to internal structure! + // Do not free this pointer! + X509 *getX509(void) const; + + std::string getDER(void) const; + + std::string getBase64(void) const; + + // This const is cheating here because you have no + // guarantee that X509_get_subject_name will not + // change X509 object. + bool isSignedBy(const CertificatePtr &parent) const; + + Fingerprint getFingerprint(FingerprintType type) const; + + DPL::OptionalString getCommonName(FieldType type = FIELD_SUBJECT) const; + DPL::OptionalString getCountryName(FieldType type = FIELD_SUBJECT) const; + DPL::OptionalString getStateOrProvinceName( + FieldType type = FIELD_SUBJECT) const; + DPL::OptionalString getLocalityName(FieldType type = FIELD_SUBJECT) const; + DPL::OptionalString getOrganizationName( + FieldType type = FIELD_SUBJECT) const; + DPL::OptionalString getOrganizationalUnitName( + FieldType type = FIELD_SUBJECT) const; + DPL::OptionalString getOCSPURL() const; + + // Openssl supports 9 types of alternative name filed. + // 4 of them are "string similar" types so it is possible + // to create more generic function. + AltNameSet getAlternativeNameDNS() const; + + time_t getNotAfter() const; + + /** + * @brief This is convenient function. + * + * @details It can't be const function (however it doesn't change internal + * object). For details see #isSignedBy() function description. + */ + bool isRootCert(); + + /** + * @brief Gets list of CRL distribution's points URIs + */ + std::list getCrlUris() const; + + protected: + DPL::OptionalString getField(FieldType type, + int fieldNid) const; + + X509 *m_x509; +}; +} // namespace ValidationCore + +#endif diff --git a/modules/vcore/src/vcore/CertificateCacheDAO.cpp b/modules/vcore/src/vcore/CertificateCacheDAO.cpp new file mode 100644 index 0000000..04956a3 --- /dev/null +++ b/modules/vcore/src/vcore/CertificateCacheDAO.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file CertificateCacheDAO.cpp + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 0.1 + * @brief CertificateCacheDAO implementation + */ + +#include "CertificateCacheDAO.h" +#include "VCorePrivate.h" + +#include +#include +#include +#include +#include + +using namespace DPL::DB::ORM; +using namespace DPL::DB::ORM::vcore; + +namespace ValidationCore { + +void CertificateCacheDAO::setOCSPStatus(const std::string& cert_chain, + VerificationStatus ocsp_status, + bool end_entity_check, + time_t next_update_time) +{ + Try { + ScopedTransaction transaction(&ThreadInterface()); + OCSPCachedStatus status; + status.cert_chain = cert_chain; + status.end_entity_check = end_entity_check; + if (getOCSPStatus(&status)) { + // only need to update data in DB + Equals e1( + DPL::FromUTF8String(cert_chain)); + Equals e2( + end_entity_check ? 1 : 0); + + OCSPResponseStorage::Row row; + + row.Set_ocsp_status(ocsp_status); + row.Set_next_update_time(next_update_time); + + VCORE_DB_UPDATE(update, OCSPResponseStorage, &ThreadInterface()) + update->Where(And(e1,e2)); + update->Values(row); + update->Execute(); + } else { + // need to insert data + OCSPResponseStorage::Row row; + + row.Set_cert_chain(DPL::FromUTF8String(cert_chain)); + row.Set_ocsp_status(ocsp_status); + row.Set_next_update_time(next_update_time); + row.Set_end_entity_check(end_entity_check ? 1 : 0); + + VCORE_DB_INSERT(insert, OCSPResponseStorage, &ThreadInterface()) + insert->Values(row); + insert->Execute(); + } + transaction.Commit(); + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to setOCSPStatus"); + } +} + +bool CertificateCacheDAO::getOCSPStatus(OCSPCachedStatus* cached_status) +{ + if (NULL == cached_status) { + LogError("NULL pointer"); + return false; + } + Try { + Equals e1( + DPL::FromUTF8String(cached_status->cert_chain)); + Equals e2( + cached_status->end_entity_check ? 1 : 0); + + VCORE_DB_SELECT(select, OCSPResponseStorage, &ThreadInterface()) + + select->Where(And(e1,e2)); + std::list rows = select->GetRowList(); + if (1 == rows.size()) { + OCSPResponseStorage::Row row = rows.front(); + cached_status->ocsp_status = intToVerificationStatus( + *(row.Get_ocsp_status())); + cached_status->next_update_time = *(row.Get_next_update_time()); + return true; + } + + LogDebug("Cached OCSP status not found"); + return false; + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to getOCSPStatus"); + } +} + +void CertificateCacheDAO::getOCSPStatusList( + OCSPCachedStatusList* cached_status_list) +{ + if (NULL == cached_status_list) { + LogError("NULL pointer"); + return; + } + Try { + VCORE_DB_SELECT(select, OCSPResponseStorage, &ThreadInterface()) + typedef std::list RowList; + RowList list = select->GetRowList(); + + FOREACH(i, list) { + OCSPCachedStatus status; + status.cert_chain = DPL::ToUTF8String(i->Get_cert_chain()); + status.ocsp_status = intToVerificationStatus( + *(i->Get_ocsp_status())); + status.end_entity_check = + *(i->Get_end_entity_check()) == 1 ? true : false; + status.next_update_time = *(i->Get_next_update_time()); + cached_status_list->push_back(status); + } + + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to getOCSPStatusList"); + } +} + + +void CertificateCacheDAO::setCRLResponse(const std::string& distribution_point, + const std::string& crl_body, + time_t next_update_time) +{ + Try { + ScopedTransaction transaction(&ThreadInterface()); + CRLCachedData data; + data.distribution_point = distribution_point; + if (getCRLResponse(&data)) { + // only need to update data in DB + VCORE_DB_UPDATE(update, CRLResponseStorage, &ThreadInterface()) + Equals e1( + DPL::FromUTF8String(distribution_point)); + CRLResponseStorage::Row row; + + update->Where(e1); + row.Set_crl_body(DPL::FromUTF8String(crl_body)); + row.Set_next_update_time(next_update_time); + update->Values(row); + update->Execute(); + } else { + // need to insert data + VCORE_DB_INSERT(insert, CRLResponseStorage, &ThreadInterface()) + CRLResponseStorage::Row row; + + row.Set_distribution_point(DPL::FromUTF8String(distribution_point)); + row.Set_crl_body(DPL::FromUTF8String(crl_body)); + row.Set_next_update_time(next_update_time); + insert->Values(row); + insert->Execute(); + } + transaction.Commit(); + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to setOCSPStatus"); + } +} + +bool CertificateCacheDAO::getCRLResponse(CRLCachedData* cached_data) +{ + if (NULL == cached_data) { + LogError("NULL pointer"); + return false; + } + Try { + VCORE_DB_SELECT(select, CRLResponseStorage, &ThreadInterface()) + Equals e1( + DPL::FromUTF8String(cached_data->distribution_point)); + + select->Where(e1); + std::list rows = select->GetRowList(); + if (1 == rows.size()) { + CRLResponseStorage::Row row = rows.front(); + cached_data->crl_body = DPL::ToUTF8String(row.Get_crl_body()); + cached_data->next_update_time = *(row.Get_next_update_time()); + return true; + } + + LogDebug("Cached CRL not found"); + return false; + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to getCRLResponse"); + } +} + +void CertificateCacheDAO::getCRLResponseList( + CRLCachedDataList* cached_data_list) +{ + if (NULL == cached_data_list) { + LogError("NULL pointer"); + return; + } + Try { + VCORE_DB_SELECT(select, CRLResponseStorage, &ThreadInterface()) + typedef std::list RowList; + RowList list = select->GetRowList(); + + FOREACH(i, list) { + CRLCachedData response; + response.distribution_point = DPL::ToUTF8String( + i->Get_distribution_point()); + response.crl_body = DPL::ToUTF8String(i->Get_crl_body()); + response.next_update_time = *(i->Get_next_update_time()); + cached_data_list->push_back(response); + } + + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to getCRLResponses"); + } +} + +void CertificateCacheDAO::clearCertificateCache() +{ + Try { + ScopedTransaction transaction(&ThreadInterface()); + VCORE_DB_DELETE(del1, OCSPResponseStorage, &ThreadInterface()) + del1->Execute(); + VCORE_DB_DELETE(del2, CRLResponseStorage, &ThreadInterface()) + del2->Execute(); + transaction.Commit(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(Exception::DatabaseError, "Failed to clearUserSettings"); + } +} + +VerificationStatus CertificateCacheDAO::intToVerificationStatus(int p) +{ + switch (p) { + case 1: + return VERIFICATION_STATUS_GOOD; + case 1 << 1: + return VERIFICATION_STATUS_REVOKED; + case 1 << 2: + return VERIFICATION_STATUS_UNKNOWN; + case 1 << 3: + return VERIFICATION_STATUS_VERIFICATION_ERROR; + case 1 << 4: + return VERIFICATION_STATUS_NOT_SUPPORT; + case 1 << 5: + return VERIFICATION_STATUS_CONNECTION_FAILED; + case 1 << 6: + return VERIFICATION_STATUS_ERROR; + default: + return VERIFICATION_STATUS_ERROR; + } +} + +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/CertificateCacheDAO.h b/modules/vcore/src/vcore/CertificateCacheDAO.h new file mode 100644 index 0000000..ba26091 --- /dev/null +++ b/modules/vcore/src/vcore/CertificateCacheDAO.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file CertificateCacheDAO.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 0.1 + * @brief Header file for class managing CRL and OCSP cached responses + */ + +#ifndef _WRT_SRC_CONFIGURATION_CERTIFICATE_CACHE_DAO_H_ +#define _WRT_SRC_CONFIGURATION_CERTIFICATE_CACHE_DAO_H_ + +#include +#include + +#include + +#include "VerificationStatus.h" + +namespace ValidationCore { + +struct OCSPCachedStatus +{ + std::string cert_chain; + VerificationStatus ocsp_status; + bool end_entity_check; + time_t next_update_time; +}; + +typedef std::list OCSPCachedStatusList; + +struct CRLCachedData +{ + std::string distribution_point; + std::string crl_body; + time_t next_update_time; +}; + +typedef std::list CRLCachedDataList; + +class CertificateCacheDAO { + public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, DatabaseError) + }; + + // OCSP statuses + + static void setOCSPStatus(const std::string& cert_chain, + VerificationStatus ocsp_status, + bool end_entity_check, + time_t next_update_time); + + /* + * fill cert_chain and end_entity_check in cached_status + * returns true iff cached status found without errors + */ + static bool getOCSPStatus(OCSPCachedStatus* cached_status); + static void getOCSPStatusList(OCSPCachedStatusList* cached_status_list); + + // CRL responses + + static void setCRLResponse(const std::string& distribution_point, + const std::string& crl_body, + time_t next_update_time); + + /* + * fill distribution_point + * returns true iff cached list for dist. point found without errors + */ + static bool getCRLResponse(CRLCachedData* cached_data); + static void getCRLResponseList(CRLCachedDataList* cached_data_list); + + + // clears CRL and OCSP cached data + static void clearCertificateCache(); + + private: + + static VerificationStatus intToVerificationStatus(int p); + + CertificateCacheDAO() + { + } +}; + +} // namespace ValidationCore + +#endif /* _WRT_SRC_CONFIGURATION_CERTIFICATE_CACHE_DAO_H_ */ diff --git a/modules/vcore/src/vcore/CertificateCollection.cpp b/modules/vcore/src/vcore/CertificateCollection.cpp new file mode 100644 index 0000000..988c510 --- /dev/null +++ b/modules/vcore/src/vcore/CertificateCollection.cpp @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include + +#include +#include +#include + +#include + +namespace { +using namespace ValidationCore; + +inline std::string toBinaryString(int data) +{ + char buffer[sizeof(int)]; + memcpy(buffer, &data, sizeof(int)); + return std::string(buffer, buffer + sizeof(int)); +} + +// helper functor for finding certificate basing on subject field +class SubjectElementFinder +{ + public: + SubjectElementFinder(CertificatePtr &cert) : m_searchFor(cert) + { + } + bool operator()(const CertificatePtr &cert) const + { + return cert->isSignedBy(m_searchFor); + } + void setCert(CertificatePtr &cert) + { + m_searchFor = cert; + } + + private: + CertificatePtr m_searchFor; +}; + +// helper functor for finding certificate basing on issuer field +class IssuerElementFinder +{ + public: + IssuerElementFinder(CertificatePtr &cert) : m_searchFor(cert) + { + } + bool operator()(const CertificatePtr &cert) const + { + return m_searchFor->isSignedBy(cert); + } + void setCert(CertificatePtr &cert) + { + m_searchFor = cert; + } + + private: + CertificatePtr m_searchFor; +}; + +//! Helper function +// pops an element from list, finder is responsible for match certificates +template +static CertificatePtr popAnElementFromList(CertificateList& lCertificates, + const T& finder) +{ + CertificatePtr pOutCertificate; + CertificateList::iterator it = + std::find_if(lCertificates.begin(), lCertificates.end(), finder); + + if (it != lCertificates.end()) { + pOutCertificate = *it; + lCertificates.erase(it); + } + + return pOutCertificate; +} +} // namespace + +namespace ValidationCore { +CertificateCollection::CertificateCollection() : + m_collectionStatus(COLLECTION_UNSORTED) +{ +} + +void CertificateCollection::clear(void) +{ + m_collectionStatus = COLLECTION_UNSORTED; + m_certList.clear(); +} + +void CertificateCollection::load(const CertificateList &certList) +{ + m_collectionStatus = COLLECTION_UNSORTED; + std::copy(certList.begin(), + certList.end(), + std::back_inserter(m_certList)); +} + +bool CertificateCollection::load(const std::string &buffer) +{ + Base64Decoder base64; + base64.reset(); + base64.append(buffer); + if (!base64.finalize()) { + LogWarning("Error during chain decoding"); + return false; + } + std::string binaryData = base64.get(); + + DPL::BinaryQueue queue; + queue.AppendCopy(binaryData.c_str(), binaryData.size()); + + int certNum; + queue.FlattenConsume(&certNum, sizeof(int)); + + CertificateList list; + + for (int i = 0; i < certNum; ++i) { + int certSize; + queue.FlattenConsume(&certSize, sizeof(int)); + std::vector rawDERCert; + rawDERCert.resize(certSize); + queue.FlattenConsume(&rawDERCert[0], certSize); + Try { + list.push_back(CertificatePtr( + new Certificate(std::string(rawDERCert.begin(), + rawDERCert.end())))); + } Catch(Certificate::Exception::Base) { + LogWarning("Error during certificate creation."); + return false; + } + LogDebug("Read certificate from database. Certificate common name: " << + list.back()->getCommonName()); + } + load(list); + return true; +} + +std::string CertificateCollection::toBase64String() const +{ + std::ostringstream output; + int certNum = m_certList.size(); + output << toBinaryString(certNum); + FOREACH(i, m_certList){ + std::string derCert = (*i)->getDER(); + output << toBinaryString(derCert.size()); + output << derCert; + } + Base64Encoder base64; + base64.reset(); + base64.append(output.str()); + base64.finalize(); + return base64.get(); +} + +CertificateList CertificateCollection::getCertificateList() const +{ + return m_certList; +} + +bool CertificateCollection::isChain() const +{ + if (COLLECTION_SORTED != m_collectionStatus) { + LogError("You must sort certificates first"); + ThrowMsg(Exception::WrongUsage, + "You must sort certificates first"); + } + return (COLLECTION_SORTED == m_collectionStatus) ? true : false; +} + +bool CertificateCollection::sort() +{ + if (COLLECTION_UNSORTED == m_collectionStatus) { + sortCollection(); + } + return (COLLECTION_SORTED == m_collectionStatus) ? true : false; +} + +CertificateList CertificateCollection::getChain() const +{ + if (COLLECTION_SORTED != m_collectionStatus) { + LogError("You must sort certificates first"); + ThrowMsg(Exception::WrongUsage, + "You must sort certificates first"); + } + return m_certList; +} + +void CertificateCollection::sortCollection() +{ + CertificateList lCertificates = m_certList; + // sorting is not necessary + if (lCertificates.empty()) { + m_collectionStatus = COLLECTION_SORTED; + return; + } + + // backup input list + CertificateList lBackup = lCertificates; + + // create a new output list + CertificateList lSortedCertificates; + + CertificatePtr pCertificate = lCertificates.front(); + + lCertificates.pop_front(); + lSortedCertificates.push_back(pCertificate); + + // indicates whether any matching certificate was found + bool bFound = false; + + CertificatePtr pSubjectCert = pCertificate; + + // lCertificates is very short (3 elements) so + // O(n^2) algorithm is good enough. + if (!lCertificates.empty()) { + // First, find all predecessors for, next all successors + // for pCertificate + SubjectElementFinder subjectFinder(pCertificate); + while ((pSubjectCert = popAnElementFromList(lCertificates, + subjectFinder)) != NULL) { + lSortedCertificates.push_front(pSubjectCert); + subjectFinder.setCert(pSubjectCert); + bFound = true; + } + + CertificatePtr pIssuerCert = pCertificate; + + IssuerElementFinder issuerFinder(pCertificate); + while ((pIssuerCert = popAnElementFromList(lCertificates, + issuerFinder)) != NULL) { + lSortedCertificates.push_back(pIssuerCert); + issuerFinder.setCert(pIssuerCert); + bFound = true; + } + + if (!bFound || !lCertificates.empty()) { + // well, those certificates don't form a valid chain + LogWarning("Certificates don't form a valid chain"); + lCertificates = lBackup; + m_collectionStatus = COLLECTION_CHAIN_BROKEN; + return; + } + } + + m_certList = lSortedCertificates; + m_collectionStatus = COLLECTION_SORTED; +} +} // namespace ValidationCore + diff --git a/modules/vcore/src/vcore/CertificateCollection.h b/modules/vcore/src/vcore/CertificateCollection.h new file mode 100644 index 0000000..67b9ce9 --- /dev/null +++ b/modules/vcore/src/vcore/CertificateCollection.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _WRT_ENGINE_SRC_VALIDATION_CORE_CERTIFICATECOLLECTION_H_ +#define _WRT_ENGINE_SRC_VALIDATION_CORE_CERTIFICATECOLLECTION_H_ + +#include +#include + +#include + +#include + +namespace ValidationCore { +/* + * This class is used to store Certificate Chain. + * It could serialize chain to std::string in base64 form. + * It could read chain written in base64 form. + * It could check if collection creates certificate chain. + */ + +class CertificateCollection +{ + public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, WrongUsage) + }; + + CertificateCollection(); + + typedef CertificateList::const_iterator const_iterator; + + /* + * Remove all certificates from collection. + */ + void clear(); + + /* + * In current implemenation this function MUST success. + * + * This function will add new certificate to collection. + * This function DOES NOT clean collection. + */ + void load(const CertificateList &certList); + + /* + * This function will return false if base64 string is broken + * (is not encoded in base64 format) or one from certificate + * is broken (is not encoded in der format). + * + * This function will add new certificate to collection. + * This function DOES NOT clean collection. + */ + bool load(const std::string &base64); + + /* + * This function will return all certificates from + * collection encoded in base64 format. + */ + std::string toBase64String() const; + + /* + * This will return all certificate from collection. + */ + CertificateList getCertificateList() const; + + /* + * This function will return true if certificates + * in in this structure were sorted and create + * certificate chain. + + * Note: You MUST sort certificates first. + */ + bool isChain() const; + + /* + * This function will return true if all certificate are + * able to create certificate chain. + * + * This function will sort certificates if collection + * is not sorted. + * + * Note: This function will make all iterators invalid. + */ + bool sort(); + + /* + * This function will return Certificate chain. + * + * Note: You MUST sort certificates first and + * check if certificates creates proper chain. + */ + CertificateList getChain() const; + + /* + * It returns size of certificate collection. + */ + inline size_t size() const + { + return m_certList.size(); + } + + /* + * Return true if collection is empty. + */ + inline bool empty() const + { + return m_certList.empty(); + } + + /* + * This will return end iterator to internal collection. + * + * Note: this iterator will lose validity if you call non const + * method on CertificateCollection class. + */ + inline const_iterator begin() const + { + return m_certList.begin(); + } + + /* + * This will return end iterator to internal collection. + * + * Note: this iterator will lose validity if you call non const + * method on CertificateCollection class. + */ + inline const_iterator end() const + { + return m_certList.end(); + } + + /* + * This function will return the last certificate from collection. + * + * Note: There is no point to call this function if certificate + * collection is not sorted! + */ + inline CertificatePtr back() const + { + return m_certList.back(); + } + + protected: + void sortCollection(void); + + enum CollectionStatus + { + // Certificate collection are not sorted in any way + COLLECTION_UNSORTED, + // Certificate collection creates certificate chain + COLLECTION_SORTED, + // Cerfificate collection is not able to create certificate chain + COLLECTION_CHAIN_BROKEN, + }; + + CollectionStatus m_collectionStatus; + CertificateList m_certList; +}; + +typedef std::list CertificateCollectionList; +} // namespace ValidationCore + +#endif // _WRT_ENGINE_SRC_VALIDATION_CORE_CERTIFICATECHAIN_H_ diff --git a/modules/vcore/src/vcore/CertificateConfigReader.cpp b/modules/vcore/src/vcore/CertificateConfigReader.cpp new file mode 100644 index 0000000..2a61940 --- /dev/null +++ b/modules/vcore/src/vcore/CertificateConfigReader.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#include "CertificateConfigReader.h" + +#include + +#include + +namespace { +const std::string XML_EMPTY_NAMESPACE = ""; + +const std::string TOKEN_CERTIFICATE_SET = "CertificateSet"; +const std::string TOKEN_CERTIFICATE_DOMAIN = "CertificateDomain"; +const std::string TOKEN_FINGERPRINT_SHA1 = "FingerprintSHA1"; + +const std::string TOKEN_ATTR_NAME = "name"; +const std::string TOKEN_VALUE_WAC_ROOT = "wacroot"; +const std::string TOKEN_VALUE_WAC_PUBLISHER = "wacpublisher"; +const std::string TOKEN_VALUE_WAC_MEMBER = "wacmember"; +const std::string TOKEN_VALUE_DEVELOPER = "developer"; + +int hexCharToInt(char c) +{ + if (c >= 'a' && c <= 'f') { + return 10 + static_cast(c) - 'a'; + } + if (c >= 'A' && c <= 'F') { + return 10 + static_cast(c) - 'A'; + } + if (c >= '0' && c <= '9') { + return static_cast(c) - '0'; + } + return c; +} +} // anonymous namespace + +namespace ValidationCore { +CertificateConfigReader::CertificateConfigReader() : + m_certificateDomain(0), + m_parserSchema(this) +{ + m_parserSchema.addBeginTagCallback( + TOKEN_CERTIFICATE_SET, + XML_EMPTY_NAMESPACE, + &CertificateConfigReader::blankFunction); + + m_parserSchema.addBeginTagCallback( + TOKEN_CERTIFICATE_DOMAIN, + XML_EMPTY_NAMESPACE, + &CertificateConfigReader::tokenCertificateDomain); + + m_parserSchema.addBeginTagCallback( + TOKEN_FINGERPRINT_SHA1, + XML_EMPTY_NAMESPACE, + &CertificateConfigReader::blankFunction); + + m_parserSchema.addEndTagCallback( + TOKEN_CERTIFICATE_SET, + XML_EMPTY_NAMESPACE, + &CertificateConfigReader::blankFunction); + + m_parserSchema.addEndTagCallback( + TOKEN_CERTIFICATE_DOMAIN, + XML_EMPTY_NAMESPACE, + &CertificateConfigReader::blankFunction); + + m_parserSchema.addEndTagCallback( + TOKEN_FINGERPRINT_SHA1, + XML_EMPTY_NAMESPACE, + &CertificateConfigReader::tokenEndFingerprintSHA1); +} + +void CertificateConfigReader::tokenCertificateDomain(CertificateIdentifier &) +{ + std::string name = m_parserSchema.getReader(). + attribute(TOKEN_ATTR_NAME, SaxReader::THROW_DISABLE); + + if (name.empty()) { + LogWarning("Invalid fingerprint file. Domain name is mandatory"); + ThrowMsg(Exception::InvalidFile, + "Invalid fingerprint file. Domain name is mandatory"); + } else if (name == TOKEN_VALUE_DEVELOPER) { + m_certificateDomain = CertStoreId::DEVELOPER; + } else if (name == TOKEN_VALUE_WAC_ROOT) { + m_certificateDomain = CertStoreId::WAC_ROOT; + } else if (name == TOKEN_VALUE_WAC_PUBLISHER) { + m_certificateDomain = CertStoreId::WAC_PUBLISHER; + } else if (name == TOKEN_VALUE_WAC_MEMBER) { + m_certificateDomain = CertStoreId::WAC_MEMBER; + } +} + +void CertificateConfigReader::tokenEndFingerprintSHA1( + CertificateIdentifier &identificator) +{ + std::string text = m_parserSchema.getText(); + text += ":"; // add guard at the end of fingerprint + Certificate::Fingerprint fingerprint; + int s = 0; + int byteDescLen = 0; + for (size_t i = 0; i < text.size(); ++i) { + if (isxdigit(text[i])) { + s <<= 4; + s += hexCharToInt(text[i]); + byteDescLen++; + if (byteDescLen > 2) { + Assert(0 && "Unsupported fingerprint format in xml file."); + } + } else if (text[i] == ':') { + fingerprint.push_back(static_cast(s)); + s = 0; + byteDescLen = 0; + } else { + Assert(0 && "Unussported fingerprint format in xml file."); + } + } + identificator.add(fingerprint, m_certificateDomain); +} +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/CertificateConfigReader.h b/modules/vcore/src/vcore/CertificateConfigReader.h new file mode 100644 index 0000000..92e000e --- /dev/null +++ b/modules/vcore/src/vcore/CertificateConfigReader.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#ifndef \ + _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_CERTIFICATE_CONFIG_READER_H_ +#define \ + _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_CERTIFICATE_CONFIG_READER_H_ + +#include +#include + +#include "CertificateIdentifier.h" +#include "CertStoreType.h" +#include "ParserSchema.h" + +namespace ValidationCore { +class CertificateConfigReader +{ + public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, InvalidFile) + }; + CertificateConfigReader(); + + void initialize(const std::string &file, + const std::string &scheme) + { + m_parserSchema.initialize(file, true, SaxReader::VALIDATION_XMLSCHEME, + scheme); + } + + void read(CertificateIdentifier &identificator) + { + m_parserSchema.read(identificator); + } + + private: + void blankFunction(CertificateIdentifier &) + { + } + void tokenCertificateDomain(CertificateIdentifier &identificator); + void tokenEndFingerprintSHA1(CertificateIdentifier &identificator); + + CertStoreId::Type m_certificateDomain; + ParserSchema + m_parserSchema; +}; +} // namespace ValidationCore + +#endif // _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_CERTIFICATE_CONFIG_READER_H_ diff --git a/modules/vcore/src/vcore/CertificateIdentifier.h b/modules/vcore/src/vcore/CertificateIdentifier.h new file mode 100644 index 0000000..f9ed48c --- /dev/null +++ b/modules/vcore/src/vcore/CertificateIdentifier.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#ifndef \ + _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_CERTIFICATEIDENTIFICATOR_H_ +#define \ + _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_CERTIFICATEIDENTIFICATOR_H_ + +#include + +#include + +#include "Certificate.h" +#include "CertStoreType.h" + +namespace ValidationCore { +class CertificateIdentifier : public DPL::Noncopyable +{ + public: + typedef std::map FingerPrintMap; + + CertificateIdentifier() + { + } + ~CertificateIdentifier() + { + } + + void add(const Certificate::Fingerprint &fingerprint, + CertStoreId::Type domain) + { + fingerPrintMap[fingerprint].add(domain); + } + + CertStoreId::Set find(const Certificate::Fingerprint &fingerprint) const + { + FingerPrintMap::const_iterator iter = fingerPrintMap.find(fingerprint); + if (iter == fingerPrintMap.end()) { + return CertStoreId::Set(); + } + return iter->second; + } + + CertStoreId::Set find(const CertificatePtr &certificate) const + { + return + find(certificate->getFingerprint(Certificate::FINGERPRINT_SHA1)); + } + + private: + FingerPrintMap fingerPrintMap; +}; +} // namespace ValidationCore + +#endif // _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_CERTIFICATEIDENTIFICATOR_H_ diff --git a/modules/vcore/src/vcore/CertificateLoader.cpp b/modules/vcore/src/vcore/CertificateLoader.cpp new file mode 100644 index 0000000..3bc50de --- /dev/null +++ b/modules/vcore/src/vcore/CertificateLoader.cpp @@ -0,0 +1,725 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include + +#include "Base64.h" +#include "CertificateLoader.h" +#include "SSLContainers.h" + +namespace { +const int MIN_RSA_KEY_LENGTH = 1024; +//const char *OID_CURVE_SECP256R1 = "urn:oid:1.2.840.10045.3.1.7"; +} // namespace anonymous + +namespace ValidationCore { +//// COMPARATOR CLASS START //// + +//class CertificateLoaderECDSA : public CertificateLoader::CertificateLoaderComparator, DPL::Noncopyable { +//public: +// CertificateLoaderECDSA(const std::string &publicKey) +// : m_ecPublicKey(NULL) +// , m_searchKey(NULL) +// { +// m_bnCtx = BN_CTX_new(); // if fails we can continue anyway +// m_tmpPoint = BN_new(); // if fails we can continue anyway +// m_initialized = CertificateLoader::convertBase64NodeToBigNum(publicKey, &m_searchKey); +// +// if(!m_initialized) +// LogError("Init failed!"); +// } +// +// virtual bool compare(X509 *x509cert){ +// if(!m_initialized) +// return false; +// +// EVP_PKEY_free(m_ecPublicKey); +// +// m_ecPublicKey = X509_get_pubkey(x509cert); +// +// if(m_ecPublicKey == NULL) +// return false; +// +// if(m_ecPublicKey->type != EVP_PKEY_EC){ +// LogError("ecPublicKey has wrong type!"); +// return false; +// } +// +// // Pointer to internal data of ecPublicKey. Do not free! +// EC_KEY *eckey = m_ecPublicKey->pkey.ec; +// +// const EC_POINT *ecpoint = EC_KEY_get0_public_key(eckey); +// const EC_GROUP *ecgroup = EC_KEY_get0_group(eckey); +// +// m_tmpPoint = EC_POINT_point2bn(ecgroup, ecpoint, POINT_CONVERSION_UNCOMPRESSED, m_tmpPoint, m_bnCtx); +// +// if(BN_cmp(m_tmpPoint, m_searchKey) == 0) +// return true; +// +// return false; +// } +// +// ~CertificateLoaderECDSA(){ +// BN_CTX_free(m_bnCtx); +// EVP_PKEY_free(m_ecPublicKey); +// BN_free(m_searchKey); +// BN_free(m_tmpPoint); +// } +// +//private: +// bool m_initialized; +// EVP_PKEY *m_ecPublicKey; +// BN_CTX *m_bnCtx; +// BIGNUM *m_searchKey; +// BIGNUM *m_tmpPoint; +//}; + +///// COMPARETORS CLASS END ///// + +//// COMPARATOR RSA CLASS START //// + +//class CertificateLoaderRSA : public CertificateLoader::CertificateLoaderComparator, DPL::Noncopyable { +//public: +// CertificateLoaderRSA(const std::string &m_modulus,const std::string &m_exponent ) +// : m_rsaPublicKey(NULL) +// , m_modulus_bn(NULL) +// , m_exponent_bn(NULL) +// { +// +// m_initialized_modulus = CertificateLoader::convertBase64NodeToBigNum(m_modulus, &m_modulus_bn); +// m_initialized_exponent = CertificateLoader::convertBase64NodeToBigNum(m_exponent, &m_exponent_bn); +// +// if(!m_initialized_modulus || !m_initialized_exponent) +// LogError("Init failed!"); +// } +// +// virtual bool compare(X509 *x509cert){ +// +// if(!m_initialized_modulus || !m_initialized_exponent) +// return false; +// +// EVP_PKEY_free(m_rsaPublicKey); +// m_rsaPublicKey = X509_get_pubkey(x509cert); +// +// if(m_rsaPublicKey == NULL) +// return false; +// +// if(m_rsaPublicKey->type != EVP_PKEY_RSA){ +// LogInfo("rsaPublicKey has wrong type!"); +// return false; +// } +// +// RSA *rsa = NULL; +// rsa = m_rsaPublicKey->pkey.rsa; +// +// if (BN_cmp(m_modulus_bn, rsa->n) == 0 && +// BN_cmp(m_exponent_bn, rsa->e) == 0 ){ +// LogError ("Compare TRUE"); +// return true; +// } +// return false; +// } +// +// ~CertificateLoaderRSA(){ +// EVP_PKEY_free(m_rsaPublicKey); +// BN_free(m_modulus_bn); +// BN_free(m_exponent_bn); +// +// } +// +//private: +// bool m_initialized_modulus; +// bool m_initialized_exponent; +// EVP_PKEY *m_rsaPublicKey; +// BIGNUM *m_modulus_bn; +// BIGNUM *m_exponent_bn; +//}; + +///// COMPARETORS RSA CLASS END ///// + +CertificateLoader::CertificateLoaderResult CertificateLoader:: + loadCertificateBasedOnExponentAndModulus(const std::string &m_modulus, + const std::string &m_exponent) +{ + (void) m_modulus; + (void) m_exponent; + LogError("Not implemented."); + return UNKNOWN_ERROR; + // if (m_exponent.empty() || m_modulus.empty()) + // return WRONG_ARGUMENTS; + // + // CertificateLoaderRSA comparator(m_modulus,m_exponent); + // + // CertificateLoaderResult result = NO_ERROR; + // for(int i=0; storeId[i]; ++i){ + // result = loadCertificate(std::string(storeId[i]), &comparator); + // + // if(result == ERR_NO_MORE_CERTIFICATES) + // continue; + // + // return result; + // } + // + // return result; +} + +CertificateLoader::CertificateLoaderResult CertificateLoader::loadCertificate( + const std::string &storageName, + CertificateLoader::CertificateLoaderComparator *cmp) +{ + (void) storageName; + (void) cmp; + LogError("Not Implemented"); + return UNKNOWN_ERROR; + // long int result = OPERATION_SUCCESS; + // + // char storeId[CERTMGR_MAX_PLUGIN_ID_SIZE]; + // char type[CERTMGR_MAX_CERT_TYPE_SIZE]; + // certmgr_cert_id certId; + // certmgr_ctx context; + // certmgr_mem_buff certRetrieved; + // unsigned char buffer[CERTMGR_MAX_BUFFER_SIZE]; + // + // certmgr_cert_descriptor descriptor; + // + // certRetrieved.data = buffer; + // certRetrieved.firstFree = 0; + // certRetrieved.size = CERTMGR_MAX_BUFFER_SIZE; + // certId.storeId = storeId; + // certId.type = type; + // + // CERTMGR_INIT_CONTEXT((&context), (sizeof(context))) + // + // strncpy(context.storeId, storageName.c_str(), storageName.size()); + // + // for(certRetrieved.firstFree = 0; + // OPERATION_SUCCESS == (result = certmgr_retrieve_certificate_from_store(&context, &certRetrieved, &certId)); + // certRetrieved.firstFree = 0) + // { + // + // if(OPERATION_SUCCESS!=certmgr_extract_certificate_data(&certRetrieved, &descriptor)){ + // LogError("Extracting Certificate Data failed \n"); + // continue; + // } + // + // const unsigned char *ptr = certRetrieved.data; + // + // X509 *x509cert = d2i_X509(NULL, &ptr, certRetrieved.size); + // if(x509cert == NULL){ + // certmgr_release_certificate_data(&descriptor); + // LogError("Error extracting certificate (d2i_X509)."); + // return UNKNOWN_ERROR; + // } + // + // LogDebug("The subject of this certificate is " << descriptor.mandatory.subject); + // if(cmp->compare(x509cert)){ + // LogDebug("Found match. Coping bytes: " << certRetrieved.size); + // m_certificatePtr = CertificatePtr(new Certificate(certRetrieved)); + // certmgr_release_certificate_data(&descriptor); + // X509_free(x509cert); + // break; + // } + // + // LogDebug("Release"); + // X509_free(x509cert); + // certmgr_release_certificate_data(&descriptor); + // } + // + // if(ERR_NO_MORE_CERTIFICATES == result){ + // LogError("Certificates for given DN not found\n"); + // return CERTIFICATE_NOT_FOUND; + // } + // + // if(result!= OPERATION_SUCCESS){ + // LogError("Certificate Manager Error\n"); + // return UNKNOWN_ERROR; + // } + // + // LogDebug("Exit"); + // return NO_ERROR; +} + +// TODO +CertificateLoader::CertificateLoaderResult CertificateLoader:: + loadCertificateBasedOnSubjectName(const std::string &subjectName) +{ + (void) subjectName; + LogError("Not implemented."); + return UNKNOWN_ERROR; + // if(subjectName.empty()) + // { + // return WRONG_ARGUMENTS; + // } + // + // long int result = OPERATION_SUCCESS; + // + // char storeId[CERTMGR_MAX_PLUGIN_ID_SIZE]; + // char type[CERTMGR_MAX_CERT_TYPE_SIZE]; + // certmgr_cert_id certId; + // certmgr_ctx context; + // certmgr_mem_buff certRetrieved; + // unsigned char buffer[CERTMGR_MAX_BUFFER_SIZE]; + // + // certmgr_cert_descriptor descriptor; + // + // certRetrieved.data = buffer; + // certRetrieved.firstFree = 0; + // certRetrieved.size = CERTMGR_MAX_BUFFER_SIZE; + // certId.storeId = storeId; + // certId.type = type; + // + // CERTMGR_INIT_CONTEXT((&context), (sizeof(context))) + // + // for(certRetrieved.firstFree = 0; + // OPERATION_SUCCESS == (result = certmgr_retrieve_certificate_from_store(&context, &certRetrieved, &certId)); + // certRetrieved.firstFree = 0) + // { + // + // if(OPERATION_SUCCESS!=certmgr_extract_certificate_data(&certRetrieved, &descriptor)){ + // LogError("Extracting Certificate Data failed \n"); + // continue; + // } + // + // if(!strcmp(subjectName.c_str(), descriptor.mandatory.subject)){ + // LogDebug("The subject of this certificate is " << descriptor.mandatory.subject); + // m_certificatePtr = CertificatePtr(new Certificate(certRetrieved)); + // certmgr_release_certificate_data(&descriptor); + // break; + // } + // LogDebug("Release"); + // certmgr_release_certificate_data(&descriptor); + // } + // + // if(ERR_NO_MORE_CERTIFICATES == result) { + // LogError("Certificates for given DN not found\n"); + // return CERTIFICATE_NOT_FOUND; + // } + // if(result!= OPERATION_SUCCESS){ + // LogError("Certificate Manager Error\n"); + // return UNKNOWN_ERROR; + // } + // LogDebug("Exit"); + // return NO_ERROR; +} + +// KW CertificateLoader::CertificateLoaderResult CertificateLoader::loadCertificateBasedOnIssuerName(const std::string &issuerName, const std::string &serialNumber) +// KW { +// KW if(issuerName.empty() || serialNumber.empty()) +// KW { +// KW return WRONG_ARGUMENTS; +// KW } +// KW +// KW if(m_cmBuff.data){ +// KW delete[] m_cmBuff.data; +// KW memset(&m_cmBuff, 0, sizeof(certmgr_mem_buff)); +// KW } +// KW +// KW LogDebug("IssuerName: " << issuerName << " serialNumber: " << serialNumber); +// KW +// KW //used to check status of retrieved certificate +// KW long int result = OPERATION_SUCCESS; +// KW char storeId[CERTMGR_MAX_PLUGIN_ID_SIZE]; +// KW char type[CERTMGR_MAX_CERT_TYPE_SIZE]; +// KW certmgr_cert_id certId; +// KW certmgr_ctx context; +// KW certmgr_mem_buff certRetrieved; +// KW unsigned char buffer[CERTMGR_MAX_BUFFER_SIZE]; +// KW +// KW certmgr_cert_descriptor descriptor; +// KW +// KW certRetrieved.data = buffer; +// KW certRetrieved.firstFree = 0; +// KW certRetrieved.size = CERTMGR_MAX_BUFFER_SIZE; +// KW certId.storeId = storeId; +// KW certId.type = type; +// KW +// KW CERTMGR_INIT_CONTEXT((&context), (sizeof(context))) +// KW +// KW for(certRetrieved.firstFree = 0; +// KW OPERATION_SUCCESS == (result = certmgr_retrieve_certificate_from_store(&context, &certRetrieved, &certId)); +// KW certRetrieved.firstFree = 0) +// KW { +// KW +// KW LogDebug("Extracting certificate from CertMgr"); +// KW +// KW if( OPERATION_SUCCESS != certmgr_extract_certificate_data(&certRetrieved, &descriptor) ){ +// KW LogError("Extracting Certificate Data failed \n"); +// KW continue; +// KW } +// KW +// KW LogDebug("Issuer: " << descriptor.mandatory.issuer); +// KW +// KW const unsigned char *ptr = certRetrieved.data; +// KW char *tmp; +// KW +// KW X509 *x509cert = d2i_X509(NULL, &ptr, certRetrieved.size); +// KW std::string serialNO = std::string(tmp = i2s_ASN1_INTEGER(NULL, X509_get_serialNumber(x509cert))); +// KW OPENSSL_free(tmp); +// KW X509_free(x509cert); +// KW +// KW LogInfo("Certificate number found: " << serialNO); +// KW LogInfo("Certificate number looking for: " << serialNumber); +// KW +// KW if(!strcmp(issuerName.c_str(), descriptor.mandatory.issuer) +// KW && serialNumber == serialNO) +// KW { +// KW LogError("The issuer of this certificate is " << descriptor.mandatory.issuer); +// KW +// KW m_cmBuff.data = new unsigned char[certRetrieved.size]; +// KW m_cmBuff.firstFree = m_cmBuff.size = certRetrieved.size; +// KW memcpy(m_cmBuff.data, certRetrieved.data, certRetrieved.size); +// KW certmgr_release_certificate_data(&descriptor); +// KW break; +// KW } +// KW certmgr_release_certificate_data(&descriptor); +// KW } +// KW +// KW if(ERR_NO_MORE_CERTIFICATES == result) { +// KW LogError("Certificates not found"); +// KW return CERTIFICATE_NOT_FOUND; +// KW } +// KW if(result != OPERATION_SUCCESS){ +// KW LogError("Certificate Manager Error"); +// KW return UNKNOWN_ERROR; +// KW } +// KW return NO_ERROR; +// KW } + +CertificateLoader::CertificateLoaderResult CertificateLoader:: + loadCertificateWithECKEY(const std::string &curveName, + const std::string &publicKey) +{ + (void) curveName; + (void) publicKey; + LogError("Not implemented."); + return UNKNOWN_ERROR; + // if(curveName != OID_CURVE_SECP256R1){ + // LogError("Found field id: " << curveName << " Expected: " << OID_CURVE_SECP256R1); + // return UNSUPPORTED_CERTIFICATE_FIELD; + // } + // + // CertificateLoaderECDSA comparator(publicKey); + // + // CertificateLoaderResult result = NO_ERROR; + // for(int i=0; storeId[i]; ++i){ + // result = loadCertificate(std::string(storeId[i]), &comparator); + // + // if(result == ERR_NO_MORE_CERTIFICATES) + // continue; + // + // return result; + // } + // + // return result; +} + +CertificateLoader::CertificateLoaderResult CertificateLoader:: + loadCertificateFromRawData(const std::string &rawData) +{ + Try { + Base64Decoder base; + base.reset(); + base.append(rawData); + if (!base.finalize()) { + LogWarning("Certificate format is broken."); + return UNKNOWN_ERROR; + } + std::string derCert = base.get(); + m_certificatePtr = CertificatePtr(new Certificate(derCert)); + } Catch(Certificate::Exception::Base) { + LogWarning("Error reading certificate by openssl."); + return UNKNOWN_ERROR; + } + + // Check the key length if sig algorithm is RSA + EVP_PKEY *pKey = X509_get_pubkey(m_certificatePtr->getX509()); + + if (pKey->type == EVP_PKEY_RSA) { + RSA* pRSA = pKey->pkey.rsa; + + if (pRSA) { + int keyLength = RSA_size(pRSA); + + // key Length (modulus) is in bytes + keyLength <<= 3; + LogDebug("RSA key length: " << keyLength << " bits"); + + if (keyLength < MIN_RSA_KEY_LENGTH) { + LogError( + "RSA key too short!" << "Has only " << keyLength << " bits"); + return CERTIFICATE_SECURITY_ERROR; + } + } + } + + return NO_ERROR; +} + +// DEPRACETED FUNCTION +//CertificateLoader::CertificateLoaderResult CertificateLoader::loadCertificateFromRawData(const std::string &rawData) +//{ +// certmgr_mem_buff cmBuff = {0,0,0}; +// +// long int size; +// cmBuff.data = certmgr_util_base64_decode(const_cast(static_cast(rawData.c_str())), rawData.size(), &size); +// +// cmBuff.firstFree = cmBuff.size = size; +// +// certmgr_cert_descriptor descriptor; +// +// long int result = certmgr_extract_certificate_data(&cmBuff, &descriptor); +// +// if (result != OPERATION_SUCCESS) +// { +// LogError("Unable to load certificate"); +// return UNKNOWN_ERROR; +// } +// +// certmgr_release_certificate_data(&descriptor); +// +// m_certificatePtr = CertificatePtr(new Certificate(cmBuff)); +// +// // we have to use temp pointer cause d2i_x509 modifies its input +// const unsigned char* tmpPtr = cmBuff.data; +// X509* pCertificate = d2i_X509(NULL, &tmpPtr, cmBuff.size); +// +// if (pCertificate) +// { +// SSLSmartContainer pX509(pCertificate); +// +// // Check the key length if sig algorithm is RSA +// EVP_PKEY *pKey = X509_get_pubkey(pX509); +// +// if (pKey->type == EVP_PKEY_RSA) +// { +// RSA* pRSA = pKey->pkey.rsa; +// +// if (pRSA) +// { +// int keyLength = RSA_size(pRSA); +// +// // key Length (modulus) is in bytes +// keyLength <<= 3; +// LogDebug("RSA key length: " << keyLength << " bits"); +// +// if (keyLength < MIN_RSA_KEY_LENGTH) +// { +// LogError("RSA key too short!" << "Has only " << keyLength << " bits"); +// return CERTIFICATE_SECURITY_ERROR; +// } +// } +// } +// } +// +// return NO_ERROR; +//} + +CertificateLoader::CertificateLoaderResult CertificateLoader:: + loadCertificateBasedOnDSAComponents(const std::string& strP, + const std::string& strQ, + const std::string& strG, + const std::string& strY, + const std::string& strJ, + const std::string& strSeed, + const std::string& strPGenCounter) +{ + (void) strP; + (void) strQ; + (void) strG; + (void) strY; + (void) strJ; + (void) strSeed; + (void) strPGenCounter; + LogError("Not implemented."); + return UNKNOWN_ERROR; + // (void)strY; + // (void)strJ; + // (void)strSeed; + // (void)strPGenCounter; + // + // long int result = UNKNOWN_ERROR; + // + // char storeId[CERTMGR_MAX_PLUGIN_ID_SIZE]; + // char type[CERTMGR_MAX_CERT_TYPE_SIZE]; + // certmgr_cert_id certId; + // certmgr_ctx context; + // certmgr_mem_buff certRetrieved; + // + // unsigned char buffer[CERTMGR_MAX_BUFFER_SIZE]; + // + // certmgr_cert_descriptor descriptor; + // + // certRetrieved.data = buffer; + // certRetrieved.firstFree = 0; + // certRetrieved.size = CERTMGR_MAX_BUFFER_SIZE; + // certId.storeId = storeId; + // certId.type = type; + // + // CERTMGR_INIT_CONTEXT((&context), (sizeof(context))) + // std::string strStoreType("Operator"); + // strncpy(context.storeId, strStoreType.c_str(), strStoreType.length()); + // + // for (certRetrieved.firstFree = 0; + // OPERATION_SUCCESS == (result = certmgr_retrieve_certificate_from_store(&context, &certRetrieved, &certId)); + // certRetrieved.firstFree = 0) + // { + // + // if (OPERATION_SUCCESS != certmgr_extract_certificate_data(&certRetrieved, &descriptor)) + // { + // LogDebug("unable to retrieve cert from storage"); + // continue; + // } + // + // X509* pCertificate = d2i_X509(NULL, (const unsigned char**) &(certRetrieved.data), certRetrieved.size); + // + // if (pCertificate) + // { + // EVP_PKEY *pKey = X509_get_pubkey(pCertificate); + // + // if (pKey->type == EVP_PKEY_DSA) + // { + // DSA* pDSA = pKey->pkey.dsa; + // + // if (pDSA) + // { + // BIGNUM *pDSApBigNum = NULL, *pDSAqBigNum = NULL, *pDSAgBigNum = NULL; + // + // convertBase64NodeToBigNum(strP, &pDSApBigNum); + // convertBase64NodeToBigNum(strQ, &pDSAqBigNum); + // convertBase64NodeToBigNum(strG, &pDSAgBigNum); + // + // if (pDSApBigNum && pDSAqBigNum && pDSAgBigNum && + // BN_cmp(pDSApBigNum, pDSA->p) == 0 && + // BN_cmp(pDSAqBigNum, pDSA->q) == 0 && + // BN_cmp(pDSAgBigNum, pDSA->g) == 0) + // { + // LogInfo("DSA Certificate found"); + // /* TODO load this certificate to m_cmBuff value */ + // LogError("Not implemented!"); + // + // EVP_PKEY_free(pKey); + // X509_free(pCertificate); + // + // BN_free(pDSApBigNum); + // BN_free(pDSAqBigNum); + // BN_free(pDSAgBigNum); + // + // certmgr_release_certificate_data(&descriptor); + // return NO_ERROR; + // } + // + // if (pDSApBigNum) + // { + // BN_free(pDSApBigNum); + // } + // if (pDSAqBigNum) + // { + // BN_free(pDSAqBigNum); + // } + // if (pDSAgBigNum) + // { + // BN_free(pDSAgBigNum); + // } + // + // } + // EVP_PKEY_free(pKey); + // } + // X509_free(pCertificate); + // } + // else + // LogError("Unable to load certificate"); + // + // certmgr_release_certificate_data(&descriptor); + // } + // + // LogError("No DSA certificate with given parameters"); + // + // return CERTIFICATE_NOT_FOUND; +} + +bool CertificateLoader::convertBase64NodeToBigNum(const std::string& strNode, + BIGNUM** ppBigNum) +{ + (void) strNode; + (void) ppBigNum; + LogError("Not implemented."); + return false; + // if (!ppBigNum || *ppBigNum != NULL) + // { + // LogError("Ptr variable not initialized properly!"); + // return false; + // } + // + // // decode base64 to binary + // long int binBuffLength = 0; + // unsigned char* binBuff = NULL; + // + // binBuff = certmgr_util_base64_decode(const_cast (strNode.c_str()), strNode.length(), &binBuffLength); + // + // if (!binBuff) + // { + // LogError("base64 decode failed"); + // return false; + // } + // + // // convert binary to bignum + // *ppBigNum = BN_bin2bn(binBuff, binBuffLength, *ppBigNum); + // + // free(binBuff); + // + // if (!(*ppBigNum)) + // { + // LogError("Conversion from node to bignum failed"); + // return false; + // } + // + // return true; +} + +// KW bool CertificateLoader::convertBigNumToBase64Node(const BIGNUM* pBigNum, std::string& strNode) +// KW { +// KW if (!pBigNum) +// KW { +// KW LogError("null ptr"); +// KW return false; +// KW } +// KW +// KW int nNumLength = BN_num_bytes(pBigNum); +// KW unsigned char* buffer = new unsigned char[nNumLength + 1]; +// KW +// KW // convert bignum to binary format +// KW if (BN_bn2bin(pBigNum, buffer) < 0) +// KW { +// KW LogError("Conversion from bignum to binary failed"); +// KW delete []buffer; +// KW return false; +// KW } +// KW +// KW char* pBase64Node = NULL; +// KW unsigned long int buffLen = 0; +// KW certmgr_util_base64_encode(buffer, (unsigned long int) nNumLength, &pBase64Node, &buffLen); +// KW +// KW strNode.assign(pBase64Node, buffLen); +// KW +// KW delete []buffer; +// KW return true; +// KW } +} // namespace ValidationCore + diff --git a/modules/vcore/src/vcore/CertificateLoader.h b/modules/vcore/src/vcore/CertificateLoader.h new file mode 100644 index 0000000..64c38ac --- /dev/null +++ b/modules/vcore/src/vcore/CertificateLoader.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _CERTIFICATELOADER_H_ +#define _CERTIFICATELOADER_H_ + +#include +#include + +#include +#include + +#include + +#include "Certificate.h" + +namespace ValidationCore { +class CertificateLoader : public DPL::Noncopyable +{ + public: + class CertificateLoaderComparator + { + public: + virtual bool compare(X509 *x509cert) = 0; + virtual ~CertificateLoaderComparator() + { + } + }; + + enum CertificateLoaderResult + { + NO_ERROR, + CERTIFICATE_NOT_FOUND, + UNSUPPORTED_CERTIFICATE_FIELD, + WRONG_ARGUMENTS, + CERTIFICATE_SECURITY_ERROR, //!< there are some issues with certificate security (i.e. key too short) + UNKNOWN_ERROR + }; + + CertificateLoader() + { + } + + virtual ~CertificateLoader() + { + } + + CertificateLoaderResult loadCertificate(const std::string& storage, + CertificateLoaderComparator *cmp); + + CertificateLoaderResult loadCertificateBasedOnSubjectName( + const std::string &subjectName); + CertificateLoaderResult loadCertificateBasedOnExponentAndModulus( + const std::string &m_modulus, + const std::string &m_exponent); + // KW CertificateLoaderResult loadCertificateBasedOnIssuerName(const std::string &isserName, + // KW const std::string &serialNumber); + + CertificateLoaderResult loadCertificateFromRawData( + const std::string &rawData); + + CertificateLoaderResult loadCertificateBasedOnDSAComponents( + const std::string& strP, + const std::string& strQ, + const std::string& strG, + const std::string& strY, + const std::string& strJ, + const std::string& strSeed, + const std::string& strPGenCounter); + + CertificateLoaderResult loadCertificateWithECKEY( + const std::string &curveName, + const std::string &publicKey); + + /** + * converts base64 encoded node to SSL bignum + * allocates mem on *ppBigNum, don't forget to free it later with BN_free! + * returns conversion status + */ + static bool convertBase64NodeToBigNum(const std::string& strNode, + BIGNUM** ppBigNum); + + /* + * encodes SSL bignum into base64 octstring + * returns conversion status + */ + // KW static bool convertBigNumToBase64Node(const BIGNUM* pBigNum, std::string& strNode); + + CertificatePtr getCertificatePtr() const + { + return m_certificatePtr; + } + private: + CertificatePtr m_certificatePtr; +}; +} // namespace ValidationCore + +#endif // _CERTIFICATELOADER_H_ diff --git a/modules/vcore/src/vcore/CertificateStorage.h b/modules/vcore/src/vcore/CertificateStorage.h new file mode 100644 index 0000000..7fbcb6b --- /dev/null +++ b/modules/vcore/src/vcore/CertificateStorage.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef VCORE_SRC_VCORE_CERTIFICATESTORAGE_H +#define VCORE_SRC_VCORE_CERTIFICATESTORAGE_H + +#include +#include + +namespace ValidationCore { +typedef std::list < X509* > X509CertificatesList; +} + +#endif // VCORE_SRC_VCORE_CERTIFICATESTORAGE_H diff --git a/modules/vcore/src/vcore/CertificateVerifier.cpp b/modules/vcore/src/vcore/CertificateVerifier.cpp new file mode 100644 index 0000000..f05662a --- /dev/null +++ b/modules/vcore/src/vcore/CertificateVerifier.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @author Bartlomiej Grzelewski (b.grzelewski@gmail.com) + * @version 0.1 + * @file CertificateVerifier.cpp + * @brief This class integrates OCSP and CRL. + */ +#include "CertificateVerifier.h" + +#include +#include +#include + +namespace ValidationCore { + +CertificateVerifier::CertificateVerifier(bool enableOcsp, bool enableCrl) +: m_enableOcsp(enableOcsp) +, m_enableCrl(enableCrl) +{} + +VerificationStatus CertificateVerifier::check( + CertificateCollection &certCollection) const +{ + LogDebug("== Certificate collection validation start =="); + Assert(certCollection.isChain() && "Collection must form chain."); + + VerificationStatus statusOcsp; + VerificationStatus statusCrl; + + if (m_enableOcsp) { + statusOcsp = obtainOcspStatus(certCollection); + } else { + statusOcsp = VERIFICATION_STATUS_GOOD; + } + + if (m_enableCrl) { + statusCrl = obtainCrlStatus(certCollection); + } else { + statusCrl = VERIFICATION_STATUS_GOOD; + } + LogDebug("== Certificate collection validation end =="); + return getStatus(statusOcsp, statusCrl); +} + +VerificationStatus CertificateVerifier::obtainOcspStatus( + const CertificateCollection &chain) const +{ + LogDebug("== Obtain ocsp status =="); + CachedOCSP ocsp; + return ocsp.check(chain); +} + +VerificationStatus CertificateVerifier::obtainCrlStatus( + const CertificateCollection &chain) const +{ + LogDebug("== Obtain crl status =="); + CachedCRL crl; + return crl.check(chain); +} + +VerificationStatus CertificateVerifier::getStatus( + VerificationStatus ocsp, + VerificationStatus crl) const +{ + if (ocsp == VERIFICATION_STATUS_REVOKED || + crl == VERIFICATION_STATUS_REVOKED) + { + LogDebug("Return status: REVOKED"); + return VERIFICATION_STATUS_REVOKED; + } + + if (ocsp == VERIFICATION_STATUS_GOOD) { + LogDebug("Return status: GOOD"); + return VERIFICATION_STATUS_GOOD; + } + + if (ocsp == VERIFICATION_STATUS_UNKNOWN) { + LogDebug("Return status: UNKNOWN"); + return VERIFICATION_STATUS_UNKNOWN; + } + + if (ocsp == VERIFICATION_STATUS_NOT_SUPPORT) { + LogDebug("Return status: NOT_SUPPORT"); + return VERIFICATION_STATUS_GOOD; + } + + LogDebug("Return status: ERROR"); + return VERIFICATION_STATUS_ERROR; +} + +VerificationStatus CertificateVerifier::checkEndEntity( + CertificateCollectionList &collectionList) const +{ + VerificationStatusSet statusOcsp; + VerificationStatusSet statusCrl; + + if (m_enableOcsp) { + CachedOCSP ocsp; + FOREACH(it, collectionList){ + statusOcsp.add(ocsp.checkEndEntity(*it)); + } + } else { + statusOcsp.add(VERIFICATION_STATUS_GOOD); + } + + if (m_enableCrl) { + CachedCRL crl; + FOREACH(it, collectionList){ + statusCrl.add(crl.checkEndEntity(*it)); + } + } else { + statusCrl.add(VERIFICATION_STATUS_GOOD); + } + LogDebug("== Certificate collection validateion end =="); + return getStatus(statusOcsp.convertToStatus(), statusCrl.convertToStatus()); +} + +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/CertificateVerifier.h b/modules/vcore/src/vcore/CertificateVerifier.h new file mode 100644 index 0000000..ea77812 --- /dev/null +++ b/modules/vcore/src/vcore/CertificateVerifier.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @author Bartlomiej Grzelewski (b.grzelewski@gmail.com) + * @version 0.1 + * @file CertificateVerifier.h + * @brief This class integrates OCSP and CRL into one module. + */ +#ifndef _SRC_VALIDATION_CORE_CERTIFICATE_VERIFIER_H_ +#define _SRC_VALIDATION_CORE_CERTIFICATE_VERIFIER_H_ + +#include "Certificate.h" +#include "CertificateCollection.h" +#include "CachedCRL.h" +#include "CachedOCSP.h" +#include "VerificationStatus.h" + +namespace ValidationCore { + +class CertificateVerifier { + public: + explicit CertificateVerifier(bool enableOcsp, bool enableCrl); + ~CertificateVerifier(){} + + /* + * Run OCSP and CRL for all certificates in collection. + * Collection must represent chain. + * + * Evaluate status. This function converts ocsp status set + * into one status - the most restricted. This one ocsp status + * and status from crl is evaluated to end result. + * + * Algorithm to evaluate result is represented in table: + * + * +--------------+-------+-------+-------+------------+---------+ + * | OCSP |Good |Revoked|Unknown|Undetermined|Not | + * | | | | | |supported| + * | CRL | | | | | | + * +--------------+-------+-------+-------+------------+---------+ + * | GOOD |GOOD |Revoked|Unknown|Undetermined|Good | + * +--------------+-------+-------+-------+------------+---------+ + * | REVOKED |Revoked|Revoked|Revoked|Revoked |Revoked | + * +--------------+-------+-------+-------+------------+---------+ + * | UNDETERMINED |Good |Revoked|Unknown|Undetermined|Good | + * +--------------+-------+-------+-------+------------+---------+ + * | Not supported|Good |Revoked|Unknown|Undetermined|Good | + * +--------------+-------+-------+-------+------------+---------+ + * + * As Undetermind function returns VERIFICATION_STATUS_ERROR. + */ + + VerificationStatus check(CertificateCollection &certCollection) const; + + VerificationStatus checkEndEntity( + CertificateCollectionList &certCollectionList) const; + + private: + VerificationStatus obtainOcspStatus( + const CertificateCollection &chain) const; + VerificationStatus obtainCrlStatus( + const CertificateCollection &chain) const; + VerificationStatus getStatus(VerificationStatus ocsp, + VerificationStatus crl) const; + + bool m_enableOcsp; + bool m_enableCrl; +}; + +} // namespace ValidationCore + +#endif // _SRC_VALIDATION_CORE_CERTIFICATE_VERIFIER_H_ + diff --git a/modules/vcore/src/vcore/Config.cpp b/modules/vcore/src/vcore/Config.cpp new file mode 100644 index 0000000..7dfaedf --- /dev/null +++ b/modules/vcore/src/vcore/Config.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Config.h" + +#include +IMPLEMENT_SINGLETON(ValidationCore::Config) diff --git a/modules/vcore/src/vcore/Config.h b/modules/vcore/src/vcore/Config.h new file mode 100644 index 0000000..0783cd1 --- /dev/null +++ b/modules/vcore/src/vcore/Config.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _SRC_VALIDATION_CORE_VALIDATION_CORE_CONFIG_H_ +#define _SRC_VALIDATION_CORE_VALIDATION_CORE_CONFIG_H_ + +#include + +#include + +namespace ValidationCore { +class Config { +public: + /* + * Set path to config file with certificate description. + */ + bool setXMLConfigPath(const std::string& path) { + if (!m_certificateXMLConfigPath.empty()) { + return false; + } + m_certificateXMLConfigPath = path; + return true; + } + + /* + * Set path to schema of config file. + */ + bool setXMLSchemaPath(const std::string& path) { + if (!m_certificateXMLSchemaPath.empty()) { + return false; + } + m_certificateXMLSchemaPath = path; + return true; + } + + /* + * Set path to database with OCSP/CRL Cache. + */ + bool setDatabasePath(const std::string& path) { + if (!m_databasePath.empty()) { + return false; + } + m_databasePath = path; + return true; + } + + /* + * Get path to config file with certificate description. + */ + std::string getXMLConfigPath() { + return m_certificateXMLConfigPath; + } + + /* + * Get path to schema of config file. + */ + std::string getXMLSchemaPath() { + return m_certificateXMLSchemaPath; + } + + /* + * Get path to database with OCSP/CRL Cache. + */ + std::string getDatabasePath() { + return m_databasePath; + } + +private: + std::string m_certificateXMLConfigPath; + std::string m_certificateXMLSchemaPath; + std::string m_databasePath; +}; + +typedef DPL::Singleton ConfigSingleton; + +} // namespace ValidationCore + +#endif // _SRC_VALIDATION_CORE_VALIDATION_CORE_CONFIG_H_ + diff --git a/modules/vcore/src/vcore/Database.cpp b/modules/vcore/src/vcore/Database.cpp new file mode 100644 index 0000000..6d363af --- /dev/null +++ b/modules/vcore/src/vcore/Database.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file webruntime_database.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file contains the definition of webruntime database + */ +#include "Database.h" + +#include +#include +#include + +#include "Config.h" + +const char* VCoreDatabaseConnectionTraits::Address() +{ + /* This function should be fixed but some changes in + * dpl are required. + */ +// return ValidationCore::VCConfigSingleton::Instance(). +// getDatabasePath().c_str(); + + LogWarning( + "VCore will use hardcoded path for database: /opt/dbspace/.vcore.db"); + return "/opt/dbspace/.vcore.db"; +} + +DPL::Mutex g_vcoreDbQueriesMutex; diff --git a/modules/vcore/src/vcore/Database.h b/modules/vcore/src/vcore/Database.h new file mode 100644 index 0000000..7c30bc1 --- /dev/null +++ b/modules/vcore/src/vcore/Database.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file webruntime_database.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of webruntime database + */ +#ifndef VCORE_SRC_VCORE_DATABASE_H +#define VCORE_SRC_VCORE_DATABASE_H + +#include +#include +#include +#include + +struct VCoreDatabaseConnectionTraits +{ + static const char *Address(); + + static DPL::DB::SqlConnection::Flag::Type Flags() + { + return DPL::DB::SqlConnection::Flag::UseLucene; + } +}; + +extern DPL::Mutex g_vcoreDbQueriesMutex; + +#define VCORE_DB_INTERNAL(tlsCommand, InternalType, interface) \ + static DPL::ThreadLocalVariable *tlsCommand ## Ptr = NULL; \ + { \ + DPL::Mutex::ScopedLock lock(&g_vcoreDbQueriesMutex); \ + if (!tlsCommand ## Ptr) { \ + static DPL::ThreadLocalVariable tmp; \ + tlsCommand ## Ptr = &tmp; \ + } \ + } \ + DPL::ThreadLocalVariable &tlsCommand = *tlsCommand ## Ptr; \ + if (tlsCommand.IsNull()) { tlsCommand = InternalType(interface); } + +#define VCORE_DB_SELECT(name, type, interface) \ + VCORE_DB_INTERNAL(name, type::Select, interface) + +#define VCORE_DB_INSERT(name, type, interface) \ + VCORE_DB_INTERNAL(name, type::Insert, interface) + +#define VCORE_DB_UPDATE(name, type, interface) \ + VCORE_DB_INTERNAL(name, type::Update, interface) + +#define VCORE_DB_DELETE(name, type, interface) \ + VCORE_DB_INTERNAL(name, type::Delete, interface) + +#endif // define VCORE_SRC_VCORE_DATABASE_H diff --git a/modules/vcore/src/vcore/DeveloperModeValidator.cpp b/modules/vcore/src/vcore/DeveloperModeValidator.cpp new file mode 100644 index 0000000..7c30d49 --- /dev/null +++ b/modules/vcore/src/vcore/DeveloperModeValidator.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file DeveloperModeValidator.cpp + * @author Bartosz Janiak (b.janiak@samsung.com) + * @version 1.0 + * @brief DeveloperModeValidatorValidator - implementing WAC 2.0 spec, including TargetRestriction + */ + +#include "DeveloperModeValidator.h" +#include +#include +#include +#include +#include + +namespace ValidationCore { + +DeveloperModeValidator::DeveloperModeValidator(bool complianceMode, + const std::string& fakeIMEI, + const std::string& fakeMEID): + m_complianceModeEnabled(complianceMode), + m_fakeIMEI(fakeIMEI), + m_fakeMEID(fakeMEID) +{ +} + +void DeveloperModeValidator::check(const SignatureData &data) +{ + LogDebug("entered"); + const SignatureData::IMEIList& IMEIList = data.getIMEIList(); + const SignatureData::MEIDList& MEIDList = data.getMEIDList(); + + if (IMEIList.empty() && MEIDList.empty()) { + LogDebug("No TargetRestriction in signature."); + return; + } + + if (!IMEIList.empty()) { + std::string phoneIMEIString = m_fakeIMEI; + if (!m_complianceModeEnabled) { + LogDebug("Compilance Mode is not enabled"); + DPL::ScopedFree phoneIMEI( + vconf_get_str(VCONFKEY_TELEPHONY_IMEI)); + if (!phoneIMEI.Get()) { + ThrowMsg(Exception::NoTargetRestrictionSatisfied, + "Unable to get phone IMEI from vconf."); + } + phoneIMEIString = phoneIMEI.Get(); + } + + LogDebug("Phone IMEI: " << phoneIMEIString); + if (IMEIList.end() == + std::find(IMEIList.begin(), IMEIList.end(), phoneIMEIString)) + { + Throw(Exception::NoTargetRestrictionSatisfied); + } + } + + if (!MEIDList.empty()) { + std::string phoneMEIDString = m_fakeMEID; + if (!m_complianceModeEnabled) + { + TelMiscSNInformation phoneInfo; + if (TAPI_API_SUCCESS != + tel_get_misc_me_sn(TAPI_MISC_ME_MEID, &phoneInfo)) + { + ThrowMsg(Exception::NoTargetRestrictionSatisfied, + "Unable to get phone MEID from Tapi service."); + } + phoneMEIDString = reinterpret_cast(phoneInfo.szNumber); + } + + LogDebug("Phone MEID: " << phoneMEIDString); + if (MEIDList.end() == + std::find(MEIDList.begin(), MEIDList.end(), phoneMEIDString)) + { + Throw(Exception::NoTargetRestrictionSatisfied); + } + } + LogDebug("exit: ok"); +} + +} //ValidationCore diff --git a/modules/vcore/src/vcore/DeveloperModeValidator.h b/modules/vcore/src/vcore/DeveloperModeValidator.h new file mode 100644 index 0000000..a99076c --- /dev/null +++ b/modules/vcore/src/vcore/DeveloperModeValidator.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file DeveloperModeValidator.h + * @author Bartosz Janiak (b.janiak@samsung.com) + * @version 1.0 + * @brief DeveloperModeValidatorValidator - implementing WAC 2.0 spec, including TargetRestriction + */ + +#ifndef \ + WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_DEVELOPER_MODE_VALIDATOR_H +#define \ + WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_DEVELOPER_MODE_VALIDATOR_H + +#include +#include +#include "SignatureData.h" + +namespace ValidationCore { + +class DeveloperModeValidator +{ + public: + explicit DeveloperModeValidator(bool complianceMode = false, + const std::string &fakeIMEI = "", + const std::string &fakeMEID = ""); + + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, UnableToLoadTestCertificate) + DECLARE_EXCEPTION_TYPE(Base, NoTargetRestrictionSatisfied) + }; + + void check(const SignatureData &data); + private: + bool m_complianceModeEnabled; + std::string m_fakeIMEI; + std::string m_fakeMEID; +}; + +} +#endif /* WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_DEVELOPER_MODE_VALIDATOR_H */ + diff --git a/modules/vcore/src/vcore/IAbstractResponseCache.h b/modules/vcore/src/vcore/IAbstractResponseCache.h new file mode 100644 index 0000000..38a6fa8 --- /dev/null +++ b/modules/vcore/src/vcore/IAbstractResponseCache.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * + * @file AbstractResponseCache.h + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 0.1 + * @brief Common interface for OCSP/CRL caches + */ + +#ifndef _SRC_VALIDATION_CORE_IABSTRACT_RESPONSE_CACHE_H_ +#define _SRC_VALIDATION_CORE_IABSTRACT_RESPONSE_CACHE_H_ + +#include "Certificate.h" +#include "CertificateCollection.h" +#include "VerificationStatus.h" + +namespace ValidationCore { + +class IAbstractResponseCache { + public: + virtual VerificationStatus check(const CertificateCollection &certs) = 0; + virtual VerificationStatus checkEndEntity(CertificateCollection &certs) = 0; + virtual void updateCache() = 0; + + virtual ~IAbstractResponseCache() + { + } +}; + +} // namespace ValidationCore + +#endif /* _SRC_VALIDATION_CORE_IABSTRACT_RESPONSE_CACHE_H_ */ diff --git a/modules/vcore/src/vcore/OCSP.cpp b/modules/vcore/src/vcore/OCSP.cpp new file mode 100644 index 0000000..d276b87 --- /dev/null +++ b/modules/vcore/src/vcore/OCSP.cpp @@ -0,0 +1,538 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @author Tomasz Morawski(t.morawski@samsung.com) + * @author Michal Ciepielski(m.ciepielski@samsung.com) + * @author Piotr Marcinkiewicz(p.marcinkiew@samsung.com) + * @version 0.4 + * @file OCPS.cpp + * @brief Routines for certificate validation over OCSP + */ + +#include "OCSP.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "Certificate.h" +#include "SoupMessageSendSync.h" + +extern "C" { +// This function is needed to fix "Invalid conversion from void* +// to unsigned char*" C++ compiler error during calling +// i2d_OCSP_REQUEST_bio macro + extern bool convertToBuffer(OCSP_REQUEST* req, + char** buf, + int* size); +} + +namespace { +const int ConnectionTimeoutInSeconds = 6; +const int ConnectionRetryCount = 5; + +//! Maximum leeway in validity period in seconds: default 1 day +//! (@see checkRevocationStatus function code) + +//! Maximum validity time for revocation status (1 day) +const int MaxValidatyPeriodInSeconds = 24 * 60 * 60; + +//! Max age (@see checkRevocationStatus function code) +const int MaxAge = -1; +} + +namespace ValidationCore { + +const char* OCSP::DEFAULT_RESPONDER_URI_ENV = "OCSP_DEFAULT_RESPONDER_URI"; + +OCSP::DigestAlgorithmMap createDigestAlgMap() +{ + OCSP::DigestAlgorithmMap mDigestAlg = OCSP::DigestAlgorithmMap(); + + mDigestAlg.insert(std::make_pair(OCSP::SHA1, EVP_sha1())); + mDigestAlg.insert(std::make_pair(OCSP::SHA224, EVP_sha224())); + mDigestAlg.insert(std::make_pair(OCSP::SHA256, EVP_sha256())); + mDigestAlg.insert(std::make_pair(OCSP::SHA384, EVP_sha384())); + mDigestAlg.insert(std::make_pair(OCSP::SHA512, EVP_sha512())); + + return mDigestAlg; +} + +OCSP::DigestAlgorithmMap OCSP::m_sDigestAlgMap = createDigestAlgMap(); + +OCSP::OCSP() : + /* Upgrade of openssl is required to support sha256 */ + // m_pCertIdDigestAlg(EVP_sha256()), + // m_pRequestDigestAlg(EVP_sha256()), + m_pCertIdDigestAlg(EVP_sha1()), + m_pRequestDigestAlg(EVP_sha1()), + m_bUseNonce(false), + m_bUseDefResponder(false), + m_bSignRequest(false), + m_pSignKey(0) +{ +} + +SoupWrapper::SoupMessageSendBase::RequestStatus OCSP::sendOcspRequest( + OCSP_REQUEST* argRequest, + const DPL::OptionalString& argUri) +{ + using namespace SoupWrapper; + // convert OCSP_REQUEST to memory buffer + std::string url = DPL::ToUTF8String(*argUri); + char* requestBuffer; + int requestSizeInt; + if (!convertToBuffer(argRequest, &requestBuffer, &requestSizeInt)) { + ThrowMsg(OCSP::Exception::VerificationError, + "OCSP: failed to convert OCSP_REQUEST to mem buffer"); + } + + Assert(requestSizeInt >= 0); + + SoupMessageSendBase::MessageBuffer buffer; + buffer.resize(requestSizeInt); + memcpy(&buffer[0], requestBuffer, requestSizeInt); + free(requestBuffer); + + char *cport = 0,*chost = 0,*cpath = 0; + int use_ssl = 0; + + if (!OCSP_parse_url(const_cast(url.c_str()), + &chost, + &cport, + &cpath, + &use_ssl)) + { + LogWarning("Error in OCSP_parse_url"); + return SoupMessageSendBase::REQUEST_STATUS_CONNECTION_ERROR; + } + + std::string host = chost; + + if (cport) { + host += ":"; + host += cport; + } + + free(cport); + free(chost); + free(cpath); + + m_soupMessage.setHost(url); + m_soupMessage.setHeader("Host", host); + m_soupMessage.setRequest(std::string("application/ocsp-request"), + buffer); + + return m_soupMessage.sendSync(); +} + +ValidationCore::VerificationStatusSet OCSP::validateCertificateList( + const CertificateList &certs) +{ + VerificationStatusSet statusSet; + + if (certs.size() < 2) { + // no certificates to verify, just return a error + LogWarning("No validation will be proceed. OCSP require at" + " least 2 certificates in chain. Found only " << + certs.size()); + statusSet.add(VERIFICATION_STATUS_ERROR); + return statusSet; + } + + CertificateList::const_iterator iter = certs.begin(); + CertificateList::const_iterator parent = iter; + + time_t minValidity = 0; + for (++parent; parent != certs.end(); ++iter, ++parent) { + LogDebug("Certificate validation (CN:" << + (*iter)->getCommonName() << ")"); + statusSet.add(validateCertificate(*iter, *parent)); + if ((0 == minValidity || minValidity > m_responseValidity) && + m_responseValidity > 0) + { + minValidity = m_responseValidity; + } + } + m_responseValidity = minValidity; + + return statusSet; +} + +VerificationStatus OCSP::checkEndEntity( + const CertificateCollection &chain) +{ + const char *defResponderURI = getenv(OCSP::DEFAULT_RESPONDER_URI_ENV); + + VerificationStatusSet verSet; + if (defResponderURI) { + setUseDefaultResponder(true); + setDefaultResponder(defResponderURI); + } + + // this is temporary fix. it must be rewriten + CertificateList clst; + if (chain.isChain() && chain.size() >= 2) { + CertificateList::const_iterator icert = chain.begin(); + clst.push_back(*icert); + ++icert; + clst.push_back(*icert); + } + verSet += validateCertificateList(clst); + + return verSet.convertToStatus(); +} + +VerificationStatus OCSP::validateCertificate(CertificatePtr argCert, + CertificatePtr argIssuer) +{ + using namespace SoupWrapper; + + Assert(!!argCert); + Assert(!!argIssuer); + + Try { + DPL::OptionalString uri; + + if (!m_bUseDefResponder) { + uri = argCert->getOCSPURL(); + if (!uri) { + return VERIFICATION_STATUS_NOT_SUPPORT; + } + } else { + if (m_strResponderURI.empty()) { + ThrowMsg(Exception::VerificationError, + "Default responder is not set"); + } + LogWarning("Default responder will be used"); + + uri = m_strResponderURI; + } + + // creates a request + CreateRequestResult newRequest = createRequest(argCert, argIssuer); + if (!newRequest.success) { + ThrowMsg(Exception::VerificationError, "Request creation failed"); + } + + // SSLSmartContainer certIdCont(certId); + // this smart ptr is commented out in purpose. request + // manages certIdmemory (which was done in createRequest above) + SSLSmartContainer requestCont(newRequest.ocspRequest); + + SoupMessageSendBase::RequestStatus requestStatus; + requestStatus = sendOcspRequest(requestCont, uri); + + if (requestStatus != SoupMessageSendBase::REQUEST_STATUS_OK) { + return VERIFICATION_STATUS_CONNECTION_FAILED; + } + + // Response is m_soupMessage, convert it to OCSP_RESPONSE + OcspResponse response = convertToResponse(); + + if (!response.first) { + ThrowMsg(OCSP::Exception::VerificationError, + "OCSP: failed to convert mem buffer to OCSP_RESPONSE"); + } + + SSLSmartContainer responseCont(response.second); + // verify response eg. check response status, + // validate responder certificate + validateResponse(requestCont, + responseCont, + newRequest.ocspCertId); + } Catch(Exception::ConnectionError) { + LogWarning("OCSP: ConnectionError"); + return VERIFICATION_STATUS_CONNECTION_FAILED; + } Catch(Exception::CertificateRevoked) { + LogWarning("OCSP: Revoked"); + return VERIFICATION_STATUS_REVOKED; + } Catch(Exception::CertificateUnknown) { + LogWarning("OCSP: Unknown"); + return VERIFICATION_STATUS_UNKNOWN; + } Catch(Exception::VerificationError) { + LogWarning("OCSP: Verification error"); + return VERIFICATION_STATUS_VERIFICATION_ERROR; + } Catch(Exception::Base) { + LogWarning("OCSP: Error"); + return VERIFICATION_STATUS_ERROR; + } + LogWarning("OCSP: Good"); + return VERIFICATION_STATUS_GOOD; +} + +OCSP::CreateRequestResult OCSP::createRequest(CertificatePtr argCert, + CertificatePtr argIssuer) +{ + OCSP_REQUEST* newRequest = OCSP_REQUEST_new(); + + if (!newRequest) { + LogWarning("OCSP: Failed to create a request"); + return CreateRequestResult(); + } + + SSLSmartContainer requestCont(newRequest); + + OCSP_CERTID* certId = addSerial(argCert, argIssuer); + + if (!certId) { + LogWarning("OCSP: Unable to create a serial id"); + return CreateRequestResult(); + } + SSLSmartContainer certIdCont(certId); + + // Inserting certificate ID to request + if (!OCSP_request_add0_id(requestCont, certIdCont)) { + LogWarning("OCSP: Unable to create a certificate id"); + return CreateRequestResult(); + } + + if (m_bUseNonce) { + OCSP_request_add1_nonce(requestCont, 0, -1); + } + + if (m_bSignRequest) { + if (!m_pSignCert || !m_pSignKey) { + LogWarning("OCSP: Unable to sign request if " + "SignCert or SignKey was not set"); + return CreateRequestResult(); + } + + if (!OCSP_request_sign(requestCont, + m_pSignCert->getX509(), + m_pSignKey, + m_pRequestDigestAlg, + 0, + 0)) + { + LogWarning("OCSP: Unable to sign request"); + return CreateRequestResult(); + } + } + return CreateRequestResult(true, + requestCont.DetachPtr(), + certIdCont.DetachPtr()); +} + +OCSP_CERTID* OCSP::addSerial(CertificatePtr argCert, + CertificatePtr argIssuer) +{ + X509_NAME* iname = X509_get_subject_name(argIssuer->getX509()); + ASN1_BIT_STRING* ikey = X509_get0_pubkey_bitstr(argIssuer->getX509()); + ASN1_INTEGER* serial = X509_get_serialNumber(argCert->getX509()); + + return OCSP_cert_id_new(m_pCertIdDigestAlg, iname, ikey, serial); +} + +void OCSP::setDigestAlgorithmForCertId(DigestAlgorithm alg) +{ + DigestAlgorithmMap::const_iterator cit = m_sDigestAlgMap.find(alg); + + if (cit != m_sDigestAlgMap.end()) { + m_pCertIdDigestAlg = cit->second; + } else { + LogDebug("Request for unsupported CertId digest algorithm" + "ignored!"); + } +} + +void OCSP::setDigestAlgorithmForRequest(DigestAlgorithm alg) +{ + DigestAlgorithmMap::const_iterator cit = m_sDigestAlgMap.find(alg); + + if (cit != m_sDigestAlgMap.end()) { + m_pRequestDigestAlg = cit->second; + } else { + LogDebug("Request for unsupported OCSP request digest algorithm" + "ignored!"); + } +} + +void OCSP::setTrustedStore(const CertificateList& certs) +{ + X509_STORE *store = X509_STORE_new(); + m_pTrustedStore = store; + // create a trusted store basing on certificate chain from a signature + FOREACH(iter, certs) { + X509_STORE_add_cert(store, (*iter)->getX509()); + } +} + +void OCSP::validateResponse(OCSP_REQUEST* argRequest, + OCSP_RESPONSE* argResponse, + OCSP_CERTID* argCertId) +{ + int result = OCSP_response_status(argResponse); + + if (result != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + handleInvalidResponse(result); + ThrowMsg(Exception::VerificationError, "OCSP_response_status failed"); + } + + // get response object + OCSP_BASICRESP* basic = OCSP_response_get1_basic(argResponse); + if (!basic) { + ThrowMsg(Exception::VerificationError, + "OCSP: Unable to get a BASICRESP object."); + } + + SSLSmartContainer basicRespCont(basic); + if (m_bUseNonce && OCSP_check_nonce(argRequest, basicRespCont) <= 0) { + ThrowMsg(Exception::VerificationError, "OCSP: Invalid nonce"); + } + + if (!verifyResponse(basic)) { + ThrowMsg(Exception::VerificationError, + "Unable to verify the OCSP responder's certificate"); + } + + checkRevocationStatus(basicRespCont, argCertId); +} + +bool OCSP::verifyResponse(OCSP_BASICRESP* basic) +{ + Assert(m_pTrustedStore); + // verify ocsp response + int response = OCSP_basic_verify(basic, NULL, m_pTrustedStore, 0); + if (response <= 0) { + LogWarning("OCSP verification failed"); + } + + return response > 0; +} + +void OCSP::checkRevocationStatus(OCSP_BASICRESP* basic, + OCSP_CERTID* id) +{ + ASN1_GENERALIZEDTIME* producedAt; + ASN1_GENERALIZEDTIME* thisUpdate; + ASN1_GENERALIZEDTIME* nextUpdate; + int reason; + int status; + + m_responseValidity = 0; + + if (!OCSP_resp_find_status(basic, + id, + &status, + &reason, + &producedAt, + &thisUpdate, + &nextUpdate)) + { + ThrowMsg(Exception::VerificationError, + "OCSP: Failed to find certificate status."); + } + + if (!OCSP_check_validity(thisUpdate, + nextUpdate, + MaxValidatyPeriodInSeconds, + MaxAge)) + { + ThrowMsg(Exception::VerificationError, + "OCSP: Failed to check certificate validate."); + } + + if (nextUpdate) { + asn1GeneralizedTimeToTimeT(nextUpdate,&m_responseValidity); + time_t now; + time(&now); + LogDebug("Time of next OCSP update got from server: " << + m_responseValidity); + LogDebug("Expires in: " << (m_responseValidity - now)); + LogDebug("Original: " << nextUpdate->data); + } + + switch (status) { + case V_OCSP_CERTSTATUS_GOOD: + return; + case V_OCSP_CERTSTATUS_REVOKED: + ThrowMsg(Exception::CertificateRevoked, "Certificate is Revoked"); + case V_OCSP_CERTSTATUS_UNKNOWN: + ThrowMsg(Exception::CertificateUnknown, "Certificate is Unknown"); + default: + Assert(false && "Invalid status"); + } +} + +OCSP::OcspResponse OCSP::convertToResponse() +{ + using namespace SoupWrapper; + + // convert memory buffer to ocsp response object + BUF_MEM res_bmem; + OCSP_RESPONSE* response; + + SoupMessageSendBase::MessageBuffer buffer = m_soupMessage.getResponse(); + + res_bmem.length = buffer.size(); + res_bmem.data = &buffer[0]; + res_bmem.max = buffer.size(); + + BIO* res_mem_bio = BIO_new(BIO_s_mem()); + BIO_set_mem_buf(res_mem_bio, &res_bmem, BIO_NOCLOSE); + + response = d2i_OCSP_RESPONSE_bio(res_mem_bio, NULL); + BIO_free_all(res_mem_bio); + + if (!response) { + LogWarning("OCSP: Failed to convert OCSP Response to DER format"); + return std::make_pair(false, static_cast(NULL)); + } + + return std::make_pair(true, response); +} + +void OCSP::handleInvalidResponse(int result) +{ + switch (result) { + case OCSP_RESPONSE_STATUS_MALFORMEDREQUEST: + LogWarning("OCSP: Server returns " + "OCSP_RESPONSE_STATUS_MALFORMEDREQUEST status"); + break; + case OCSP_RESPONSE_STATUS_INTERNALERROR: + LogWarning("OCSP: Server returns " + "OCSP_RESPONSE_STATUS_INTERNALERROR status"); + break; + case OCSP_RESPONSE_STATUS_TRYLATER: + LogWarning("OCSP: Server returns " + "OCSP_RESPONSE_STATUS_TRYLATER status"); + break; + case OCSP_RESPONSE_STATUS_SIGREQUIRED: + LogWarning("OCSP: Server returns " + "OCSP_RESPONSE_STATUS_SIGREQUIRED status"); + break; + case OCSP_RESPONSE_STATUS_UNAUTHORIZED: + LogWarning("OCSP: Server returns " + "OCSP_RESPONSE_STATUS_UNAUTHORIZED status"); + break; + default: + Assert(false && "Invalid result value"); + } +} +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/OCSP.h b/modules/vcore/src/vcore/OCSP.h new file mode 100644 index 0000000..5f60da0 --- /dev/null +++ b/modules/vcore/src/vcore/OCSP.h @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @author Tomasz Morawski(t.morawski@samsung.com) + * @author Michal Ciepielski(m.ciepielski@samsung.com) + * @author Piotr Marcinkiewicz(p.marcinkiew@samsung.com) + * @version 0.4 + * @file OCPS.h + * @brief Routines for certificate validation over OCSP + */ + +#ifndef WRT_ENGINE_SRC_VALIDATION_CORE_ENGINE_OCSP_H_ +#define WRT_ENGINE_SRC_VALIDATION_CORE_ENGINE_OCSP_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "OCSPCertMgrUtil.h" +#include "CertificateCollection.h" +#include "CertificateStorage.h" +#include "VerificationStatus.h" +#include "SSLContainers.h" + +#include "SoupMessageSendBase.h" +#include "SoupMessageSendSync.h" +/* + * The WRT MUST NOT allow installation of widgets with revoked signatures. + * + * The WRT MUST NOT allow use of widgets with revoked signatures. + * + * The WRT MUST support checking for revocation of widget signatures via + * OCSP [RFC 2560] at widget installation time, according to the following: + * + * At widget installation time, the WRT shall make several attempts + * (5 attempts at 6 seconds apart recommended) to establish contact with + * the OCSP server. + * + * If connectivity is successful and the application is validated, the + * installation process shall continue. + * + * If connectivity is successful and if the widget signature is + * determined to be revoked, the WRT shall issue a suitable error message + * and cancel installation. + * + * If connectivity is successful and revocation status is unknown or if + * connectivity is unsuccessful, the user must be notified that the + * widget was unable to be installed as trusted - the certification of + * the widget signature has not been validated -, and prompt the user to allow + * the user to install the widget as an untrusted application, or reject + * the installation. + * + * The WRT MUST support checking for revocation of widget signatures via OCSP + * [RFC 2560] at widget runtime. + * + * The WRT MUST support OCSP access policy. + */ + +namespace ValidationCore { + +class OCSP +// : public RevocationCheckerBase +{ + public: + static const char* DEFAULT_RESPONDER_URI_ENV; + + VerificationStatus checkEndEntity(const CertificateCollection &certList); + OCSP(); + + enum DigestAlgorithm + { + SHA1, + SHA224, + SHA256, + SHA384, + SHA512 + }; + typedef std::map DigestAlgorithmMap; + /** + * Sets digest algorithm for certid in ocsp request + */ + void setDigestAlgorithmForCertId(DigestAlgorithm alg); + + /** + * Sets digest algorithm for certid in ocsp request + */ + void setDigestAlgorithmForRequest(DigestAlgorithm alg); + + void setTrustedStore(const CertificateList& certs); + + VerificationStatusSet validateCertificateList(const CertificateList &certs); + + VerificationStatus validateCertificate(CertificatePtr argCert, + CertificatePtr argIssuer); + + void setDefaultResponder(const char* uri) + { + Assert(uri); + m_strResponderURI = DPL::FromUTF8String(uri); + } + + void setUseDefaultResponder(bool value) + { + m_bUseDefResponder = value; + } + + /** + * @return time when response will become invalid - for list of + * certificates, this is the minimum of all validities; value is + * valid only for not-revoked certificates (non error validation result) + */ + time_t getResponseValidity() + { + return m_responseValidity; + } + + private: + typedef WRT::ScopedGPointer ScopedSoupSession; + typedef WRT::ScopedGPointer ScopedSoupMessage; + + void handleInvalidResponse(int result); + void sendHTTPRequest(ScopedSoupSession& session, + ScopedSoupMessage& msg, + const char* host, + const char* port, + const char* path, + char* requestBuffer, + size_t reqestSize); + void sendRequest(const std::string& uri, + char* requestBuffer, + size_t requestSize, + char** responseBuffer, + size_t* responseSize); + + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, ConnectionError) + DECLARE_EXCEPTION_TYPE(Base, CertificateRevoked) + DECLARE_EXCEPTION_TYPE(Base, CertificateUnknown) + DECLARE_EXCEPTION_TYPE(Base, VerificationError) + DECLARE_EXCEPTION_TYPE(Base, RetrieveCertFromStoreError) + DECLARE_EXCEPTION_TYPE(Base, VerificationNotSupport) + }; + + const EVP_MD* m_pCertIdDigestAlg; + const EVP_MD* m_pRequestDigestAlg; + static DigestAlgorithmMap m_sDigestAlgMap; + + typedef std::pair HttpResponseBuffer; + + SoupWrapper::SoupMessageSendBase::RequestStatus sendOcspRequest( + OCSP_REQUEST* argRequest, + const DPL::OptionalString& argUri); + + //! Validates a single certificate + /*! + * @param cert The certificate to check + * @param issuer A certificate used to sign the certificate to check. + */ + + struct CreateRequestResult + { + bool success; + OCSP_REQUEST* ocspRequest; + OCSP_CERTID* ocspCertId; + CreateRequestResult(bool argSuccess = false, + OCSP_REQUEST* argOcspRequest = NULL, + OCSP_CERTID* argOcspCertId = NULL) : + success(argSuccess), + ocspRequest(argOcspRequest), + ocspCertId(argOcspCertId) + { + } + }; + + //! Creates a OCSP request + /*! + * @param request Returns created OCSP_REQUEST + * @param id Returns CertId that is used to find proper OCSP result in + * the OCSP response (@see checkRevocationStatus for more details). + * + */ + CreateRequestResult createRequest(CertificatePtr argCert, + CertificatePtr argIssuer); + + OCSP_CERTID* addSerial(CertificatePtr argCert, + CertificatePtr argIssuer); + + void validateResponse(OCSP_REQUEST* argRequest, + OCSP_RESPONSE* argResponse, + OCSP_CERTID* argCertId); + + //! Create a X509 store + bool verifyResponse(OCSP_BASICRESP* argResponse); + + void checkRevocationStatus(OCSP_BASICRESP* argBasicResponse, + OCSP_CERTID* argCertId); + + typedef std::pair OcspResponse; + + OcspResponse convertToResponse(); + + time_t m_responseValidity; + bool m_bUseNonce; + bool m_bUseDefResponder; + DPL::String m_strResponderURI; + bool m_bSignRequest; + EVP_PKEY* m_pSignKey; + CertificatePtr m_pSignCert; + SSLSmartContainer m_pTrustedStore; + SoupWrapper::SoupMessageSendSync m_soupMessage; +}; +} // ValidationCore + +#endif //ifndef WRT_ENGINE_SRC_VALIDATION_CORE_ENGINE_OCSP_H_ diff --git a/modules/vcore/src/vcore/OCSPCertMgrUtil.cpp b/modules/vcore/src/vcore/OCSPCertMgrUtil.cpp new file mode 100644 index 0000000..794ecfa --- /dev/null +++ b/modules/vcore/src/vcore/OCSPCertMgrUtil.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @author Michal Ciepielski(m.ciepielski@samsung.com) + * @version 0.3 + * @brief + */ + +#include "OCSPCertMgrUtil.h" +#include "SSLContainers.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { +const int MAX_BUF = 1024; + +struct ContextDeleter +{ + typedef CERT_CONTEXT* Type; + static Type NullValue() + { + return NULL; + } + static void Destroy(Type context) + { + if (context) { + cert_svc_cert_context_final(context); + } + } +}; +} + +namespace ValidationCore { +namespace OCSPCertMgrUtil { +/* + * TODO This API function should be changed to: + * CertifiatePtr getCertFromStore(const std::string &subject); + * + * All of cert_svc function could return error because input + * data are corruped. That's why I dont want to throw exceptions + * in this function. + */ +void getCertFromStore(X509_NAME *subject, + X509 **xcert) +{ + if (!xcert || *xcert || !subject) { + LogError("Invalid input!"); + return; + } + + typedef DPL::ScopedResource ScopedContext; + + int result; + char buffer[MAX_BUF]; + const unsigned char* ptr = NULL; + X509 *pCertificate = NULL; + cert_svc_filename_list *fileList = NULL; + + X509_NAME_oneline(subject, buffer, MAX_BUF); + + ScopedContext ctx(cert_svc_cert_context_init()); + if (ctx.Get() == NULL) { + LogWarning("Error in cert_svc_cert_context_init."); + return; + } + + LogDebug("Search certificate with subject: " << buffer); + result = cert_svc_search_certificate(ctx.Get(), SUBJECT_STR, buffer); + LogDebug("Search finished!"); + + if (CERT_SVC_ERR_NO_ERROR != result) { + LogWarning("Error during certificate search"); + return; + } + + fileList = ctx.Get()->fileNames; + + if (fileList == NULL) { + LogDebug("No certificate found"); + return; + } + + if (fileList->filename == NULL) { + LogWarning("Empty filename"); + return; + } + + LogDebug("Found cert file: " << fileList->filename); + ScopedContext ctx2(cert_svc_cert_context_init()); + + if (ctx2.Get() == NULL) { + LogWarning("Error in cert_svc_cert_context_init."); + return; + } + + // TODO add read_certifcate_from_file function to Certificate.h + if (CERT_SVC_ERR_NO_ERROR != + cert_svc_load_file_to_context(ctx2.Get(), fileList->filename)) { + LogWarning("Error in cert_svc_load_file_to_context"); + return; + } + + ptr = ctx2.Get()->certBuf->data; + // create a certificate from mem buff + pCertificate = d2i_X509(NULL, &ptr, ctx2.Get()->certBuf->size); + + if (pCertificate == NULL) { + LogWarning("Error during certificate conversion in d2i_X509"); + return; + } + + *xcert = pCertificate; + if (fileList->next != NULL) { + LogError("There is more then one certificate with same subject :/"); + // TODO Implement me. + for (fileList = fileList->next; + fileList != NULL; + fileList = fileList->next) { + LogError( + "Additional certificate with same subject: " << + fileList->filename); + } + } +} + +CertificatePtr getParentFromStore(const CertificatePtr &certificate) +{ + Assert(certificate.Get()); + X509* rawPtr = certificate->getX509(); + + /* TODO Add getIssuerName function to Certificate.h */ + X509_NAME *name = X509_get_issuer_name(rawPtr); + + X509* rawTemp = NULL; + getCertFromStore(name, &rawTemp); + + if (rawTemp == NULL) { + return CertificatePtr(); + } + + SSLSmartContainer scope(rawTemp); + return CertificatePtr(new Certificate(rawTemp)); +} + +CertificateList completeCertificateChain(const CertificateList &certificateList) +{ + CertificateList result = certificateList; + CertificatePtr last = result.back(); + if (last->isSignedBy(last)) { + return result; + } + CertificatePtr parent = getParentFromStore(last); + if (parent.Get()) { + result.push_back(parent); + } + return result; +} +} // namespace OCSPCertMgrUtil +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/OCSPCertMgrUtil.h b/modules/vcore/src/vcore/OCSPCertMgrUtil.h new file mode 100644 index 0000000..a93a42e --- /dev/null +++ b/modules/vcore/src/vcore/OCSPCertMgrUtil.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @author Tomasz Morawski(t.morawski@samsung.com) + * @author Michal Ciepielski(m.ciepielski@samsung.com) + * @version 0.2 + * @brief + */ + +#ifndef _WRT_OCSP_CERT_MGR_UTIL_H_ +#define _WRT_OCSP_CERT_MGR_UTIL_H_ + +#include + +#include "Certificate.h" + +namespace ValidationCore { +namespace OCSPCertMgrUtil { +void getCertFromStore(X509_NAME *subject, + X509 **xcert); +CertificatePtr getParentFromStore(const CertificatePtr &certificate); +/* + * Look for "parent" certificate from store. + * It returns new certificate chain. + */ +CertificateList completeCertificateChain(const CertificateList &certList); +} // namespace OCSPCertMgrUtil +} // namespace ValidationCore +#endif + diff --git a/modules/vcore/src/vcore/OCSPUtil.c b/modules/vcore/src/vcore/OCSPUtil.c new file mode 100644 index 0000000..451884a --- /dev/null +++ b/modules/vcore/src/vcore/OCSPUtil.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @author Tomasz Morawski(t.morawski@samsung.com) + * @version 0.1 + * @brief + */ + +#include + +/* + * This function is needed to fix "Invalid conversion from void* to unsigned char*" + * C++ compiler error during calling i2d_OCSP_REQUEST_bio macro + */ +int convertToBuffer(OCSP_REQUEST *req, char **buf, int *size) { + BIO *req_mem_bio; + BUF_MEM req_bmem; + + /* + * size and membuffer for request + */ + *size = i2d_OCSP_REQUEST(req, NULL); + *buf = (char*) malloc(*size); + + if (!*buf) + return 0; + + /* copy request into buffer */ + req_bmem.length = 0; + req_bmem.data = *buf; + req_bmem.max = *size; + + /* + * create a new buffer using openssl + */ + req_mem_bio = BIO_new(BIO_s_mem()); + + if (!req_mem_bio) { + /* + * creation failed, return + */ + free(*buf); + *buf = NULL; + return 0; + } + + BIO_set_mem_buf(req_mem_bio, &req_bmem, BIO_NOCLOSE); + + /* + * prepare request + */ + if (i2d_OCSP_REQUEST_bio(req_mem_bio, req) <= 0) { + free(*buf); + *buf = NULL; + BIO_free_all(req_mem_bio); + return 0; + } + + /* + * check consistency + */ + if (*size != ((int)req_bmem.length) || req_bmem.length != req_bmem.max) + { + free(*buf); + *buf = NULL; + BIO_free_all(req_mem_bio); + return 0; + } + + /* + * free all reserved memory + */ + BIO_free_all(req_mem_bio); + + /* + * and return success + */ + return 1; +} diff --git a/modules/vcore/src/vcore/ParserSchema.h b/modules/vcore/src/vcore/ParserSchema.h new file mode 100644 index 0000000..6fabff8 --- /dev/null +++ b/modules/vcore/src/vcore/ParserSchema.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file ParserSchema.h + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#ifndef _PARSERSCHEMA_H_ +#define _PARSERSCHEMA_H_ + +#include +#include + +#include + +#include "SaxReader.h" + +namespace ValidationCore { +namespace ParserSchemaException { +DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) +DECLARE_EXCEPTION_TYPE(Base, XmlReaderError) +DECLARE_EXCEPTION_TYPE(Base, CertificateLoaderError) +DECLARE_EXCEPTION_TYPE(Base, UnsupportedAlgorithm) +DECLARE_EXCEPTION_TYPE(Base, UnsupportedValue) +} + +template +class ParserSchema +{ + public: + struct TagDescription + { + TagDescription(const std::string &tag, + const std::string & xmlNamespace) : + tagName(tag), + namespaceUri(xmlNamespace) + { + } + + std::string tagName; + std::string namespaceUri; + + bool operator<(const TagDescription &second) const + { + if (tagName < second.tagName) { + return true; + } + if (tagName > second.tagName) { + return false; + } + if (namespaceUri < second.namespaceUri) { + return true; + } + return false; + } + }; + + ParserSchema(ParserType * parser) : + m_functions(parser) + { + } + + virtual ~ParserSchema() + { + } + + void initialize(const std::string &filename, + bool defaultArgs, + SaxReader::ValidationType valType, + const std::string &xmlschema) + { + Try + { + m_reader.initialize(filename, defaultArgs, valType, xmlschema); + } + Catch(SaxReader::Exception::Base) + { + ReThrowMsg(ParserSchemaException::XmlReaderError, "XmlReaderError"); + } + } + + void deinitialize() + { + m_reader.deinitialize(); + } + + void read(DataType &dataContainer) + { + Try { + while (m_reader.next()) { + switch (m_reader.type()) { + case SaxReader::NODE_BEGIN: + beginNode(dataContainer); + break; + case SaxReader::NODE_END: + endNode(dataContainer); + break; + case SaxReader::NODE_TEXT: + textNode(dataContainer); + break; + default: + // LogInfo("Unknown Type Node"); + break; + } + } + } + Catch(SaxReader::Exception::Base) + { + ReThrowMsg(ParserSchemaException::XmlReaderError, "XmlReaderError"); + } + } + + typedef void (ParserType::*FunctionPtr)(DataType &data); + typedef std::map FunctionMap; + + void addBeginTagCallback(const std::string &tag, + const std::string &namespaceUri, + FunctionPtr function) + { + TagDescription desc(tag, namespaceUri); + m_beginFunctionMap[desc] = function; + } + + void addEndTagCallback(const std::string &tag, + const std::string &namespaceUri, + FunctionPtr function) + { + TagDescription desc(tag, namespaceUri); + m_endFunctionMap[desc] = function; + } + + SaxReader& getReader(void) + { + return m_reader; + } + + std::string& getText(void) + { + return m_textNode; + } + + protected: + void beginNode(DataType &dataContainer) + { + TagDescription desc(m_reader.name(), m_reader.namespaceURI()); + FunctionPtr fun = m_beginFunctionMap[desc]; + + if (fun == 0) { + LogDebug("No function found for xml tag: " << m_reader.name()); + return; + } + + (m_functions->*fun)(dataContainer); + } + + void endNode(DataType &dataContainer) + { + TagDescription desc(m_reader.name(), m_reader.namespaceURI()); + FunctionPtr fun = m_endFunctionMap[desc]; + + if (fun == 0) { + LogDebug("No function found for xml tag: " << m_reader.name()); + return; + } + + (m_functions->*fun)(dataContainer); + } + + void textNode(DataType &dataContainer) + { + (void)dataContainer; + m_textNode = m_reader.value(); + } + + ParserType *m_functions; + + SaxReader m_reader; + FunctionMap m_beginFunctionMap; + FunctionMap m_endFunctionMap; + + // temporary values require due parsing textNode + std::string m_textNode; +}; +} // namespace ValidationCore +#endif diff --git a/modules/vcore/src/vcore/ReferenceValidator.cpp b/modules/vcore/src/vcore/ReferenceValidator.cpp new file mode 100644 index 0000000..d56eea8 --- /dev/null +++ b/modules/vcore/src/vcore/ReferenceValidator.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include +#include + +#include "Base64.h" +#include "ReferenceValidator.h" + +namespace { +const char *SPECIAL_SYMBOL_CURRENT_DIR = "."; +const char *SPECIAL_SYMBOL_UPPER_DIR = ".."; +const char *SPECIAL_SYMBOL_AUTHOR_SIGNATURE_FILE = "author-signature.xml"; +const char *REGEXP_DISTRIBUTOR_SIGNATURE = "^signature[1-9][0-9]*\\.xml"; +} // namespace anonymous + +namespace ValidationCore { +ReferenceValidator::ReferenceValidator(const std::string &dirpath) : + m_dirpath(dirpath), + m_signatureRegexp(REGEXP_DISTRIBUTOR_SIGNATURE) +{ +} + +ReferenceValidator::Result ReferenceValidator::checkReferences( + const SignatureData &signatureData) +{ + return dfsCheckDirectories(signatureData, std::string()); +} + +ReferenceValidator::Result ReferenceValidator::dfsCheckDirectories( + const SignatureData &signatureData, + const std::string &directory) +{ + DIR *dp; + struct dirent *dirp; + std::string currentDir = m_dirpath + directory; + + if ((dp = opendir(currentDir.c_str())) == NULL) { + LogError("Error opening directory: " << currentDir.c_str()); + m_errorDescription = currentDir; + return ERROR_OPENING_DIR; + } + + for (errno = 0; (dirp = readdir(dp)) != NULL; errno = 0) { + if (!strcmp(dirp->d_name, SPECIAL_SYMBOL_CURRENT_DIR)) { + continue; + } + + if (!strcmp(dirp->d_name, SPECIAL_SYMBOL_UPPER_DIR)) { + continue; + } + + if (currentDir == m_dirpath && dirp->d_type == DT_REG && + !strcmp(dirp->d_name, + SPECIAL_SYMBOL_AUTHOR_SIGNATURE_FILE) && + signatureData.isAuthorSignature()) { + continue; + } + + if (currentDir == m_dirpath && dirp->d_type == DT_REG && + isDistributorSignature(dirp->d_name)) { + continue; + } + + if (dirp->d_type == DT_DIR) { + LogDebug("Open directory: " << (directory + dirp->d_name)); + std::string tmp_directory = directory + dirp->d_name + "/"; + Result result = dfsCheckDirectories(signatureData, tmp_directory); + if (result != NO_ERROR) { + closedir(dp); + return result; + } + } else if (dirp->d_type == DT_REG) { + LogDebug("Found file: " << (directory + dirp->d_name)); + const ReferenceSet &referenceSet = signatureData.getReferenceSet(); + if (referenceSet.end() == + referenceSet.find(directory + dirp->d_name)) { + closedir(dp); + m_errorDescription = directory + dirp->d_name; + return ERROR_REFERENCE_NOT_FOUND; + } + } else { + LogError("Unknown file type."); + closedir(dp); + m_errorDescription = directory + dirp->d_name; + return ERROR_UNSUPPORTED_FILE_TYPE; + } + } + + if (errno != 0) { + m_errorDescription = DPL::GetErrnoString(); + LogError("readdir failed. Errno code: " << errno << + " Description: " << m_errorDescription); + closedir(dp); + return ERROR_READING_DIR; + } + + closedir(dp); + + return NO_ERROR; +} +} diff --git a/modules/vcore/src/vcore/ReferenceValidator.h b/modules/vcore/src/vcore/ReferenceValidator.h new file mode 100644 index 0000000..5f05095 --- /dev/null +++ b/modules/vcore/src/vcore/ReferenceValidator.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _REFERENCEVALIDATOR_H_ +#define _REFERENCEVALIDATOR_H_ + +#include + +#include "SignatureData.h" + +namespace ValidationCore { +class ReferenceValidator +{ + public: + enum Result + { + NO_ERROR = 0, + ERROR_OPENING_DIR, + ERROR_READING_DIR, + ERROR_UNSUPPORTED_FILE_TYPE, + ERROR_REFERENCE_NOT_FOUND + }; + + ReferenceValidator(const std::string &dirpath); + + virtual ~ReferenceValidator() + { + } + + Result checkReferences(const SignatureData &signatureData); + + private: + + Result dfsCheckDirectories(const SignatureData &signatureData, + const std::string &directory); + + inline bool isDistributorSignature(const char *cstring) const + { + return m_signatureRegexp.FullMatch(cstring); + } + + std::string m_dirpath; + std::string m_errorDescription; + pcrecpp::RE m_signatureRegexp; +}; +} + +#endif // _REFERENCEVALIDATOR_H_ diff --git a/modules/vcore/src/vcore/RevocationCheckerBase.cpp b/modules/vcore/src/vcore/RevocationCheckerBase.cpp new file mode 100644 index 0000000..f0e43e7 --- /dev/null +++ b/modules/vcore/src/vcore/RevocationCheckerBase.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @author Piotr Marcinkiewicz(p.marcinkiew@samsung.com) + * @version 0.4 + * @file CommonCertValidator.cpp + * @brief Common routines for certificate validation over OCSP and CRL + */ + +#include "RevocationCheckerBase.h" + +#include + +#include + +#include + +#include "Certificate.h" +#include "CertificateCollection.h" + +namespace { +const char DefaultBundlePatch[] = "/opt/etc/ssl/certs/ca-certificates.crt"; +} //Anonymous name space + +namespace ValidationCore { +CertificatePtr RevocationCheckerBase::loadPEMFile(const char* fileName) +{ + DPL::ScopedFClose fd(fopen(fileName, "rb")); + + // no such file, return NULL + if (!fd.Get()) { + return CertificatePtr(); + } + + // create a new X509 certificate basing on file + CertificatePtr cert(new Certificate(PEM_read_X509(fd.Get(), + NULL, + NULL, + NULL))); + return cert; +} + +bool RevocationCheckerBase::sortCertList(CertificateList &lCertificates) +{ + CertificateCollection collection; + collection.load(lCertificates); + + if (collection.sort()) { + lCertificates = collection.getChain(); + return true; + } + return false; +} + +} // ValidationCore diff --git a/modules/vcore/src/vcore/RevocationCheckerBase.h b/modules/vcore/src/vcore/RevocationCheckerBase.h new file mode 100644 index 0000000..3ce934d --- /dev/null +++ b/modules/vcore/src/vcore/RevocationCheckerBase.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @author Piotr Marcinkiewicz(p.marcinkiew@samsung.com) + * @version 0.4 + * @file CommonCertValidator.h + * @brief Common routines for certificate validation over OCSP and CRL + */ + +#ifndef WRT_ENGINE_SRC_VALIDATION_CORE_REVOCATIONCHECKERBASE_H_ +#define WRT_ENGINE_SRC_VALIDATION_CORE_REVOCATIONCHECKERBASE_H_ + +#include + +#include "Certificate.h" + +namespace ValidationCore { +class RevocationCheckerBase +{ + public: + //! Loads a PEM file and returns X509 certificate object. + static CertificatePtr loadPEMFile(const char* path); + + //! Sorts a list of certficates and verifies them if they form + //! a valid chain + static bool sortCertList(CertificateList &cert) __attribute__((deprecated)); +}; +} // ValidationCore + +#endif //ifndef WRT_ENGINE_SRC_VALIDATION_CORE_REVOCATIONCHECKERBASE_H_ + diff --git a/modules/vcore/src/vcore/SSLContainers.h b/modules/vcore/src/vcore/SSLContainers.h new file mode 100644 index 0000000..e18cb00 --- /dev/null +++ b/modules/vcore/src/vcore/SSLContainers.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _SSLCONTAINERS_H +#define _SSLCONTAINERS_H + +#include +#include + +/* + * default deleter functor with no overloaded operator() + */ +template +struct MySSLFree {}; + +/* + * macro for defining custom deleters for openssl structs + * usage DECLARE_DELETER(OpenSSLType) + */ +#define DECLARE_DELETER(Type) template<> \ + struct MySSLFree \ + { \ + void operator() (Type* p) \ + { \ + Type ## _free(p); \ + } \ + \ + }; + +/* + * declare custom deleter for X509 structs + */ +DECLARE_DELETER(X509) +/* + * declare custom deleter for OCSP_REQUEST structs + */ +DECLARE_DELETER(OCSP_REQUEST) +/* + * declare custom deleter for OCSP_RESPONSE structs + */ +DECLARE_DELETER(OCSP_RESPONSE) +/* + * declare custom deleter for OCSP_CERTID structs + */ +DECLARE_DELETER(OCSP_CERTID) +/* + * declare custom deleter for OCSP_BASICRESP structs + */ +DECLARE_DELETER(OCSP_BASICRESP) +/* + * declare custom deleter for X509_STORE structs + */ +DECLARE_DELETER(X509_STORE) + +/* + * undef it, so anyone could use that macro name + */ +#undef DECLARE_DELETER + +/* + * OpenSSL smart container + * usage SSLSmartContainer smartptr = ptrToOpenSSLType + * remember to add OpenSSLType to macro list few lines above + */ +template > +class SSLSmartContainer +{ + public: + SSLSmartContainer() : m_pData(NULL) + { + } + + /* + * explicit constructor, we don't want any auto casting + */ + explicit SSLSmartContainer(T* pData) + { + m_pData = pData; + } + + /* + * overloaded assignment operator + */ + SSLSmartContainer & operator=(SSLSmartContainer& pContainer) + { + /* + * check if no assignment was done before + */ + if (this != &pContainer) { + // if so, free internal data + deleter ssl_free; + ssl_free(m_pData); + + // and assign new + m_pData = pContainer.m_pData; + + pContainer.m_pData = NULL; + } + + return *this; + } + + SSLSmartContainer & operator=(T* pData) + { + /* + * check if no assignment was done before + */ + if (m_pData != pData) { + // if so, free internal data + deleter ssl_free; + ssl_free(m_pData); + + // and assign new + m_pData = pData; + } + + return *this; + } + + ~SSLSmartContainer() + { + deleter ssl_free; + ssl_free(m_pData); + } + + /* + * overloaded operators for standardptr - like usage + */ + SSLSmartContainer & operator*() + { + return *m_pData; + } + SSLSmartContainer* operator->() + { + return m_pData; + } + + /* + * auto cast to T operator + */ + operator T *() const { return m_pData; + } + + /* + * detachs internal pointer from smart pointer + */ + T* DetachPtr() + { + T* pData = m_pData; + m_pData = NULL; + return pData; + } + + private: + /* + * blocked assignment from another types operator + */ + template + T & operator = (S& pContainer) + { + return *this; + } + + /* + * internal data + */ + T* m_pData; +}; + +#endif /* _SSLCONTAINERS_H */ + diff --git a/modules/vcore/src/vcore/SaxReader.cpp b/modules/vcore/src/vcore/SaxReader.cpp new file mode 100644 index 0000000..5bef911 --- /dev/null +++ b/modules/vcore/src/vcore/SaxReader.cpp @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file SaxReader.cpp + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief Simple c++ interface for libxml2. + */ +#include +#include +#include + +#include "SaxReader.h" + +namespace ValidationCore { +SaxReader::SaxReader() : + m_reader(0) +{ +} + +SaxReader::~SaxReader() +{ + if (m_reader) { + deinitialize(); + } +} + +void SaxReader::initialize(const std::string &filename, + bool defaultArgs, + ValidationType validate, + const std::string &schema) +{ + Assert(m_reader == 0 && "Double initialization of SaxReader"); + + LogDebug("SaxReader opening file: " << filename); + + /* + * create a new xml text reader + */ + m_reader = xmlNewTextReaderFilename(filename.c_str()); + + if (m_reader == NULL) { + /* + * no such file, return + */ + LogWarning("Error during opening file " << filename); + Throw(Exception::FileOpeningError); + } + if (validate == VALIDATION_XMLSCHEME && + xmlTextReaderSchemaValidate(m_reader, schema.c_str())) { + /* + * unable to turn on schema validation + */ + LogError("Turn on Schema validation failed."); + ThrowMsg(Exception::ParserInternalError, + "Turn on Scheme validation failed!"); + } + // Path to DTD schema is taken from xml file. + if (validate == VALIDATION_DTD && + xmlTextReaderSetParserProp(m_reader, XML_PARSER_VALIDATE, 1)) { + /* + * unable to turn on DTD validation + */ + LogError("Turn on DTD validation failed!"); + ThrowMsg(Exception::ParserInternalError, + "Turn on DTD validation failed!"); + } + if (defaultArgs && + xmlTextReaderSetParserProp(m_reader, XML_PARSER_DEFAULTATTRS, 1)) { + /* + * unable to turn on default arguments + */ + LogError("Turn on default arguments failed"); + ThrowMsg(Exception::ParserInternalError, + "Turn on Default Arguments failed!"); + } +} + +void SaxReader::deinitialize() +{ + xmlFreeTextReader(m_reader); + m_reader = 0; +} + +bool SaxReader::next() +{ + int res = xmlTextReaderRead(m_reader); + + if (0 == xmlTextReaderIsValid(m_reader)) { + LogWarning("Throw exception file not valid!"); + Throw(Exception::FileNotValid); + } + + if (res == 1) { + return true; + } + + if (res == 0) { + return false; + } + LogError("ParserInternalError"); + Throw(Exception::ParserInternalError); +} + +void SaxReader::next(const std::string &token) +{ + xmlTextReaderRead(m_reader); + if (0 == xmlTextReaderIsValid(m_reader)) { + /* + * invalid file + */ + LogWarning("Throw exception file not valid!"); + Throw(Exception::FileNotValid); + } + + xmlChar *name = xmlTextReaderName(m_reader); + + if (name == NULL) { + /* + * invalid file + */ + LogWarning("File not Valid"); + Throw(Exception::FileNotValid); + } + + if (token == reinterpret_cast(name)) { + xmlFree(name); + } else { + /* + * we encountered wrong token + */ + xmlFree(name); + LogWarning("Wrong Token"); + Throw(Exception::WrongToken); + } +} + +bool SaxReader::isEmpty(void) +{ + int ret = xmlTextReaderIsEmptyElement(m_reader); + if (-1 == ret) { + LogError("Parser Internal Error"); + Throw(Exception::ParserInternalErrorInEmptyQuery); + } + return ret; +} + +std::string SaxReader::attribute(const std::string &token, + ThrowType throwStatus) +{ + std::string value; + xmlChar *attr = xmlTextReaderGetAttribute(m_reader, BAD_CAST(token.c_str())); + if ((NULL == attr) && (throwStatus == THROW_DISABLE)) { + /* + * return empty string + */ + //TODO why not DPL::Optional? + return std::string(); + } + if (NULL == attr) { + /* + * error during read attribute + */ + LogError("Error in reading attribute."); + Throw(Exception::ParserInternalErrorInReadingAttribute); + } + + /* + * cast it to val and return it + */ + value = reinterpret_cast(attr); + xmlFree(attr); + return value; +} + +// KW std::string SaxReader::fullName(){ +// KW std::string value; +// KW xmlChar *name = xmlTextReaderName(m_reader); +// KW if(NULL == name) { +// KW LogError("Error in reading name."); +// KW Throw(Exception::ErrorReadingName); +// KW } +// KW value = reinterpret_cast(name); +// KW xmlFree(name); +// KW return value; +// KW } + +std::string SaxReader::name() +{ + std::string value; + xmlChar *name = xmlTextReaderName(m_reader); + if (NULL == name) { + LogError("Error in reading name."); + Throw(Exception::ErrorReadingName); + } + value = reinterpret_cast(name); + xmlFree(name); + size_t pos = value.find_last_of(":"); + if (pos != std::string::npos) { + value.erase(0, pos + 1); + } + return value; +} + +std::string SaxReader::namespaceURI() +{ + std::string value; + xmlChar *name = xmlTextReaderNamespaceUri(m_reader); + if (NULL != name) { + value = reinterpret_cast(name); + xmlFree(name); + } + return value; +} + +std::string SaxReader::value() +{ + std::string value; + /* + * get value of node + */ + xmlChar *text = xmlTextReaderValue(m_reader); + if (NULL == text) { + LogError("Error in reading value"); + Throw(Exception::ErrorReadingValue); + } + value = reinterpret_cast(text); + /* + * free text and return the val + */ + xmlFree(text); + return value; +} + +SaxReader::NodeType SaxReader::type() +{ + xmlReaderTypes type = + static_cast(xmlTextReaderNodeType(m_reader)); + switch (type) { + case XML_READER_TYPE_ELEMENT: + return NODE_BEGIN; + case XML_READER_TYPE_END_ELEMENT: + return NODE_END; + case XML_READER_TYPE_TEXT: + return NODE_TEXT; + case XML_READER_TYPE_NONE: + case XML_READER_TYPE_ATTRIBUTE: + case XML_READER_TYPE_CDATA: + case XML_READER_TYPE_ENTITY_REFERENCE: + case XML_READER_TYPE_ENTITY: + case XML_READER_TYPE_PROCESSING_INSTRUCTION: + case XML_READER_TYPE_COMMENT: + case XML_READER_TYPE_DOCUMENT: + case XML_READER_TYPE_DOCUMENT_TYPE: + case XML_READER_TYPE_DOCUMENT_FRAGMENT: + case XML_READER_TYPE_NOTATION: + case XML_READER_TYPE_WHITESPACE: + case XML_READER_TYPE_SIGNIFICANT_WHITESPACE: + case XML_READER_TYPE_END_ENTITY: + case XML_READER_TYPE_XML_DECLARATION: + default: + return NODE_UNSUPPORTED; + } +} + +void SaxReader::dumpNode(std::string &buffer) +{ + /* + * size of buffer + */ + int size; + /* + * pointer to buffer + */ + xmlBufferPtr buff = xmlBufferCreate(); + + xmlNodePtr node = xmlTextReaderExpand(m_reader); + + if (node == NULL) { + /* + * internal parser error + */ + xmlBufferFree(buff); + LogError("Parser Internal Error"); + Throw(Exception::ParserInternalError); + } + + /* + * get a size and fill in a buffer + */ + size = xmlNodeDump(buff, node->doc, node, 0, 0); + buffer.insert(0, reinterpret_cast(buff->content), size); + xmlBufferFree(buff); +} +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/SaxReader.h b/modules/vcore/src/vcore/SaxReader.h new file mode 100644 index 0000000..816405f --- /dev/null +++ b/modules/vcore/src/vcore/SaxReader.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file SaxReader.h + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief Simple c++ interface for libxml2. + */ +#ifndef _SAXREADER_H_ +#define _SAXREADER_H_ + +#include +#include +#include + +namespace ValidationCore { +class SaxReader +{ + public: + SaxReader(); + ~SaxReader(); + + /* + * custom exceptions + */ + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, FileOpeningError) + DECLARE_EXCEPTION_TYPE(Base, FileNotValid) + DECLARE_EXCEPTION_TYPE(Base, ParserInternalError) + DECLARE_EXCEPTION_TYPE(Base, WrongToken) + DECLARE_EXCEPTION_TYPE(Base, ParserInternalErrorInReadingAttribute) + DECLARE_EXCEPTION_TYPE(Base, ParserInternalErrorInEmptyQuery) + DECLARE_EXCEPTION_TYPE(Base, ErrorReadingValue) + DECLARE_EXCEPTION_TYPE(Base, ErrorReadingName) + DECLARE_EXCEPTION_TYPE(Base, UnsupportedType) + }; + + enum NodeType + { + NODE_UNSUPPORTED, + NODE_BEGIN, + NODE_END, + NODE_TEXT + }; + + enum ThrowType + { + THROW_ENABLE = 0, + THROW_DISABLE + }; + + /* + * xml validation modes + */ + enum ValidationType + { + VALIDATION_DISABLE, + VALIDATION_XMLSCHEME, + VALIDATION_DTD + }; + + /* + * initializes parser + */ + void initialize(const std::string &filename, + bool defaultArgs = false, + ValidationType validation = VALIDATION_DISABLE, + const std::string &schema = std::string()); + /* + * deinitializes parser + */ + void deinitialize(); + + /** + * Move to next xml node. + */ + bool next(); + + /** + * Move to next xml node. If next node name is differ from token the exception will + * be thrown. + */ + void next(const std::string &token); + + /** + * Check if xml tag is empty. + */ + bool isEmpty(void); + + /** + * Read attribute tag. + */ + std::string attribute(const std::string &token, + ThrowType throwStatus = THROW_ENABLE); + + /** + * Read xml tag name with namespace. + */ + // KW std::string fullName(); + + /** + * Read xml tag name without namespace. + */ + std::string name(); + + /** + * Read xml tag namespace URI + */ + std::string namespaceURI(); + + /** + * Read xml tag value. + */ + std::string value(); + + /** + * Return information about node type. + */ + NodeType type(); + + /** + * Save all contonet of xml file which is between current tag and + * it's close tag into buffer. + */ + void dumpNode(std::string &buffer); + + private: + /* + * internal libxml text reader + */ + xmlTextReaderPtr m_reader; +}; +} + +#endif // _SAXREADER_H_ diff --git a/modules/vcore/src/vcore/SignatureData.h b/modules/vcore/src/vcore/SignatureData.h new file mode 100644 index 0000000..c0b7aad --- /dev/null +++ b/modules/vcore/src/vcore/SignatureData.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file SignatureData.cpp + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief SignatureData is used to storage data parsed from digsig file. + */ +#ifndef _SIGNATUREDATA_H_ +#define _SIGNATUREDATA_H_ + +#include +#include +#include + +#include +#include +#include + +#include "Certificate.h" +#include "CertStoreType.h" +#include "ValidatorCommon.h" + +/* TODO this class should not depend from OCSP headers */ +#include "OCSPCertMgrUtil.h" + +namespace ValidationCore { +class SignatureData +{ + public: + + SignatureData() : + m_signatureNumber(-1), + m_certificateSorted(false) + { + } + + SignatureData(std::string fileName, + int fileNumber) : + m_signatureNumber(fileNumber), + m_fileName(fileName), + m_certificateSorted(false) + { + } + + virtual ~SignatureData() + { + } + typedef std::list IMEIList; + typedef std::list MEIDList; + + const ReferenceSet& getReferenceSet() const + { + return m_referenceSet; + } + + void setReference(const ReferenceSet &referenceSet) + { + m_referenceSet = referenceSet; + } + + CertificateList getCertList(void) const + { + return m_certList; + } + + void setSortedCertificateList(const CertificateList &list) + { + m_certList = list; + m_certificateSorted = true; + } + + bool isAuthorSignature(void) const + { + return m_signatureNumber == -1; + } + + std::string getSignatureFileName(void) const + { + return m_fileName; + } + + int getSignatureNumber() const + { + return m_signatureNumber; + } + + std::string getRoleURI() const + { + return m_roleURI; + } + + std::string getProfileURI() const + { + return m_profileURI; + } + + bool containObjectReference(const std::string &ref) const + { + std::string rName = "#"; + rName += ref; + return m_referenceSet.end() != m_referenceSet.find(rName); + } + + ObjectList getObjectList() const + { + return m_objectList; + } + + void setStorageType(const CertStoreId::Set &storeIdSet) + { + m_storeIdSet = storeIdSet; + } + + const CertStoreId::Set& getStorageType(void) const + { + return m_storeIdSet; + } + + const IMEIList& getIMEIList() const + { + return m_imeiList; + } + + const MEIDList& getMEIDList() const + { + return m_meidList; + } + + CertificatePtr getEndEntityCertificatePtr() const + { + if (m_certificateSorted) { + return m_certList.front(); + } + return CertificatePtr(); + } + + CertificatePtr getRootCaCertificatePtr() const + { + if (m_certificateSorted) { + return m_certList.back(); + } + return CertificatePtr(); + } + + friend class SignatureReader; + private: + ReferenceSet m_referenceSet; + CertificateList m_certList; + + //TargetRestriction + IMEIList m_imeiList; + MEIDList m_meidList; + + /* + * This number is taken from distributor signature file name. + * Author signature do not contain any number on the file name. + * Author signature should have signature number equal to -1. + */ + int m_signatureNumber; + std::string m_fileName; + std::string m_roleURI; + std::string m_profileURI; + std::string m_identifier; + ObjectList m_objectList; + CertStoreId::Set m_storeIdSet; + bool m_certificateSorted; +}; + +typedef std::set SignatureDataSet; +} + +#endif diff --git a/modules/vcore/src/vcore/SignatureFinder.cpp b/modules/vcore/src/vcore/SignatureFinder.cpp new file mode 100644 index 0000000..ed2a27f --- /dev/null +++ b/modules/vcore/src/vcore/SignatureFinder.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file SignatureFinder.cpp + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief Search for author-signature.xml and signatureN.xml files. + */ +#include +#include +#include + +#include + +#include "SignatureFinder.h" + +namespace ValidationCore { +static const char *SIGNATURE_AUTHOR = "author-signature.xml"; +static const char *REGEXP_DISTRIBUTOR_SIGNATURE = + "^(signature)([1-9][0-9]*)(\\.xml)"; + +SignatureFinder::SignatureFinder(const std::string& dir) : + m_dir(dir), + m_signatureRegexp(REGEXP_DISTRIBUTOR_SIGNATURE) +{ +} + +SignatureFinder::Result SignatureFinder::find(SignatureFileInfoSet &set) +{ + DIR *dp; + struct dirent *dirp; + + /* + * find a dir + */ + if ((dp = opendir(m_dir.c_str())) == NULL) { + LogError("Error opening directory:" << m_dir); + return ERROR_OPENING_DIR; + } + + for (errno = 0; (dirp = readdir(dp)) != NULL; errno = 0) { + /** + * check if it's author signature + */ + if (!strcmp(dirp->d_name, SIGNATURE_AUTHOR)) { + set.insert(SignatureFileInfo(std::string(dirp->d_name), -1)); + continue; + } + + std::string sig, num, xml; + if (m_signatureRegexp.FullMatch(dirp->d_name, &sig, &num, &xml)) { + std::istringstream stream(num); + int number; + stream >> number; + + if (stream.fail()) { + closedir(dp); + return ERROR_ISTREAM; + } + + set.insert(SignatureFileInfo(std::string(dirp->d_name), number)); + } + } + + if (errno != 0) { + LogError("Error in readdir"); + closedir(dp); + return ERROR_READING_DIR; + } + + closedir(dp); + return NO_ERROR; +} +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/SignatureFinder.h b/modules/vcore/src/vcore/SignatureFinder.h new file mode 100644 index 0000000..0e04213 --- /dev/null +++ b/modules/vcore/src/vcore/SignatureFinder.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file SignatureFinder.h + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief Search for author-signature.xml and signatureN.xml files. + */ +#ifndef _SIGNATUREFINDER_H_ +#define _SIGNATUREFINDER_H_ + +#include +#include + +#include + +#include "SignatureData.h" + +namespace ValidationCore { +class SignatureFileInfo +{ + public: + SignatureFileInfo(const std::string &fileName, + int num) : + m_fileName(fileName), + m_fileNumber(num) + { + } + + std::string getFileName() const + { + return m_fileName; + } + + int getFileNumber() const + { + return m_fileNumber; + } + + bool operator<(const SignatureFileInfo &second) const + { + return m_fileNumber < second.m_fileNumber; + } + private: + std::string m_fileName; + int m_fileNumber; +}; + +typedef std::set SignatureFileInfoSet; + +class SignatureFinder +{ + public: + enum Result + { + NO_ERROR, + ERROR_OPENING_DIR, + ERROR_READING_DIR, + ERROR_ISTREAM + }; + + SignatureFinder(const std::string& dir); + + Result find(SignatureFileInfoSet &set); + + private: + std::string m_dir; + pcrecpp::RE m_signatureRegexp; +}; +} // namespace ValidationCore + +#endif diff --git a/modules/vcore/src/vcore/SignatureReader.cpp b/modules/vcore/src/vcore/SignatureReader.cpp new file mode 100644 index 0000000..cf7540c --- /dev/null +++ b/modules/vcore/src/vcore/SignatureReader.cpp @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file SignatureReader.cpp + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief SignatureReader is used to parse widget digital signature. + */ +#include "SignatureReader.h" + +#include "CertificateLoader.h" + +namespace ValidationCore { +static const std::string XML_NAMESPACE = + "http://www.w3.org/2000/09/xmldsig#"; +static const std::string XML_NAMESPACE_DIGITALSIG = + "http://wacapps.net/ns/digsig"; +static const std::string XML_OBJ_NS = + "http://www.w3.org/2009/xmldsig-properties"; + +// TAG TOKENS +static const std::string TOKEN_SIGNATURE = "Signature"; +static const std::string TOKEN_SIGNED_INFO = "SignedInfo"; +static const std::string TOKEN_CANONICALIZATION_METHOD = + "CanonicalizationMethod"; +static const std::string TOKEN_SIGNATURE_METHOD = "SignatureMethod"; +static const std::string TOKEN_REFERENCE = "Reference"; +static const std::string TOKEN_TRANSFORMS = "Transforms"; +static const std::string TOKEN_TRANSFORM = "Transform"; +static const std::string TOKEN_DIGEST_METHOD = "DigestMethod"; +static const std::string TOKEN_DIGEST_VALUE = "DigestValue"; +static const std::string TOKEN_SIGNATURE_VALUE = "SignatureValue"; +static const std::string TOKEN_KEY_INFO = "KeyInfo"; +static const std::string TOKEN_X509DATA = "X509Data"; +static const std::string TOKEN_X509CERTIFICATE = "X509Certificate"; +static const std::string TOKEN_KEY_VALUE = "KeyValue"; +static const std::string TOKEN_RSA_KEY_VALUE = "RSAKeyValue"; +static const std::string TOKEN_MODULUS_COMPONENT = "Modulus"; +static const std::string TOKEN_EXPONENT_COMPONENT = "Exponent"; +static const std::string TOKEN_ECKEY_VALUE = "ECKeyValue"; +static const std::string TOKEN_NAMED_CURVE = "NamedCurve"; +static const std::string TOKEN_PUBLIC_KEY = "PublicKey"; +static const std::string TOKEN_OBJECT = "Object"; +static const std::string TOKEN_SIGNATURE_PROPERTIES = "SignatureProperties"; +static const std::string TOKEN_SIGNATURE_PROPERTY = "SignatureProperty"; +static const std::string TOKEN_PROFILE = "Profile"; +static const std::string TOKEN_ROLE = "Role"; +static const std::string TOKEN_IDENTIFIER = "Identifier"; +static const std::string TOKEN_DSAKEYVALUE = "DSAKeyValue"; +static const std::string TOKEN_DSA_P_COMPONENT = "P"; +static const std::string TOKEN_DSA_Q_COMPONENT = "Q"; +static const std::string TOKEN_DSA_G_COMPONENT = "G"; +static const std::string TOKEN_DSA_Y_COMPONENT = "Y"; +static const std::string TOKEN_DSA_J_COMPONENT = "J"; +static const std::string TOKEN_DSA_SEED_COMPONENT = "Seed"; +static const std::string TOKEN_DSA_PGENCOUNTER_COMPONENT = "PgenCounter"; +static const std::string TOKEN_TARGET_RESTRICTION = "TargetRestriction"; + +// ATTRIBUTTE TOKENS + +static const std::string TOKEN_ALGORITHM = "Algorithm"; +static const std::string TOKEN_URI = "URI"; +static const std::string TOKEN_ID = "Id"; +static const std::string TOKEN_TARGET = "Target"; +static const std::string TOKEN_IMEI = "IMEI"; +static const std::string TOKEN_MEID = "MEID"; + +// ATTIRUBTE VALUES + +static const std::string TOKEN_ATTR_PROFILE = "profile"; +static const std::string TOKEN_ATTR_ROLE = "role"; +static const std::string TOKEN_ATTR_IDENTIFIER = "identifier"; + +// ALGORITHMS + +//static const std::string TOKEN_ALGORITHM_XML_EXC_CAN = +// "http://www.w3.org/2001/10/xml-exc-c14n#"; +//static const std::string TOKEN_ALGORITHM_RSA_SHA256 = +// "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; +//static const std::string TOKEN_ALGORITHM_DSA_SHA1 = +// "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; +//static const std::string TOKEN_ALGORITHM_ECDSA_SHA256 = +// "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"; +//static const std::string TOKEN_ALGORITHM_SHA1 = +// "http://www.w3.org/2000/09/xmldsig#sha1"; +//static const std::string TOKEN_ALGORITHM_SHA256 = +// "http://www.w3.org/2001/04/xmlenc#sha256"; +//static const std::string TOKEN_ALGORITHM_SHA384 = +// "http://www.w3.org/2001/04/xmldsig-more#sha384"; +//static const std::string TOKEN_ALGORITHM_SHA512 = +// "http://www.w3.org/2001/04/xmlenc#sha512"; + +SignatureReader::SignatureReader() : + m_signaturePropertiesCounter(0), + m_targetRestrictionObjectFound(false), + m_parserSchema(this) +{ + /** + * member func pointers map + */ + m_parserSchema.addBeginTagCallback(TOKEN_SIGNATURE, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_SIGNED_INFO, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_CANONICALIZATION_METHOD, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_SIGNATURE_METHOD, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_REFERENCE, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_TRANSFORMS, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_TRANSFORM, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_DIGEST_METHOD, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_DIGEST_VALUE, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_SIGNATURE_VALUE, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_KEY_INFO, + XML_NAMESPACE, + &SignatureReader::tokenKeyInfo); + m_parserSchema.addBeginTagCallback(TOKEN_X509DATA, + XML_NAMESPACE, + &SignatureReader::tokenX509Data); + m_parserSchema.addBeginTagCallback(TOKEN_X509CERTIFICATE, + XML_NAMESPACE, + &SignatureReader::tokenX509Certificate); + m_parserSchema.addBeginTagCallback(TOKEN_ECKEY_VALUE, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_NAMED_CURVE, + XML_NAMESPACE, + &SignatureReader::tokenNamedCurve); + m_parserSchema.addBeginTagCallback(TOKEN_PUBLIC_KEY, + XML_NAMESPACE, + &SignatureReader::tokenPublicKey); + m_parserSchema.addBeginTagCallback(TOKEN_OBJECT, + XML_NAMESPACE, + &SignatureReader::tokenObject); + m_parserSchema.addBeginTagCallback( + TOKEN_SIGNATURE_PROPERTIES, + XML_NAMESPACE, + &SignatureReader:: + tokenSignatureProperties); + m_parserSchema.addBeginTagCallback(TOKEN_SIGNATURE_PROPERTY, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_PROFILE, + XML_OBJ_NS, + &SignatureReader::tokenProfile); + m_parserSchema.addBeginTagCallback(TOKEN_ROLE, + XML_OBJ_NS, + &SignatureReader::tokenRole); + m_parserSchema.addBeginTagCallback(TOKEN_IDENTIFIER, + XML_OBJ_NS, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_KEY_VALUE, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_DSAKEYVALUE, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_DSA_P_COMPONENT, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_DSA_Q_COMPONENT, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_DSA_G_COMPONENT, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_DSA_Y_COMPONENT, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_DSA_J_COMPONENT, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_DSA_SEED_COMPONENT, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_DSA_PGENCOUNTER_COMPONENT, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_RSA_KEY_VALUE, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_MODULUS_COMPONENT, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_EXPONENT_COMPONENT, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addBeginTagCallback(TOKEN_TARGET_RESTRICTION, + XML_NAMESPACE_DIGITALSIG, + &SignatureReader::tokenTargetRestriction); + + m_parserSchema.addEndTagCallback(TOKEN_SIGNATURE, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_SIGNED_INFO, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_CANONICALIZATION_METHOD, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_SIGNATURE_METHOD, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_REFERENCE, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_TRANSFORMS, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_TRANSFORM, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_DIGEST_METHOD, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_DIGEST_VALUE, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_SIGNATURE_VALUE, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_KEY_INFO, + XML_NAMESPACE, + &SignatureReader::tokenEndKeyInfo); + m_parserSchema.addEndTagCallback(TOKEN_X509DATA, + XML_NAMESPACE, + &SignatureReader::tokenEndX509Data); + m_parserSchema.addEndTagCallback(TOKEN_X509CERTIFICATE, + XML_NAMESPACE, + &SignatureReader::tokenEndX509Certificate); + m_parserSchema.addEndTagCallback(TOKEN_ECKEY_VALUE, + XML_NAMESPACE, + &SignatureReader::tokenEndECKeyValue); + m_parserSchema.addEndTagCallback(TOKEN_PUBLIC_KEY, + XML_NAMESPACE, + &SignatureReader::tokenEndPublicKey); + m_parserSchema.addEndTagCallback(TOKEN_OBJECT, + XML_NAMESPACE, + &SignatureReader::tokenEndObject); + m_parserSchema.addEndTagCallback(TOKEN_SIGNATURE_PROPERTIES, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_SIGNATURE_PROPERTY, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_PROFILE, + XML_OBJ_NS, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_ROLE, + XML_OBJ_NS, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_IDENTIFIER, + XML_OBJ_NS, + &SignatureReader::tokenEndIdentifier); + m_parserSchema.addEndTagCallback(TOKEN_KEY_VALUE, + XML_NAMESPACE, + &SignatureReader::blankFunction); + m_parserSchema.addEndTagCallback(TOKEN_DSAKEYVALUE, + XML_NAMESPACE, + &SignatureReader::tokenEndDSAKeyValue); + m_parserSchema.addEndTagCallback(TOKEN_DSA_P_COMPONENT, + XML_NAMESPACE, + &SignatureReader::tokenEndDSAPComponent); + m_parserSchema.addEndTagCallback(TOKEN_DSA_Q_COMPONENT, + XML_NAMESPACE, + &SignatureReader::tokenEndDSAQComponent); + m_parserSchema.addEndTagCallback(TOKEN_DSA_G_COMPONENT, + XML_NAMESPACE, + &SignatureReader::tokenEndDSAGComponent); + m_parserSchema.addEndTagCallback(TOKEN_DSA_Y_COMPONENT, + XML_NAMESPACE, + &SignatureReader::tokenEndDSAYComponent); + m_parserSchema.addEndTagCallback(TOKEN_DSA_J_COMPONENT, + XML_NAMESPACE, + &SignatureReader::tokenEndDSAJComponent); + m_parserSchema.addEndTagCallback(TOKEN_DSA_SEED_COMPONENT, + XML_NAMESPACE, + &SignatureReader::tokenEndDSASeedComponent); + m_parserSchema.addEndTagCallback( + TOKEN_DSA_PGENCOUNTER_COMPONENT, + XML_NAMESPACE, + &SignatureReader:: + tokenEndDSAPGenCounterComponent); + m_parserSchema.addEndTagCallback(TOKEN_RSA_KEY_VALUE, + XML_NAMESPACE, + &SignatureReader::tokenEndRSAKeyValue); + m_parserSchema.addEndTagCallback(TOKEN_MODULUS_COMPONENT, + XML_NAMESPACE, + &SignatureReader::tokenEndKeyModulus); + m_parserSchema.addEndTagCallback(TOKEN_EXPONENT_COMPONENT, + XML_NAMESPACE, + &SignatureReader::tokenEndKeyExponent); + m_parserSchema.addEndTagCallback(TOKEN_TARGET_RESTRICTION, + XML_NAMESPACE, + &SignatureReader::blankFunction); +} + +void SignatureReader::tokenKeyInfo(SignatureData &signatureData) +{ + (void)signatureData; +} +void SignatureReader::tokenX509Data(SignatureData &signatureData) +{ + (void)signatureData; +} +void SignatureReader::tokenX509Certificate(SignatureData &signatureData) +{ + (void)signatureData; +} +void SignatureReader::tokenPublicKey(SignatureData &signatureData) +{ + (void)signatureData; +} + +void SignatureReader::tokenNamedCurve(SignatureData &signatureData) +{ + (void)signatureData; + m_nameCurveURI = m_parserSchema.getReader().attribute(TOKEN_URI); +} + +void SignatureReader::tokenTargetRestriction(SignatureData &signatureData) +{ + std::string IMEI = m_parserSchema.getReader().attribute( + TOKEN_IMEI, + SaxReader:: + THROW_DISABLE); + std::string MEID = m_parserSchema.getReader().attribute( + TOKEN_MEID, + SaxReader:: + THROW_DISABLE); + + //less verbose way to say (IMEI && MEID) || (!IMEI && !MEID) + if (IMEI.empty() == MEID.empty()) { + //WAC 2.0 WR-4650 point 4 + ThrowMsg(Exception::TargetRestrictionException, + "TargetRestriction should contain exactly one attribute."); + return; + } + + if (!IMEI.empty()) { + signatureData.m_imeiList.push_back(IMEI); + } + if (!MEID.empty()) { + signatureData.m_meidList.push_back(MEID); + } +} + +void SignatureReader::tokenEndKeyInfo(SignatureData &signatureData) +{ + (void)signatureData; +} + +void SignatureReader::tokenEndX509Data(SignatureData &signatureData) +{ + (void)signatureData; +} + +void SignatureReader::tokenEndX509Certificate(SignatureData &signatureData) +{ + CertificateLoader loader; + if (CertificateLoader::NO_ERROR != + loader.loadCertificateFromRawData(m_parserSchema.getText())) { + LogWarning("Certificate could not be loaded!"); + ThrowMsg(ParserSchemaException::CertificateLoaderError, + "Certificate could not be loaded."); + } + signatureData.m_certList.push_back(loader.getCertificatePtr()); +} +// KW void SignatureReader::tokenEndKeyName(SignatureData &signatureData){ +// KW CertificateLoader loader; +// KW if(CertificateLoader::NO_ERROR != loader.loadCertificateBasedOnSubjectName(m_parserSchema.getText())){ +// KW LogError("Certificate could not be loaded!"); +// KW ThrowMsg(ParserSchemaException::CertificateLoaderError, "Certificate could not be loaded."); +// KW } +// KW signatureData.m_certList.push_back(loader); +// KW } + +void SignatureReader::tokenEndRSAKeyValue(SignatureData &signatureData) +{ + CertificateLoader loader; + if (CertificateLoader::NO_ERROR != + loader.loadCertificateBasedOnExponentAndModulus(m_modulus, + m_exponent)) { + LogWarning("Certificate could not be loaded!"); + ThrowMsg(ParserSchemaException::CertificateLoaderError, + "Certificate could not be loaded."); + } + signatureData.m_certList.push_back(loader.getCertificatePtr()); +} + +void SignatureReader::tokenEndKeyModulus(SignatureData &signatureData) +{ + (void)signatureData; + m_modulus = m_parserSchema.getText(); +} + +void SignatureReader::tokenEndKeyExponent(SignatureData &signatureData) +{ + (void)signatureData; + m_exponent = m_parserSchema.getText(); +} + +void SignatureReader::tokenEndPublicKey(SignatureData &signatureData) +{ + (void)signatureData; + m_publicKey = m_parserSchema.getText(); +} + +void SignatureReader::tokenEndECKeyValue(SignatureData &signatureData) +{ + CertificateLoader loader; + if (CertificateLoader::NO_ERROR != + loader.loadCertificateWithECKEY(m_nameCurveURI, m_publicKey)) { + ThrowMsg(ParserSchemaException::CertificateLoaderError, + "Certificate could not be loaded."); + } + signatureData.m_certList.push_back(loader.getCertificatePtr()); +} + +void SignatureReader::tokenEndObject(SignatureData &signatureData) +{ + m_signaturePropertiesCounter = 0; + + if (((!signatureData.m_imeiList.empty()) || + (!signatureData.m_meidList.empty())) && + m_targetRestrictionObjectFound) { + //WAC 2.0 WR-4650 point 1 + ThrowMsg( + Exception::TargetRestrictionException, + "TargetRestriction should contain exactly one ds:Object containing zero or more wac:TargetRestriction children."); + return; + } + if ((!signatureData.m_imeiList.empty()) || + (!signatureData.m_meidList.empty())) { + m_targetRestrictionObjectFound = true; + } +} +void SignatureReader::tokenEndDSAPComponent(SignatureData& signatureData) +{ + (void)signatureData; + m_dsaKeyPComponent = m_parserSchema.getText(); +} + +void SignatureReader::tokenEndDSAQComponent(SignatureData& signatureData) +{ + (void)signatureData; + m_dsaKeyQComponent = m_parserSchema.getText(); +} + +void SignatureReader::tokenEndDSAGComponent(SignatureData& signatureData) +{ + (void)signatureData; + m_dsaKeyGComponent = m_parserSchema.getText(); +} + +void SignatureReader::tokenEndDSAYComponent(SignatureData& signatureData) +{ + (void)signatureData; + m_dsaKeyYComponent = m_parserSchema.getText(); +} + +void SignatureReader::tokenEndDSAJComponent(SignatureData& signatureData) +{ + (void)signatureData; + m_dsaKeyJComponent = m_parserSchema.getText(); +} + +void SignatureReader::tokenEndDSASeedComponent(SignatureData& signatureData) +{ + (void)signatureData; + m_dsaKeySeedComponent = m_parserSchema.getText(); +} + +void SignatureReader::tokenEndDSAPGenCounterComponent( + SignatureData& signatureData) +{ + (void)signatureData; + m_dsaKeyPGenCounter = m_parserSchema.getText(); +} + +void SignatureReader::tokenEndDSAKeyValue(SignatureData& signatureData) +{ + CertificateLoader loader; + + if (CertificateLoader::NO_ERROR != + loader.loadCertificateBasedOnDSAComponents(m_dsaKeyPComponent, + m_dsaKeyQComponent, + m_dsaKeyGComponent, + m_dsaKeyYComponent, + m_dsaKeyJComponent, + m_dsaKeySeedComponent, + m_dsaKeyPGenCounter)) { + LogWarning("Certificate could not be loaded."); + ThrowMsg(ParserSchemaException::CertificateLoaderError, + "Certificate could not be loaded."); + } + signatureData.m_certList.push_back(loader.getCertificatePtr()); +} + +void SignatureReader::tokenRole(SignatureData &signatureData) +{ + if (!signatureData.m_roleURI.empty()) { + LogWarning("Multiple definition of Role is not allowed."); + ThrowMsg(ParserSchemaException::UnsupportedValue, + "Multiple definition of Role is not allowed."); + } + signatureData.m_roleURI = m_parserSchema.getReader().attribute(TOKEN_URI); +} + +void SignatureReader::tokenProfile(SignatureData &signatureData) +{ + if (!signatureData.m_profileURI.empty()) { + LogWarning("Multiple definition of Profile is not allowed."); + ThrowMsg(ParserSchemaException::UnsupportedValue, + "Multiple definition of Profile is not allowed."); + } + signatureData.m_profileURI = m_parserSchema.getReader().attribute(TOKEN_URI); +} + +void SignatureReader::tokenEndIdentifier(SignatureData &signatureData) +{ + if (!signatureData.m_identifier.empty()) { + LogWarning("Multiple definition of Identifier is not allowed."); + ThrowMsg(ParserSchemaException::UnsupportedValue, + "Multiple definition of Identifier is not allowed."); + } + signatureData.m_identifier = m_parserSchema.getText(); +} + +void SignatureReader::tokenObject(SignatureData &signatureData) +{ + std::string id = m_parserSchema.getReader().attribute(TOKEN_ID); + + if (id.empty()) { + LogWarning("Unsupported value of Attribute Id in Object tag."); + ThrowMsg(ParserSchemaException::UnsupportedValue, + "Unsupported value of Attribute Id in Object tag."); + } + + signatureData.m_objectList.push_back(id); +} + +void SignatureReader::tokenSignatureProperties(SignatureData &signatureData) +{ + (void)signatureData; + if (++m_signaturePropertiesCounter > 1) { + LogWarning("Only one SignatureProperties tag is allowed in Object"); + ThrowMsg(ParserSchemaException::UnsupportedValue, + "Only one SignatureProperties tag is allowed in Object"); + } +} +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/SignatureReader.h b/modules/vcore/src/vcore/SignatureReader.h new file mode 100644 index 0000000..e6368fc --- /dev/null +++ b/modules/vcore/src/vcore/SignatureReader.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file SignatureReader.h + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief SignatureReader is used to parse widget digital signature. + */ +#ifndef _SIGNATUREREADER_H_ +#define _SIGNATUREREADER_H_ + +#include +#include + +#include "SignatureData.h" +#include "ParserSchema.h" + +namespace ValidationCore { +class SignatureReader +{ + public: + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, TargetRestrictionException) + }; + + SignatureReader(); + + void initialize(SignatureData &data, + const std::string &xmlscheme) + { + m_parserSchema.initialize( + data.getSignatureFileName(), true, SaxReader::VALIDATION_XMLSCHEME, + xmlscheme); + } + + void read(SignatureData &data) + { + m_parserSchema.read(data); + } + + private: + void blankFunction(SignatureData &) + { + } + + void tokenKeyInfo(SignatureData &signatureData); + void tokenKeyModulus(SignatureData &signatureData); + void tokenKeyExponent(SignatureData &signatureData); + void tokenX509Data(SignatureData &signatureData); + void tokenX509Certificate(SignatureData &signatureData); + void tokenPublicKey(SignatureData &signatureData); + void tokenNamedCurve(SignatureData &signatureData); + void tokenRole(SignatureData &signatureData); + void tokenProfile(SignatureData &signatureData); + void tokenObject(SignatureData &signatureData); + void tokenSignatureProperties(SignatureData &signatureData); + void tokenTargetRestriction(SignatureData &signatureData); + + void tokenEndKeyInfo(SignatureData &signatureData); + // KW void tokenEndKeyName(SignatureData &signatureData); + void tokenEndRSAKeyValue(SignatureData &signatureData); + void tokenEndKeyModulus(SignatureData &signatureData); + void tokenEndKeyExponent(SignatureData &signatureData); + void tokenEndX509Data(SignatureData &signatureData); + void tokenEndX509Certificate(SignatureData &signatureData); + void tokenEndPublicKey(SignatureData &signatureData); + void tokenEndECKeyValue(SignatureData &signatureData); + void tokenEndIdentifier(SignatureData &signatureData); + void tokenEndObject(SignatureData &signatureData); + + // DSA key components + void tokenEndDSAPComponent(SignatureData& signatureData); + void tokenEndDSAQComponent(SignatureData& signatureData); + void tokenEndDSAGComponent(SignatureData& signatureData); + void tokenEndDSAYComponent(SignatureData& signatureData); + void tokenEndDSAJComponent(SignatureData& signatureData); + + void tokenEndDSAKeyValue(SignatureData& signatureData); + + void tokenEndDSASeedComponent(SignatureData& signatureData); + void tokenEndDSAPGenCounterComponent(SignatureData& signatureData); + + // temporary values required due reference parsing process + // optional parameters for dsa + std::string m_dsaKeyPComponent; + std::string m_dsaKeyQComponent; + std::string m_dsaKeyGComponent; + std::string m_dsaKeyYComponent; + std::string m_dsaKeyJComponent; + std::string m_dsaKeySeedComponent; + std::string m_dsaKeyPGenCounter; + // temporary values of ecdsa key + std::string m_publicKey; + std::string m_nameCurveURI; + std::string m_modulus; + std::string m_exponent; + + // temporary values required due Object parsing + int m_signaturePropertiesCounter; + bool m_targetRestrictionObjectFound; + + ParserSchema m_parserSchema; +}; +} + +#endif diff --git a/modules/vcore/src/vcore/SignatureValidator.cpp b/modules/vcore/src/vcore/SignatureValidator.cpp new file mode 100644 index 0000000..e965ecc --- /dev/null +++ b/modules/vcore/src/vcore/SignatureValidator.cpp @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include + +#include "CertificateVerifier.h" +#include "OCSPCertMgrUtil.h" +#include "Certificate.h" +#include "ReferenceValidator.h" +#include "SignatureValidator.h" +#include "SSLContainers.h" +#include "ValidatorCommon.h" +#include "ValidatorFactories.h" +#include "XmlsecAdapter.h" + +namespace { +const time_t TIMET_DAY = 60 * 60 * 24; + +const std::string TOKEN_ROLE_AUTHOR_URI = + "http://www.w3.org/ns/widgets-digsig#role-author"; +const std::string TOKEN_ROLE_DISTRIBUTOR_URI = + "http://www.w3.org/ns/widgets-digsig#role-distributor"; +const std::string TOKEN_PROFILE_URI = + "http://www.w3.org/ns/widgets-digsig#profile"; +} // namespace anonymouse + +namespace ValidationCore { + +SignatureValidator::SignatureValidator(bool ocspEnable, + bool crlEnable, + bool complianceMode) : + m_ocspEnable(ocspEnable), + m_crlEnable(crlEnable), + m_complianceModeEnabled(complianceMode) +{ +} + +SignatureValidator::~SignatureValidator() +{ +} + +bool SignatureValidator::checkRoleURI(const SignatureData &data) +{ + std::string roleURI = data.getRoleURI(); + + if (roleURI.empty()) { + LogWarning("URI attribute in Role tag couldn't be empty."); + return false; + } + + if (roleURI != TOKEN_ROLE_AUTHOR_URI && data.isAuthorSignature()) { + LogWarning("URI attribute in Role tag does not " + "match with signature filename."); + return false; + } + + if (roleURI != TOKEN_ROLE_DISTRIBUTOR_URI && !data.isAuthorSignature()) { + LogWarning("URI attribute in Role tag does not " + "match with signature filename."); + return false; + } + return true; +} + +bool SignatureValidator::checkProfileURI(const SignatureData &data) +{ + if (TOKEN_PROFILE_URI != data.getProfileURI()) { + LogWarning( + "Profile tag contains unsupported value in URI attribute(" << + data.getProfileURI() << ")."); + return false; + } + return true; +} + +bool SignatureValidator::checkObjectReferences(const SignatureData &data) +{ + ObjectList objectList = data.getObjectList(); + ObjectList::const_iterator iter; + for (iter = objectList.begin(); iter != objectList.end(); ++iter) { + if (!data.containObjectReference(*iter)) { + LogWarning("Signature does not contain reference for object " << + *iter); + return false; + } + } + return true; +} + +SignatureValidator::Result SignatureValidator::check( + SignatureData &data, + const std::string &widgetContentPath) +{ + bool disregard = false; + + if (!checkRoleURI(data)) { + return SIGNATURE_INVALID; + } + + if (!checkProfileURI(data)) { + return SIGNATURE_INVALID; + } + + // CertificateList sortedCertificateList = data.getCertList(); + + CertificateCollection collection; + collection.load(data.getCertList()); + + // First step - sort certificate + if (!collection.sort()) { + LogWarning("Certificates do not form valid chain."); + return SIGNATURE_INVALID; + } + + // Check for error + if (collection.empty()) { + LogWarning("Certificate list in signature is empty."); + return SIGNATURE_INVALID; + } + + CertificateList sortedCertificateList = collection.getChain(); + + // TODO move it to CertificateCollection + // Add root CA and CA certificates (if chain is incomplete) + sortedCertificateList = + OCSPCertMgrUtil::completeCertificateChain(sortedCertificateList); + + CertificatePtr root = sortedCertificateList.back(); + + // Is Root CA certificate trusted? + CertStoreId::Set storeIdSet = createCertificateIdentifier().find(root); + + // WAC chapter 3.2.1 - verified definition + if (data.isAuthorSignature()) { + if (!storeIdSet.contains(CertStoreId::WAC_PUBLISHER)) { + LogWarning("Author signature has got unrecognized Root CA " + "certificate. Signature will be disregarded."); + disregard = true; + } + LogDebug("Root CA for author signature is correct."); + } else { + if (!storeIdSet.contains(CertStoreId::DEVELOPER) && + !storeIdSet.contains(CertStoreId::WAC_ROOT) && + !storeIdSet.contains(CertStoreId::WAC_MEMBER)) + { + LogWarning("Distiributor signature has got unrecognized Root CA " + "certificate. Signature will be disregarded."); + disregard = true; + } + LogDebug("Root CA for distributor signature is correct."); + } + + data.setStorageType(storeIdSet); + data.setSortedCertificateList(sortedCertificateList); + + // We add only Root CA certificate because WAC ensure that the rest + // of certificates are present in signature files ;-) + XmlSec::XmlSecContext context; + context.signatureFile = data.getSignatureFileName(); + context.certificatePtr = root; + + // Now we should have full certificate chain. + // If the end certificate is not ROOT CA we should disregard signature + // but still signature must be valid... Aaaaaa it's so stupid... + if (!(root->isSignedBy(root))) { + LogWarning("Root CA certificate not found. Chain is incomplete."); + context.allowBrokenChain = true; + } + + // WAC 2.0 SP-2066 The wrt must not block widget installation + // due to expiration of the author certificate. + time_t notAfter = data.getEndEntityCertificatePtr()->getNotAfter(); + bool expired = notAfter < time(NULL); + if (data.isAuthorSignature() && expired) { + context.validationTime = notAfter - TIMET_DAY; + } + // end + + if (XmlSec::NO_ERROR != XmlSecSingleton::Instance().validate(&context)) { + LogWarning("Installation break - invalid package!"); + return SIGNATURE_INVALID; + } + + data.setReference(context.referenceSet); + + if (!checkObjectReferences(data)) { + return SIGNATURE_INVALID; + } + + ReferenceValidator fileValidator(widgetContentPath); + if (ReferenceValidator::NO_ERROR != fileValidator.checkReferences(data)) { + LogWarning("Invalid package - file references broken"); + return SIGNATURE_INVALID; + } + + // It is good time to do OCSP check + // ocspCheck will throw an exception on any error. + // TODO Probably we should catch this exception and add + // some information to SignatureData. + if (!m_complianceModeEnabled && !data.isAuthorSignature()) { + CertificateCollection coll; + coll.load(sortedCertificateList); + + if (!coll.sort()) { + LogDebug("Collection does not contain chain!"); + return SIGNATURE_INVALID; + } + + CertificateVerifier verificator(m_ocspEnable, m_crlEnable); + VerificationStatus result = verificator.check(coll); + + if (result == VERIFICATION_STATUS_REVOKED) { + return SIGNATURE_REVOKED; + } + + if (result == VERIFICATION_STATUS_UNKNOWN || + result == VERIFICATION_STATUS_ERROR) + { + disregard = true; + } + } + + if (disregard) { + LogWarning("Signature is disregard."); + return SIGNATURE_DISREGARD; + } + return SIGNATURE_VERIFIED; +} + +std::string SignatureValidator::FingerprintToColonHex( + const Certificate::Fingerprint &fingerprint) +{ + std::string outString; + + char buff[8]; + + for (size_t i = 0; i < fingerprint.size(); ++i) { + snprintf(buff, + sizeof(buff), + "%02X:", + static_cast(fingerprint[i])); + outString += buff; + } + + // remove trailing ":" + outString.erase(outString.end() - 1); + return outString; +} +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/SignatureValidator.h b/modules/vcore/src/vcore/SignatureValidator.h new file mode 100644 index 0000000..aa381cb --- /dev/null +++ b/modules/vcore/src/vcore/SignatureValidator.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _SIGNATUREVALIDATOR_H_ +#define _SIGNATUREVALIDATOR_H_ + +#include + +#include "Certificate.h" +#include "OCSPCertMgrUtil.h" +#include "SignatureData.h" + +#include "ValidatorCommon.h" +#include "VerificationStatus.h" + +namespace ValidationCore { +// Todo nocopyable +class SignatureValidator +{ + public: + enum Result + { + SIGNATURE_VALID, + SIGNATURE_INVALID, + SIGNATURE_VERIFIED, + SIGNATURE_DISREGARD, // no ocsp response or ocsp return unknown status + SIGNATURE_REVOKED + }; + + /** + * Validation of the signature. + * If falidation succeed SignatureData will contains: + * list of validated references + * set selfSigned value + * root ca certificate + * end entity certificate + */ + Result check(SignatureData &data, + const std::string &widgetContentPath); + + static std::string FingerprintToColonHex( + const Certificate::Fingerprint &fingerprint); + + explicit SignatureValidator(bool ocspEnable, + bool crlEnable, + bool complianceMode); + virtual ~SignatureValidator(); + + private: + bool checkRoleURI(const SignatureData &data); + bool checkProfileURI(const SignatureData &data); + bool checkObjectReferences(const SignatureData &data); + + bool m_ocspEnable; + bool m_crlEnable; + bool m_complianceModeEnabled; +}; + +} // namespace ValidationCore + +#endif // _SIGNATUREVALIDATOR_H_ diff --git a/modules/vcore/src/vcore/SoupMessageSendAsync.cpp b/modules/vcore/src/vcore/SoupMessageSendAsync.cpp new file mode 100644 index 0000000..d8bb132 --- /dev/null +++ b/modules/vcore/src/vcore/SoupMessageSendAsync.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/modules/vcore/src/vcore/SoupMessageSendAsync.h b/modules/vcore/src/vcore/SoupMessageSendAsync.h new file mode 100644 index 0000000..c6900e2 --- /dev/null +++ b/modules/vcore/src/vcore/SoupMessageSendAsync.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 0.1 + * @file SoupMessageSendAsync.h + * @brief Routines for certificate validation over OCSP + */ +#ifndef _SRC_VALIDATION_CORE_SOUP_MESSAGE_SEND_ASYNC_H_ +#define _SRC_VALIDATION_CORE_SOUP_MESSAGE_SEND_ASYNC_H_ + +#include +#include + +#include + +#include + +#include "SoupMessageSendBase.h" + +namespace SoupWrapper { + +class SoupMessageSendAsync + : public SoupMessageSendBase + , public DPL::Event::ICDelegateSupport +{ + typedef DPL::Event::ICDelegate SoupDelegate; + public: + void sendAsync() { + Assert(m_status == STATUS_IDLE); + Assert(!m_soupSession); + Assert(!m_soupMessage); + + m_status = STATUS_SEND_ASYNC; + m_tryLeft = m_tryCount; + m_mainContext = g_main_context_new(); + + if (!m_mainContext){ + m_status = STATUS_IDLE; + + // call the delegate to outside with error! + return; + } + + m_soupSession = soup_session_async_new_with_options( + SOUP_SESSION_ASYNC_CONTEXT, + m_mainContext, + SOUP_SESSION_TIMEOUT, + m_timeout, + NULL); + + if (!m_soupSession){ + m_status = STATUS_IDLE; + g_object_unref(m_mainContext); + m_mainContext = 0; + + // call the deletage to outside with error! + return; + } + + m_soupMessage = createRequest(); + + if (!m_soupMessage){ + m_status = STATUS_IDLE; + g_object_unref(m_soupSession); + m_soupSession = 0; + g_object_unref(m_mainContext); + m_mainContext = 0; + + // call the delegate to outsize with error! + return; + } + + sendAsyncIterationStart(); + } + + protected: + + struct SoupDelegateOpaque { + SoupDelegate dlg; + }; + + void sendAsyncIterationStart(){ + // ICDelegate could be called only once. + // We can set user data only once. + // We need nasty hack because we will call ICDelegate m_tryCount times. + SoupDelegateOpaque *opaq = new SoupDelegateOpaque; + opaq->dlg = makeICDelegate(&SoupMessageSendAsync::requestReceiver); + + soup_session_queue_message(m_soupSession, + m_soupMessage, + soupSessionCallback, + reinterpret_cast(opaq)); + } + + void sendAsyncIteration(SoupDelegateOpaque *opaq){ + // Replace used ICDelegate with new one without changing + // userdata ;-) + opaq->dlg = makeICDelegate(&SoupMessageSendAsync::requestReceiver); + soup_session_requeue_message(m_soupSession, + m_soupMessage); + } + + void requestReceiver(SoupSession *session, SoupMessage *msg, void *opaque){ + // We are in thread which called sendAsync function. + Assert(session == m_soupSession); + Assert(msg == m_soupMessage); + Assert(opaque != 0); + Assert(m_status == STATUS_SEND_ASYNC); + + m_tryLeft--; + + if (msg->status_code == SOUP_STATUS_OK) { + m_responseBuffer.resize(msg->response_body->length); + memcpy(&m_responseBuffer[0], + msg->response_body->data, + msg->response_body->length); + // We are done. + m_status = STATUS_IDLE; + delete static_cast(opaque); + + // call the delegate to outside! + return; + } + + // Error protocol // + if (m_tryLeft <= 0) { + m_status = STATUS_IDLE; + delete static_cast(opaque); + + // call the delegate to outside with error! + return; + } + + // create delegate and send the request once again. + sendAsyncIteration(reinterpret_cast(opaque)); + } + + static void soupSessionCallback(SoupSession *session, + SoupMessage *msg, + gpointer userdata) + { + // We are in main thread. We need to switch context. + // This delegate can switch context to dpl thread or main thread. + SoupDelegateOpaque *opaque; + opaque = reinterpret_cast(userdata); + opaque->dlg(session, msg, userdata); + } + + int m_tryLeft; + + GMainContext *m_mainContext; + SoupSession *m_soupSession; + SoupMessage *m_soupMessage; +}; + +} // namespace ValidationCore + +#endif diff --git a/modules/vcore/src/vcore/SoupMessageSendBase.cpp b/modules/vcore/src/vcore/SoupMessageSendBase.cpp new file mode 100644 index 0000000..4d1fafa --- /dev/null +++ b/modules/vcore/src/vcore/SoupMessageSendBase.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 0.1 + * @file SoupMessageSendBase.cpp + * @brief Simple wrapper for soup. + */ +#include "SoupMessageSendBase.h" + +#include +#include +#include + +namespace SoupWrapper { + +SoupMessageSendBase::SoupMessageSendBase() + : m_status(STATUS_IDLE) + , m_timeout(30) + , m_tryCount(5) +{} + +SoupMessageSendBase::~SoupMessageSendBase(){ + Assert(m_status == STATUS_IDLE); +} + +void SoupMessageSendBase::setHeader(const std::string &property, const std::string &value){ + Assert(m_status == STATUS_IDLE); + m_headerMap[property] = value; +} + +void SoupMessageSendBase::setHost(const std::string &host){ + Assert(m_status == STATUS_IDLE); + m_host = host; +} + +void SoupMessageSendBase::setRequest(const std::string &contentType, const MessageBuffer &message){ + Assert(m_status == STATUS_IDLE); + m_requestType = contentType; + m_requestBuffer = message; +} + +SoupMessageSendBase::MessageBuffer SoupMessageSendBase::getResponse() const { + Assert(m_status == STATUS_IDLE); + return m_responseBuffer; +} + +void SoupMessageSendBase::setTimeout(int seconds) { + Assert(m_status == STATUS_IDLE); + Assert(seconds >= 0); + m_timeout = seconds; +} + +void SoupMessageSendBase::setRetry(int retry) { + Assert(m_status == STATUS_IDLE); + Assert(retry >= 0); + m_tryCount = retry + 1; +} + + +SoupMessage* SoupMessageSendBase::createRequest(){ + SoupMessage *message; + + LogInfo("Soup message will be send to: " << m_host.c_str()); + + if (!m_requestBuffer.empty()) { + message = soup_message_new("POST", m_host.c_str()); + } else { + message = soup_message_new("GET", m_host.c_str()); + } + + if (!message) { + LogError("Error creating request!"); + return 0; + } + + FOREACH(it, m_headerMap){ + soup_message_headers_append(message->request_headers, + it->first.c_str(), + it->second.c_str()); + } + + if (!m_requestBuffer.empty()) { + soup_message_set_http_version(message, SOUP_HTTP_1_0); + soup_message_set_request(message, + m_requestType.c_str(), + SOUP_MEMORY_COPY, + &m_requestBuffer[0], + m_requestBuffer.size()); + } + soup_message_set_flags(message, SOUP_MESSAGE_NO_REDIRECT); + return message; +} + +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/SoupMessageSendBase.h b/modules/vcore/src/vcore/SoupMessageSendBase.h new file mode 100644 index 0000000..aaa5fb4 --- /dev/null +++ b/modules/vcore/src/vcore/SoupMessageSendBase.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 0.1 + * @file SoupMessageSendBase.h + * @brief Simple wrapper for soup. + */ +#ifndef _SRC_VALIDATION_CORE_SOUP_MESSAGE_SEND_BASE_H_ +#define _SRC_VALIDATION_CORE_SOUP_MESSAGE_SEND_BASE_H_ + +#include +#include +#include + +#include + +namespace SoupWrapper { + +class SoupMessageSendBase { + public: + + typedef std::vector MessageBuffer; + typedef std::map HeaderMap; + + enum RequestStatus { + REQUEST_STATUS_OK, + REQUEST_STATUS_CONNECTION_ERROR + }; + + SoupMessageSendBase(); + + virtual ~SoupMessageSendBase(); + + /** + * Add specific information to request header. + * + * @param[in] property property name (for example "Host") + * @param[in] value property value (for example "onet.pl:80") + */ + void setHeader(const std::string &property, + const std::string &value); + + /** + * Set request destination. + * + * @param[in] host - full path to source (http://onet.pl/index.html) + */ + void setHost(const std::string &host); + + /** + * Set body of request. + * + * @param[in] contentType (for example: "application/ocsp-request") + * @param[in] message body of reqeust + */ + void setRequest(const std::string &contentType, + const MessageBuffer &message); + + /** + * Set network timeout. Default is 30 seconds. + * + * @param[in] seconds timeout in seconds + */ + void setTimeout(int seconds); + + /** + * How many erros soup will accept before he will terminate connection. + * Default is 5. + * + * @param[in] retry number + */ + void setRetry(int retry); + + /** + * Get response from serwer. + */ + MessageBuffer getResponse() const; + + protected: + + SoupMessage* createRequest(); + + enum Status { + STATUS_IDLE, + STATUS_SEND_SYNC, + STATUS_SEND_ASYNC + }; + + Status m_status; + + int m_timeout; + int m_tryCount; + + std::string m_host; + std::string m_requestType; + MessageBuffer m_requestBuffer; + MessageBuffer m_responseBuffer; + HeaderMap m_headerMap; +}; + +} // namespace ValidationCore + +#endif diff --git a/modules/vcore/src/vcore/SoupMessageSendSync.cpp b/modules/vcore/src/vcore/SoupMessageSendSync.cpp new file mode 100644 index 0000000..bca8e3e --- /dev/null +++ b/modules/vcore/src/vcore/SoupMessageSendSync.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 0.1 + * @file SoupMessageSendSync.cpp + * @brief Implementation of soup synchronous interface. + */ +#include "SoupMessageSendSync.h" + +#include +#include + +#include + +#include + +namespace SoupWrapper { + +SoupMessageSendBase::RequestStatus SoupMessageSendSync::sendSync() +{ + Assert(m_status == STATUS_IDLE); + m_status = STATUS_SEND_SYNC; + + ScopedGMainContext context(g_main_context_new()); + + std::unique_ptr > + proxy(vconf_get_str(VCONFKEY_NETWORK_PROXY), free); + + std::unique_ptr > + proxyURI(soup_uri_new (proxy.get()), soup_uri_free); + + LogDebug("Proxy ptr:" << (void*)proxy.get() << + " Proxy addr: " << proxy.get()); + + for(int tryCount = 0; tryCount < m_tryCount; ++ tryCount){ + LogDebug("Try(" << tryCount << ") to download " << m_host); + + ScopedSoupSession session(soup_session_async_new_with_options( + SOUP_SESSION_ASYNC_CONTEXT, + &*context, + SOUP_SESSION_TIMEOUT, + m_timeout, + SOUP_SESSION_PROXY_URI, + proxyURI.get(), + NULL)); + + ScopedSoupMessage msg; + + msg.Reset(createRequest()); + + if (!msg) { + LogError("Unable to send HTTP request."); + m_status = STATUS_IDLE; + return REQUEST_STATUS_CONNECTION_ERROR; + } + soup_session_send_message(&*session, &*msg); + + // if (SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) + + if (msg->status_code == SOUP_STATUS_OK) { + m_responseBuffer.resize(msg->response_body->length); + memcpy(&m_responseBuffer[0], + msg->response_body->data, + msg->response_body->length); + // We are done. + m_status = STATUS_IDLE; + return REQUEST_STATUS_OK; + } else { + LogWarning("Soup failed with code " << msg->status_code + << " message \n------------\n" + << msg->response_body->data + << "\n--------------\n"); + } + } + + m_status = STATUS_IDLE; + return REQUEST_STATUS_CONNECTION_ERROR; +} + +} // namespave ValidationCore diff --git a/modules/vcore/src/vcore/SoupMessageSendSync.h b/modules/vcore/src/vcore/SoupMessageSendSync.h new file mode 100644 index 0000000..ebb451d --- /dev/null +++ b/modules/vcore/src/vcore/SoupMessageSendSync.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 0.1 + * @file SoupMessageSendSync.h + * @brief Wrapper for soup synchronous interface. + */ +#ifndef _SRC_VALIDATION_CORE_SOUP_MESSAGE_SEND_SYNC_H_ +#define _SRC_VALIDATION_CORE_SOUP_MESSAGE_SEND_SYNC_H_ + +#include "SoupMessageSendBase.h" + +#include + +namespace SoupWrapper { + +class SoupMessageSendSync : public SoupMessageSendBase { + public: + RequestStatus sendSync(); + protected: + typedef WRT::ScopedGPointer ScopedSoupMessage; + typedef WRT::ScopedGPointer ScopedSoupSession; + typedef WRT::ScopedGPointer ScopedGMainContext; +}; + +} // namespace ValidationCore + +#endif diff --git a/modules/vcore/src/vcore/VCore.cpp b/modules/vcore/src/vcore/VCore.cpp new file mode 100644 index 0000000..60bf47e --- /dev/null +++ b/modules/vcore/src/vcore/VCore.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file VCore.cpp + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @brief + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace { +DPL::DB::ThreadDatabaseSupport threadInterface( + VCoreDatabaseConnectionTraits::Address(), + VCoreDatabaseConnectionTraits::Flags()); +} // namespace anonymous + +namespace ValidationCore { + +void AttachToThread(void){ + threadInterface.AttachToThread(); +} + +void DetachFromThread(void){ + threadInterface.DetachFromThread(); +} + +DPL::DB::ThreadDatabaseSupport& ThreadInterface(void) { + return threadInterface; +} + +bool VCoreInit(const std::string& configFilePath, + const std::string& configSchemaPath, + const std::string& databasePath) +{ + SSL_library_init(); + g_thread_init(NULL); + g_type_init(); + + LogDebug("Initializing VCore"); + Config &globalConfig = ConfigSingleton::Instance(); + bool returnValue = globalConfig.setXMLConfigPath(configFilePath) && + globalConfig.setXMLSchemaPath(configSchemaPath) && + globalConfig.setDatabasePath(databasePath); + + Assert(ThreadInterface().CheckTableExist(DB_CHECKSUM_STR) && + "Not a valid vcore database version"); + + return returnValue; +} + +} // namespace ValidationCore + diff --git a/modules/vcore/src/vcore/VCore.h b/modules/vcore/src/vcore/VCore.h new file mode 100644 index 0000000..bb3029a --- /dev/null +++ b/modules/vcore/src/vcore/VCore.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file VCore.h + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#ifndef _VCORE_SRC_VCORE_VCORE_H_ +#define _VCORE_SRC_VCORE_VCORE_H_ + +#include + +namespace ValidationCore { +/* + * configFilePath - path to XML config file with certificates configuration + * + * configSchemaPath - XMLschema of config file + * + * databasePath - path to database with OCSP/CRL cache. + * + * This function could be run only once. If you call it twice it will + * return false and non data will be set. + * + */ +bool VCoreInit(const std::string& configFilePath, + const std::string& configSchemaPath, + const std::string& databasePath); + +/* + * All thread with are using OCSP/CRL must call AttachToThread function before + * it can call OCSP/CRL. More than one thread could be Attach with OCPS/CRL. + * + * You mast attach thread to OCSP/CRL because OCSP/CRL is using database + * CertificateCachedDAO. For each thread that will be using this database + * vcore must create internal structure (with connection info). + * + */ +void AttachToThread(void); +void DetachFromThread(void); + +} // namespace ValidationCore + +#endif // _VCORE_SRC_VCORE_VCORE_H_ + diff --git a/modules/vcore/src/vcore/VCorePrivate.h b/modules/vcore/src/vcore/VCorePrivate.h new file mode 100644 index 0000000..ed85958 --- /dev/null +++ b/modules/vcore/src/vcore/VCorePrivate.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file VCore.h + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#ifndef _VCORE_SRC_VCORE_VCOREPRIVATE_H_ +#define _VCORE_SRC_VCORE_VCOREPRIVATE_H_ + +#include +#include +#include +#include + +namespace ValidationCore { +DPL::DB::ThreadDatabaseSupport& ThreadInterface(void); +} // namespace ValidationCore + +#endif // _VCORE_SRC_VCORE_VCORE_H_ + diff --git a/modules/vcore/src/vcore/ValidatorCommon.h b/modules/vcore/src/vcore/ValidatorCommon.h new file mode 100644 index 0000000..8815239 --- /dev/null +++ b/modules/vcore/src/vcore/ValidatorCommon.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file ValidatorCommon.h + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief This file contais definictions of common types used in ValidationCore. + */ +#ifndef _VALIDATORCOMMON_H_ +#define _VALIDATORCOMMON_H_ + +#include +#include +#include + +namespace ValidationCore { +typedef std::set< std::string > ReferenceSet; +typedef std::list< std::string > ObjectList; + +/* + * base deleter func + */ +template +struct ValidatorCoreUniversalFree {}; + +// Template Specialization +#define VC_DECLARE_DELETER(type, function) \ + template <> \ + struct ValidatorCoreUniversalFree { \ + void universal_free(type *ptr){ \ + if (ptr) { \ + function(ptr); } \ + } \ + }; + +template +class AutoPtr +{ + public: + AutoPtr(T *ptr) : + m_data(ptr) + { + } + + AutoPtr(const AutoPtr &second) + { + m_data = second.m_data; + second.m_data = 0; + } + + AutoPtr & operator=(const AutoPtr &second) + { + if (this != &second) { + ValidatorCoreUniversalFree deleter; + deleter.universal_free(m_data); + m_data = second.m_data; + second.m_data = 0; + } + return *this; + } + + /** + * overloaded -> operator, so smart ptr could act as ordinary ptr + */ + T* operator->() + { + return m_data; + } + + ~AutoPtr() + { + ValidatorCoreUniversalFree deleter; + deleter.universal_free(m_data); + } + + /** + * get internal pointer + */ + T* get(void) + { + return m_data; + } + + private: + mutable T *m_data; +}; +} // namespace ValidationCore + +#endif // _VALIDATORCOMMON_H_ diff --git a/modules/vcore/src/vcore/ValidatorFactories.cpp b/modules/vcore/src/vcore/ValidatorFactories.cpp new file mode 100644 index 0000000..bd72237 --- /dev/null +++ b/modules/vcore/src/vcore/ValidatorFactories.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#include + +#include + +#include +#include +#include + +namespace ValidationCore { + +const CertificateIdentifier& createCertificateIdentifier() +{ + static CertificateIdentifier certificateIdentifier; + static bool initialized = false; + if (!initialized) { + CertificateConfigReader reader; + std::string file = + ConfigSingleton::Instance().getXMLConfigPath(); + std::string schema = + ConfigSingleton::Instance().getXMLSchemaPath(); + reader.initialize(file, schema); + reader.read(certificateIdentifier); + initialized = true; + } + return certificateIdentifier; +} + +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/ValidatorFactories.h b/modules/vcore/src/vcore/ValidatorFactories.h new file mode 100644 index 0000000..075eef1 --- /dev/null +++ b/modules/vcore/src/vcore/ValidatorFactories.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#ifndef _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_VALIDATORFACTORY_H_ +#define _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_VALIDATORFACTORY_H_ + +#include + +namespace ValidationCore { +// First use of CertificateIdentificator should initialized it. +// We do not want to create cyclic dependencies between +// CertificateConfigReader and CertificateIdentificator so +// we are using factory method to create CertificateIdentificator. + +const CertificateIdentifier& createCertificateIdentifier(); +} // namespace ValidationCore + +#endif // _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_VALIDATORFACTORY_H_ diff --git a/modules/vcore/src/vcore/VerificationStatus.cpp b/modules/vcore/src/vcore/VerificationStatus.cpp new file mode 100644 index 0000000..98199ad --- /dev/null +++ b/modules/vcore/src/vcore/VerificationStatus.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "VerificationStatus.h" + +namespace ValidationCore { +VerificationStatus VerificationStatusSet::convertToStatus() const +{ + if (m_verdictMap & VERIFICATION_STATUS_REVOKED) { + return VERIFICATION_STATUS_REVOKED; + } + + if (m_verdictMap & VERIFICATION_STATUS_VERIFICATION_ERROR) { + return VERIFICATION_STATUS_VERIFICATION_ERROR; + } + + if (m_verdictMap & VERIFICATION_STATUS_ERROR) { + return VERIFICATION_STATUS_ERROR; + } + + if (m_verdictMap & VERIFICATION_STATUS_UNKNOWN) { + return VERIFICATION_STATUS_UNKNOWN; + } + + if (m_verdictMap & VERIFICATION_STATUS_CONNECTION_FAILED) { + return VERIFICATION_STATUS_CONNECTION_FAILED; + } + + if (m_verdictMap & VERIFICATION_STATUS_NOT_SUPPORT) { + return VERIFICATION_STATUS_NOT_SUPPORT; + } + + if (m_verdictMap & VERIFICATION_STATUS_GOOD) { + return VERIFICATION_STATUS_GOOD; + } + + return VERIFICATION_STATUS_ERROR; +} +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/VerificationStatus.h b/modules/vcore/src/vcore/VerificationStatus.h new file mode 100644 index 0000000..67eecac --- /dev/null +++ b/modules/vcore/src/vcore/VerificationStatus.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _SRC_VALIDATION_CORE_VERIFICATION_STATUS_H_ +#define _SRC_VALIDATION_CORE_VERIFICATION_STATUS_H_ + +namespace ValidationCore { +enum VerificationStatus +{ + //! The certificate has not been revoked. + /*! Connection to OCSP responder was successful and the certificate + * has not been revoked. + */ + VERIFICATION_STATUS_GOOD = 1, + + //! The certificate has been revoked. + /*! Connection to OCSP responder was successful and the certificate + * has been revoked. + * RFC2560: "The "revoked" state indicates that the certificate has + * been revoked (either permanantly or temporarily + * (on hold))." + */ + VERIFICATION_STATUS_REVOKED = 1 << 1, + + //! The certificate status is unknown. + /*! Connection to OCSP responder was successful and the certificate + * has unknown status. + * + * RFC2560: "The "unknown" state indicates that the responder + * doesn't know about the certificate being requested." + */ + VERIFICATION_STATUS_UNKNOWN = 1 << 2, + + //! The certificate status was not figure out. + /*! The response from ocsp/crl server contains broken signature. */ + VERIFICATION_STATUS_VERIFICATION_ERROR = 1 << 3, + + //! The certificate status was not figure out. + /*! The certificate does not contain ocsp/crl extension. */ + VERIFICATION_STATUS_NOT_SUPPORT = 1 << 4, + + //! The certificate status was not figure out. + /*! The CertMgr could not connect to OCSP responder. */ + VERIFICATION_STATUS_CONNECTION_FAILED = 1 << 5, + + //! The certificate status is unknown due to internal error inside OCSP + VERIFICATION_STATUS_ERROR = 1 << 6 +}; + +class VerificationStatusSet +{ + public: + VerificationStatusSet() : m_verdictMap(0) + { + } + + inline void add(VerificationStatus status) + { + m_verdictMap |= status; + } + + inline bool contains(VerificationStatus status) const + { + return m_verdictMap & status; + } + + inline bool isEmpty() const + { + return 0 == m_verdictMap; + } + + inline void operator+=(const VerificationStatusSet &second) + { + m_verdictMap |= second.m_verdictMap; + } + + inline void reset() + { + m_verdictMap = 0; + } + + VerificationStatus convertToStatus() const; + + private: + unsigned int m_verdictMap; +}; + +/* TODO this status should be defined in wrt-engine sources */ +enum WidgetVerificationStatus +{ + // All certificate has been veficated and all certificates are good. + // Widget is able to be installed. + WIDGET_VERIFICATION_STATUS_GOOD, + // Some certificate has been revoked. Widget is not able to be installed. + WIDGET_VERIFICATION_STATUS_REVOKED, +}; +} // namespace ValidationCore + +#endif // _SRC_VALIDATION_CORE_VERIFICATION_STATUS_H_ diff --git a/modules/vcore/src/vcore/WacOrigin.cpp b/modules/vcore/src/vcore/WacOrigin.cpp new file mode 100644 index 0000000..7ca0174 --- /dev/null +++ b/modules/vcore/src/vcore/WacOrigin.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#include "WacOrigin.h" + +#include +#include +#include + +#include <> + +#include +#include "ValidatorCommon.h" + +namespace { +const std::string SCHEME_HTTP = "http"; +const std::string SCHEME_HTTPS = "https"; +const int PORT_HTTP = 80; +const int PORT_HTTPS = 443; +} + +namespace ValidationCore { +VC_DECLARE_DELETER(iri_struct, iri_destroy) + +WacOrigin::WacOrigin(const std::string &url) : + m_port(0), + m_parseFailed(false) +{ + parse(url.c_str()); +} + +WacOrigin::WacOrigin(const char *url) : + m_port(0), + m_parseFailed(false) +{ + parse(url); +} + +bool WacOrigin::operator==(const WacOrigin &second) const +{ + if (m_parseFailed || second.m_parseFailed) { + return false; + } + + return (m_scheme == second.m_scheme) && + (m_host == second.m_host) && + (m_port == second.m_port); +} + +void WacOrigin::parse(const char *url) +{ + // Step are taken from algorihtm: + // http://www.w3.org/TR/html5/origin-0.html#origin-0 + + // Step 1 + // Step 2 + AutoPtr iri(iri_parse(url)); + if (!iri.get()) { + m_parseFailed = true; + return; + } + + if (iri->scheme) { + m_scheme = iri->scheme; + } else { + m_parseFailed = true; + return; + } + + // Step 3 - Skip this point. + // WAC 2.0 PRV - we are suport only "http" and "https" schemas. + + // Step 4 - Todo + + // Step 5 + std::transform(m_scheme.begin(), m_scheme.end(), m_scheme.begin(), tolower); + + // Step 6 - we only support "http" and "https" schemas + if ((m_scheme != SCHEME_HTTP) && (m_scheme != SCHEME_HTTPS)) { + m_parseFailed = true; + return; + } + + // Step 7 - Skip. We do not support "file" schema. + + // Step 8 + if (iri->host) { + m_host = iri->host; + } else { + m_parseFailed = true; + return; + } + + // Step 9 + char *output = NULL; + if (IDNA_SUCCESS != + idna_to_ascii_lz(m_host.c_str(), &output, IDNA_USE_STD3_ASCII_RULES)) { + LogError("libidn error"); + m_parseFailed = true; + free(output); + return; + } + m_host = output; + free(output); + + // Step 10 + std::transform(m_host.begin(), m_host.end(), m_host.begin(), ::tolower); + + // Step 11 + if (iri->port) { + m_port = iri->port; + } else { + setPort(); + } + + // Step 12 - Skip it. We do not return anything. + // User should create geters if he need access to schema/host/port. +} + +void WacOrigin::setPort() +{ + if (SCHEME_HTTP == m_scheme) { + m_port = PORT_HTTP; + return; + } else if (SCHEME_HTTPS == m_scheme) { + m_port = PORT_HTTPS; + return; + } else { + LogDebug("Scheme " << m_scheme << " is not support by WAC2.0"); + m_parseFailed = true; + } +} +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/WacOrigin.h b/modules/vcore/src/vcore/WacOrigin.h new file mode 100644 index 0000000..d706fe3 --- /dev/null +++ b/modules/vcore/src/vcore/WacOrigin.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief This is stub for HTML5Origin implementation. + * This implementation is compatible with WAC 2.0 PRV requirements + * and is _not_ full compatible with ORIGIN algorithm requirements + * defined in http://www.w3.org/TR/html5/origin-0.html#origin-0 + */ +#ifndef _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_HTML5ORIGIN_H_ +#define _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_HTML5ORIGIN_H_ + +#include + +namespace ValidationCore { +class WacOrigin +{ + public: + + WacOrigin(const std::string &url); + WacOrigin(const char *url); + + bool operator!=(const WacOrigin &second) const + { + return !(operator==(second)); + } + + bool operator==(const WacOrigin &second) const; + + private: + void parse(const char *url); + void setPort(); + + std::string m_scheme; + std::string m_host; + int m_port; + bool m_parseFailed; // if parsing failed we should return unique identifier +}; +} //namespace ValidationCore + +#endif // _WRT_ENGINE_SRC_INSTALLER_CORE_VALIDATION_CORE_HTML5ORIGIN_H_ diff --git a/modules/vcore/src/vcore/XmlsecAdapter.cpp b/modules/vcore/src/vcore/XmlsecAdapter.cpp new file mode 100644 index 0000000..2c3f231 --- /dev/null +++ b/modules/vcore/src/vcore/XmlsecAdapter.cpp @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file XmlsecAdapter.cpp + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ + +/* + * Copyright (C) 2002-2003 Aleksey Sanin. All Rights Reserved. + * + * 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. + */ + + +#include +#include + +#include +#include +#include + +#ifndef XMLSEC_NO_XSLT +#include +#endif /* XMLSEC_NO_XSLT */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "XmlsecAdapter.h" +#include +IMPLEMENT_SINGLETON(ValidationCore::XmlSec) + +namespace ValidationCore { +VC_DECLARE_DELETER(xmlSecKeysMngr, xmlSecKeysMngrDestroy) + +static const char* DIGEST_MD5 = "md5"; + +std::string XmlSec::s_prefixPath; + +int XmlSec::fileMatchCallback(const char *filename) +{ + std::string path = s_prefixPath + filename; + return xmlFileMatch(path.c_str()); +} + +void* XmlSec::fileOpenCallback(const char *filename) +{ + std::string path = s_prefixPath + filename; + LogDebug("Xmlsec opening: " << path); + return xmlFileOpen(path.c_str()); +} + +int XmlSec::fileReadCallback(void *context, + char *buffer, + int len) +{ + return xmlFileRead(context, buffer, len); +} + +int XmlSec::fileCloseCallback(void *context) +{ + return xmlFileClose(context); +} + +void XmlSec::fileExtractPrefix(XmlSecContext *context) +{ + if (!(context->workingDirectory.empty())) { + s_prefixPath = context->workingDirectory; + return; + } + + s_prefixPath = context->signatureFile; + size_t pos = s_prefixPath.rfind('/'); + if (pos == std::string::npos) { + s_prefixPath.clear(); + } else { + s_prefixPath.erase(pos + 1, std::string::npos); + } +} + +XmlSec::XmlSec() : + m_initialized(false) +{ + LIBXML_TEST_VERSION + xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS; + xmlSubstituteEntitiesDefault(1); +#ifndef XMLSEC_NO_XSLT + xmlIndentTreeOutput = 1; +#endif + + if (xmlSecInit() < 0) { + LogError("Xmlsec initialization failed."); + ThrowMsg(Exception::InternalError, "Xmlsec initialization failed."); + } + + if (xmlSecCheckVersion() != 1) { + xmlSecShutdown(); + LogError("Loaded xmlsec library version is not compatible."); + ThrowMsg(Exception::InternalError, + "Loaded xmlsec library version is not compatible."); + } + +#ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING + if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) { + xmlSecShutdown(); + LogError( + "Error: unable to load default xmlsec-crypto library. Make sure " + "that you have it installed and check shared libraries path " + "(LD_LIBRARY_PATH) envornment variable."); + ThrowMsg(Exception::InternalError, + "Unable to load default xmlsec-crypto library."); + } +#endif + + if (xmlSecCryptoAppInit(NULL) < 0) { + xmlSecShutdown(); + LogError("Crypto initialization failed."); + ThrowMsg(Exception::InternalError, "Crypto initialization failed."); + } + + if (xmlSecCryptoInit() < 0) { + xmlSecCryptoAppShutdown(); + xmlSecShutdown(); + LogError("Xmlsec-crypto initialization failed."); + ThrowMsg(Exception::InternalError, + "Xmlsec-crypto initialization failed."); + } + + m_initialized = true; +} + +void XmlSec::deinitialize(void) +{ + Assert(m_initialized); + + /* Shutdown xmlsec-crypto library */ + xmlSecCryptoShutdown(); + + /* Shutdown crypto library */ + xmlSecCryptoAppShutdown(); + + /* Shutdown xmlsec library */ + xmlSecShutdown(); + + /* Shutdown libxslt/libxml */ +#ifndef XMLSEC_NO_XSLT + xsltCleanupGlobals(); +#endif /* XMLSEC_NO_XSLT */ + + s_prefixPath.clear(); + m_initialized = false; +} + +XmlSec::~XmlSec() +{ + if (m_initialized) { + deinitialize(); + } +} + +XmlSec::Result XmlSec::validateFile(XmlSecContext *context, + xmlSecKeysMngrPtr mngr) +{ + xmlDocPtr doc = NULL; + xmlNodePtr node = NULL; + xmlSecDSigCtxPtr dsigCtx = NULL; + int size, res = -1; + + fileExtractPrefix(context); + LogDebug("Prefix path: " << s_prefixPath); + + xmlSecIOCleanupCallbacks(); + + xmlSecIORegisterCallbacks( + fileMatchCallback, + fileOpenCallback, + fileReadCallback, + fileCloseCallback); + + /* load file */ + doc = xmlParseFile(context->signatureFile.c_str()); + if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL)) { + LogWarning("Unable to parse file " << context->signatureFile); + goto done; + } + + /* find start node */ + node = xmlSecFindNode(xmlDocGetRootElement( + doc), xmlSecNodeSignature, xmlSecDSigNs); + if (node == NULL) { + LogWarning("Start node not found in " << context->signatureFile); + goto done; + } + + /* create signature context */ + dsigCtx = xmlSecDSigCtxCreate(mngr); + if (dsigCtx == NULL) { + LogError("Failed to create signature context."); + goto done; + } + + if (context->allowBrokenChain) { + dsigCtx->keyInfoReadCtx.flags |= + XMLSEC_KEYINFO_FLAGS_ALLOW_BROKEN_CHAIN; + } + + if (context->validationTime) { + LogDebug("Setting validation time."); + dsigCtx->keyInfoReadCtx.certsVerificationTime = context->validationTime; + } + + /* Verify signature */ + if (xmlSecDSigCtxVerify(dsigCtx, node) < 0) { + LogWarning("Signature verify error."); + goto done; + } + + if (dsigCtx->keyInfoReadCtx.flags2 & + XMLSEC_KEYINFO_ERROR_FLAGS_BROKEN_CHAIN) { + LogWarning("XMLSEC_KEYINFO_FLAGS_ALLOW_BROKEN_CHAIN was set to true!"); + LogWarning("Signature contains broken chain!"); + context->errorBrokenChain = true; + } + + /* print verification result to stdout */ + if (dsigCtx->status == xmlSecDSigStatusSucceeded) { + LogDebug("Signature is OK"); + res = 0; + } else { + LogDebug("Signature is INVALID"); + goto done; + } + + if (dsigCtx->c14nMethod && dsigCtx->c14nMethod->id && + dsigCtx->c14nMethod->id->name) { + LogInfo("Canonicalization method: " << + reinterpret_cast(dsigCtx->c14nMethod->id->name)); + } + + size = xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences)); + for (int i = 0; i < size; ++i) { + xmlSecDSigReferenceCtxPtr dsigRefCtx = + (xmlSecDSigReferenceCtxPtr)xmlSecPtrListGetItem(&(dsigCtx-> + signedInfoReferences), + i); + if (dsigRefCtx && dsigRefCtx->uri) { + if (dsigRefCtx->digestMethod && dsigRefCtx->digestMethod->id && + dsigRefCtx->digestMethod->id->name) { + const char* pDigest = + reinterpret_cast(dsigRefCtx->digestMethod->id + ->name); + std::string strDigest(pDigest); + LogInfo("reference digest method: " << + reinterpret_cast(dsigRefCtx->digestMethod + ->id + ->name)); + if (strDigest == DIGEST_MD5) { + LogWarning("MD5 digest method used! Please use sha"); + res = -1; + break; + } + } + context->referenceSet.insert(std::string(reinterpret_cast( + dsigRefCtx->uri))); + } + } + +done: + /* cleanup */ + if (dsigCtx != NULL) { + xmlSecDSigCtxDestroy(dsigCtx); + } + + if (doc != NULL) { + xmlFreeDoc(doc); + } + + if (res) { + return ERROR_INVALID_SIGNATURE; + } + return NO_ERROR; +} + +void XmlSec::loadDERCertificateMemory(XmlSecContext *context, + xmlSecKeysMngrPtr mngr) +{ + unsigned char *derCertificate = NULL; + int size = i2d_X509(context->certificatePtr->getX509(), &derCertificate); + + if (!derCertificate) { + LogError("Failed during x509 conversion to der format."); + ThrowMsg(Exception::InternalError, + "Failed during x509 conversion to der format."); + } + + if (xmlSecCryptoAppKeysMngrCertLoadMemory(mngr, + derCertificate, + size, + xmlSecKeyDataFormatDer, + xmlSecKeyDataTypeTrusted) < 0) { + OPENSSL_free(derCertificate); + LogError("Failed to load der certificate from memory."); + ThrowMsg(Exception::InternalError, + "Failed to load der certificate from memory."); + } + + OPENSSL_free(derCertificate); +} + +void XmlSec::loadPEMCertificateFile(XmlSecContext *context, + xmlSecKeysMngrPtr mngr) +{ + if (xmlSecCryptoAppKeysMngrCertLoad(mngr, + context->certificatePath.c_str(), + xmlSecKeyDataFormatPem, + xmlSecKeyDataTypeTrusted) < 0) { + LogError("Failed to load PEM certificate from file."); + ThrowMsg(Exception::InternalError, + "Failed to load PEM certificate from file."); + } +} + +XmlSec::Result XmlSec::validate(XmlSecContext *context) +{ + Assert(context); + Assert(!(context->signatureFile.empty())); + Assert(context->certificatePtr.Get() || !(context->certificatePath.empty())); + + if (!m_initialized) { + LogError("XmlSec is not initialized."); + ThrowMsg(Exception::InternalError, "XmlSec is not initialized"); + } + + AutoPtr mngr(xmlSecKeysMngrCreate()); + + if (!mngr.get()) { + LogError("Failed to create keys manager."); + ThrowMsg(Exception::InternalError, "Failed to create keys manager."); + } + + if (xmlSecCryptoAppDefaultKeysMngrInit(mngr.get()) < 0) { + LogError("Failed to initialize keys manager."); + ThrowMsg(Exception::InternalError, "Failed to initialize keys manager."); + } + context->referenceSet.clear(); + + if (context->certificatePtr.Get()) { + loadDERCertificateMemory(context, mngr.get()); + } + + if (!context->certificatePath.empty()) { + loadPEMCertificateFile(context, mngr.get()); + } + + return validateFile(context, mngr.get()); +} +} // namespace ValidationCore diff --git a/modules/vcore/src/vcore/XmlsecAdapter.h b/modules/vcore/src/vcore/XmlsecAdapter.h new file mode 100644 index 0000000..4104c88 --- /dev/null +++ b/modules/vcore/src/vcore/XmlsecAdapter.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file XmlSecAdapter.h + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief + */ +#ifndef _XMLSECADAPTER_H_ +#define _XMLSECADAPTER_H_ + +#include + +#include +#include +#include + +#include "Certificate.h" +#include "ValidatorCommon.h" + +namespace ValidationCore { +class XmlSec : public DPL::Noncopyable +{ + public: + + struct XmlSecContext + { + /* You _must_ set one of the value: certificatePath or certificate. */ + XmlSecContext() : + validationTime(0), + allowBrokenChain(false), + errorBrokenChain(false) + { + } + + /* + * Absolute path to signature file. + */ + std::string signatureFile; + /* + * Direcotory with signed data. + * If you leave it empty xmlsec will use directory extracted + * from signatureFile. + */ + std::string workingDirectory; + /* + * Path to trusted certificate. + */ + std::string certificatePath; + /* + * Trusted certificate. In most cases it should be Root CA certificate. + */ + CertificatePtr certificatePtr; + /* + * Validation date. + * 0 - uses current time. + */ + time_t validationTime; + /* + * Input parameter. + * If true, signature validation will not be interrupted by chain error. + * If true and chain is broken then the value errorBrokenChain will be + * set to true. + */ + bool allowBrokenChain; + /* + * Output parameter. + * This will be set if chain is incomplete or broken. + */ + bool errorBrokenChain; + /* + * Output parameter. + * Reference checked by xmlsec + */ + ReferenceSet referenceSet; + }; + + enum Result + { + NO_ERROR, + ERROR_INVALID_SIGNATURE + }; + + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, InternalError) + }; + + /* + * Context - input/output param. + */ + Result validate(XmlSecContext *context); + protected: + XmlSec(); + ~XmlSec(); + private: + void deinitialize(void); + + void loadDERCertificateMemory(XmlSecContext *context, + xmlSecKeysMngrPtr mngr); + void loadPEMCertificateFile(XmlSecContext *context, + xmlSecKeysMngrPtr mngr); + Result validateFile(XmlSecContext *context, + xmlSecKeysMngrPtr mngr); + + bool m_initialized; + + static std::string s_prefixPath; + static int fileMatchCallback(const char *filename); + static void* fileOpenCallback(const char *filename); + static int fileReadCallback(void *context, + char *buffer, + int len); + static int fileCloseCallback(void *context); + static void fileExtractPrefix(XmlSecContext *context); +}; + +typedef DPL::Singleton XmlSecSingleton; +} // namespace ValidationCore +#endif // _XMLSECVERIFICATOR_H_ diff --git a/modules/vcore/src/vcore/scoped_gpointer.h b/modules/vcore/src/vcore/scoped_gpointer.h new file mode 100644 index 0000000..78772df --- /dev/null +++ b/modules/vcore/src/vcore/scoped_gpointer.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @file scoped_fclose.h + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of scoped fclose RAII + */ +#ifndef WRT_ENGINE_SRC_COMMON_SCOPED_GPOINTER_H +#define WRT_ENGINE_SRC_COMMON_SCOPED_GPOINTER_H + +#include +#include + +#include +#include + +namespace WRT { +struct ScopedGPointerPolicy +{ + typedef gpointer Type; + static Type NullValue() + { + return NULL; + } + static void Destroy(Type pointer) + { + if (pointer != NULL) { + g_object_unref(pointer); + } + } +}; + +template +class ScopedGPointer : public DPL::ScopedResource +{ + typedef ScopedGPointerPolicy Policy; + typedef DPL::ScopedResource BaseType; + + public: + explicit ScopedGPointer(typename Policy::Type pointer = + Policy::NullValue()) : + BaseType(pointer) + { + } + + Class *operator->() const throw() + { + Assert(this->m_value != Policy::NullValue() && + "Dereference of scoped NULL pointer!"); + return static_cast(this->m_value); + } + + Class & operator *() const throw() + { + Assert(this->m_value != Policy::NullValue() && + "Dereference of scoped NULL pointer!"); + return *static_cast(this->m_value); + } +}; +} // namespace WRT + +#endif // WRT_ENGINE_SRC_COMMON_SCOPED_GPOINTER_H diff --git a/modules/widget_dao/CMakeLists.txt b/modules/widget_dao/CMakeLists.txt new file mode 100644 index 0000000..82403c9 --- /dev/null +++ b/modules/widget_dao/CMakeLists.txt @@ -0,0 +1,142 @@ +ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_BINARY_DIR}/modules/widget_dao/database_checksum.h + COMMAND ${CMAKE_SOURCE_DIR}/modules/widget_dao/orm/gen_db_md5.sh + ARGS ${CMAKE_BINARY_DIR}/modules/widget_dao/database_checksum.h + ${CMAKE_SOURCE_DIR}/modules/widget_dao/orm/wrt_db + ${CMAKE_SOURCE_DIR}/modules/widget_dao/orm/iana_db + DEPENDS ${CMAKE_SOURCE_DIR}/modules/widget_dao/orm/wrt_db + ${CMAKE_SOURCE_DIR}/modules/widget_dao/orm/iana_db + ${CMAKE_SOURCE_DIR}/modules/widget_dao/orm/gen_db_md5.sh + COMMENT "Generating WRT database checksum" + ) + +ADD_CUSTOM_COMMAND( OUTPUT .wrt.db + COMMAND rm -f ${CMAKE_CURRENT_BINARY_DIR}/.wrt.db + COMMAND gcc -Wall -include ${CMAKE_BINARY_DIR}/modules/widget_dao/database_checksum.h -I${PROJECT_SOURCE_DIR}/modules/db/include -I${PROJECT_SOURCE_DIR}/modules/widget_dao/orm -E ${PROJECT_SOURCE_DIR}/modules/widget_dao/orm/wrt_db_sql_generator.h | grep --invert-match "^#" > ${CMAKE_CURRENT_BINARY_DIR}/wrt_db.sql + COMMAND sqlite3 ${CMAKE_CURRENT_BINARY_DIR}/.wrt.db ".read ${CMAKE_CURRENT_BINARY_DIR}/wrt_db.sql" || rm -f ${CMAKE_CURRENT_BINARY_DIR}/.wrt.db + DEPENDS ${CMAKE_BINARY_DIR}/modules/widget_dao/database_checksum.h ${PROJECT_SOURCE_DIR}/modules/widget_dao/orm/wrt_db_sql_generator.h ${PROJECT_SOURCE_DIR}/modules/widget_dao/orm/wrt_db ${PROJECT_SOURCE_DIR}/modules/widget_dao/orm/iana_db + ) + +ADD_CUSTOM_COMMAND( OUTPUT .wrt.db-journal + COMMAND touch + ARGS ${CMAKE_CURRENT_BINARY_DIR}/.wrt.db-journal + ) + +ADD_CUSTOM_TARGET(Sqlite3DbWRT ALL DEPENDS .wrt.db .wrt.db-journal) + +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/wrt_db.sql + DESTINATION share/wrt-engine/ + ) + +############################################################################### + +INCLUDE(FindPkgConfig) + +PKG_CHECK_MODULES(WRT_DAO_DEPS + cert-svc + ecore + appcore-efl + libxml-2.0 + openssl + REQUIRED) + +set(WRT_DAO_RO_SOURCES + dao/config_parser_data.cpp + dao/common_dao_types.cpp + dao/feature_dao_read_only.cpp + dao/global_dao_read_only.cpp + dao/global_config.cpp + dao/path_builder.cpp + dao/plugin_dao_read_only.cpp + dao/property_dao_read_only.cpp + dao/widget_dao_read_only.cpp + dao/webruntime_database.cpp + dao/WrtDatabase.cpp +) + +set(WRT_DAO_RW_SOURCES + dao/feature_dao.cpp + dao/global_dao.cpp + dao/plugin_dao.cpp + dao/property_dao.cpp + dao/widget_dao.cpp +) + +ADD_DEFINITIONS("-DSEPARATED_SINGLETON_IMPLEMENTATION") + +SET(WRT_DAO_INCLUDE_DIRS + ${PROJECT_SOURCE_DIR}/modules/widget_dao/include + ${PROJECT_SOURCE_DIR}/modules/event/include + ${PROJECT_SOURCE_DIR}/modules/widget_dao/orm + ${PROJECT_SOURCE_DIR}/modules/core/include + ${PROJECT_SOURCE_DIR}/modules/db/include + ${PROJECT_SOURCE_DIR}/modules/log/include + ${WRT_DAO_DEPS_INCLUDE_DIRS} + ) + +INCLUDE_DIRECTORIES(${WRT_DAO_INCLUDE_DIRS}) + +ADD_LIBRARY(${TARGET_WRT_DAO_RO_LIB} SHARED + ${WRT_DAO_RO_SOURCES} +) +SET_TARGET_PROPERTIES(${TARGET_WRT_DAO_RO_LIB} PROPERTIES + SOVERSION ${VERSION}) + +SET_TARGET_PROPERTIES(${TARGET_WRT_DAO_RO_LIB} PROPERTIES + COMPILE_FLAGS -fPIC) + +SET_TARGET_PROPERTIES(${TARGET_WRT_DAO_RO_LIB} PROPERTIES + COMPILE_FLAGS "-include ${CMAKE_BINARY_DIR}/modules/widget_dao/database_checksum.h") + +target_link_libraries(${TARGET_WRT_DAO_RO_LIB} + ${TARGET_DPL_EFL} + ${TARGET_DPL_DB_EFL} + ${WRT_DAO_DEPS_LIBRARIES}) + +ADD_LIBRARY(${TARGET_WRT_DAO_RW_LIB} SHARED ${WRT_DAO_RW_SOURCES}) + +SET_TARGET_PROPERTIES(${TARGET_WRT_DAO_RW_LIB} PROPERTIES + SOVERSION ${VERSION}) + +SET_TARGET_PROPERTIES(${TARGET_WRT_DAO_RW_LIB} PROPERTIES COMPILE_FLAGS -fPIC) + +SET_TARGET_PROPERTIES(${TARGET_WRT_DAO_RW_LIB} PROPERTIES + COMPILE_FLAGS "-include ${CMAKE_BINARY_DIR}/modules/widget_dao/database_checksum.h") + +target_link_libraries(${TARGET_WRT_DAO_RW_LIB} + ${TARGET_WRT_DAO_RO_LIB}) + +INSTALL(TARGETS ${TARGET_WRT_DAO_RO_LIB} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + ) + +INSTALL(TARGETS ${TARGET_WRT_DAO_RW_LIB} + DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + ) + +INSTALL(FILES + include/dpl/wrt-dao-ro/config_parser_data.h + include/dpl/wrt-dao-ro/common_dao_types.h + include/dpl/wrt-dao-ro/feature_dao_read_only.h + include/dpl/wrt-dao-ro/feature_model.h + include/dpl/wrt-dao-ro/global_config.h + include/dpl/wrt-dao-ro/global_dao_read_only.h + include/dpl/wrt-dao-ro/path_builder.h + include/dpl/wrt-dao-ro/plugin_dao_read_only.h + include/dpl/wrt-dao-ro/property_dao_read_only.h + include/dpl/wrt-dao-ro/widget_config.h + include/dpl/wrt-dao-ro/widget_dao_read_only.h + include/dpl/wrt-dao-ro/WrtDatabase.h + DESTINATION include/dpl-efl/dpl/wrt-dao-ro + ) + +INSTALL(FILES + include/dpl/wrt-dao-rw/feature_dao.h + include/dpl/wrt-dao-rw/global_dao.h + include/dpl/wrt-dao-rw/plugin_dao.h + include/dpl/wrt-dao-rw/property_dao.h + include/dpl/wrt-dao-rw/widget_dao.h + DESTINATION include/dpl-efl/dpl/wrt-dao-rw + ) diff --git a/modules/widget_dao/dao/WrtDatabase.cpp b/modules/widget_dao/dao/WrtDatabase.cpp new file mode 100644 index 0000000..f3fabe6 --- /dev/null +++ b/modules/widget_dao/dao/WrtDatabase.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +namespace WrtDB { + +const char* WrtDatabase::Address() +{ + using namespace WrtDB; + return GlobalConfig::GetWrtDatabaseFilePath(); +} + +DPL::DB::SqlConnection::Flag::Type WrtDatabase::Flags() +{ + return DPL::DB::SqlConnection::Flag::UseLucene; +} + +DPL::DB::ThreadDatabaseSupport WrtDatabase::m_interface( + WrtDatabase::Address(), + WrtDatabase::Flags()); + +void WrtDatabase::attachToThread() +{ + m_interface.AttachToThread(); +} + +void WrtDatabase::detachFromThread() +{ + m_interface.DetachFromThread(); +} + +DPL::DB::ThreadDatabaseSupport& WrtDatabase::interface() +{ + return m_interface; +} + +bool WrtDatabase::CheckTableExist(const char *name) +{ + return m_interface.CheckTableExist(name); +} + +} diff --git a/modules/widget_dao/dao/bind_to_dao.h b/modules/widget_dao/dao/bind_to_dao.h new file mode 100644 index 0000000..9b9c2d3 --- /dev/null +++ b/modules/widget_dao/dao/bind_to_dao.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file bind_to_dao.h + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 1.0 + * @brief + */ + +#ifndef WRT_SRC_CONFIGURATION_BIND_TO_DAO_H_ +#define WRT_SRC_CONFIGURATION_BIND_TO_DAO_H_ + +namespace WrtDB { + +/** + * @param ObjectType type of object used as delegate argument + * @param RetType Type returned from the external function + * @param ExtArg Type of argument required by external fun + * @param getterFun Object Type method which returns value of type ExtArg + * used as argument for external function + * */ +//STATIC FUNCTION +template < + typename ObjectType, + typename ValueType, + typename ExtArg, + ExtArg(ObjectType::*argGetter) () const, + ValueType(*externalGetter) (ExtArg) + > +struct BindToDAO_Static +{ + static ValueType Get(DPL::Model* obj) + { + ObjectType* instance = static_cast(obj); + + return externalGetter((instance->*argGetter)()); + } +}; + +template < + typename ObjectType, + typename ValueType, + typename ExtArg, + typename ExtObject, + ExtArg(ObjectType::*argGetter) () const, + ValueType(ExtObject::*externalGetter) () const + > +struct BindToDAO +{ + static ValueType Get(DPL::Model* obj) + { + ObjectType* instance = static_cast(obj); + ExtObject extObject((instance->*argGetter)()); + return (extObject.*externalGetter)(); + } +}; + +} // namespace WrtDB + +#endif diff --git a/modules/widget_dao/dao/common_dao_types.cpp b/modules/widget_dao/dao/common_dao_types.cpp new file mode 100644 index 0000000..94e1222 --- /dev/null +++ b/modules/widget_dao/dao/common_dao_types.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * @file common_dao_types.h + * @author Michal Ciepielski (m.ciepielski@samsung.com) + * @version 1.0 + * @brief This file contains the implementation of common data types for wrtdb + */ + +#include + +#include + +namespace WrtDB { +namespace Powder { + +Description::LevelEntry::LevelEntry(LevelEnum level) : + level(level) +{ +} + +bool Description::LevelEntry::isContextValid(LevelEnum level, + const DPL::OptionalString& aContext) const +{ + if (!aContext) { + return level < level; + } else { + Context::const_iterator iter = context.find(*aContext); + if (iter != context.end()) { + return level < level; + } else { + return true; + } + } +} + +bool Description::CategoryEntry::isCategoryValid(LevelEntry& aReason, + LevelEnum aLevel, + const DPL::OptionalString& aContext) const +{ + for (LevelsContainer::const_iterator iter = levels.begin(); + iter != levels.end(); ++iter) { + if (!iter->isContextValid(aLevel, aContext)) { + aReason = *iter; + return false; + } + } + return true; +} +} + +namespace ChildProtection +{ + +PowderRules::CategoryRule::CategoryRule(const DPL::String& aCategory, + Powder::Description::LevelEnum aLevel, + const DPL::OptionalString& aContext) : + category(aCategory), + level(aLevel), + context(aContext) +{ +} + +PowderRules::PowderResult::PowderResult(InvalidReason aReason, + const Powder::Description::LevelEntry& anInvalidDescription, + const CategoryRule& anInvalidRule) : + invalidDescription(anInvalidDescription), + invalidRule(anInvalidRule), + reason(aReason) +{ +} + +//! Function checks if rule is fulfilled by description +//! \param[in] rule checked rule +//! \param[in] description +//! \retval true rule is valid +//! \retval false rule is invalid +PowderRules::ResultPair PowderRules::isRuleValidForDescription( + const CategoryRule& aRule, + const Powder::Description& aDescription) const +{ + Powder::Description::CategoryEntries::const_iterator + iter = aDescription.categories.find(aRule.category); + if (iter != aDescription.categories.end()) { + Powder::Description::LevelEntry invalidDescription; + if (!iter->second.isCategoryValid(invalidDescription, aRule.level, + aRule.context)) { + LogWarning("Widget forbidden for children detected"); + return std::make_pair(false, + PowderResult(PowderResult::InvalidRule, + invalidDescription, + aRule)); + } else { + return std::make_pair(true, PowderResult()); + } + } else { + return std::make_pair(true, PowderResult()); + } +} + +//! Function checks if age limit is fulfilled by description +//! \param[in] description +//! \retval true age is valid +//! \retval false age is invalid +PowderRules::ResultPair PowderRules::isAgeValidForDescription( + const Powder::Description& aDescription) const +{ + if (!ageLimit) { + return std::make_pair(true, PowderResult()); + } else { + if (!!aDescription.ageRating) { + if (*aDescription.ageRating <= *ageLimit) { + return std::make_pair(true, PowderResult()); + } else { + return std::make_pair(false, + PowderResult(PowderResult::InvalidAge)); + } + } else { + if (!isAgeRatingRequired) { + return std::make_pair(true, PowderResult()); + } else { + return std::make_pair( + false, + PowderResult(PowderResult::AgeRatingNotSet)); + } + } + } +} + +//! Function check if Widget description is valid for ChildProtection +//! configuration +//! \param description widget description +//! \retval true widget is valid +//! \retval false widget is invalid +PowderRules::ResultPair PowderRules::isDescriptionValid( + const Powder::Description& aDescription) const +{ + ResultPair powderResult; + for (RulesContainer::const_iterator iter = rules.begin(); + iter != rules.end(); ++iter) { + powderResult = isRuleValidForDescription(*iter, aDescription); + if (!powderResult.first) { + return powderResult; + } + } + return isAgeValidForDescription(aDescription); +} + +} +} // namespace WrtDB diff --git a/modules/widget_dao/dao/config_parser_data.cpp b/modules/widget_dao/dao/config_parser_data.cpp new file mode 100644 index 0000000..d2550cc --- /dev/null +++ b/modules/widget_dao/dao/config_parser_data.cpp @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file config_parser_data.cpp + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#include +#include +#include +#include + +namespace WrtDB { + +bool IsSpace(const xmlChar* str) +{ + int charlen = xmlUTF8Size(str); + + switch (charlen) { + case 1: + switch (*str) { + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x20: + return true; + + default: + return false; + } + + case 2: + switch (*(str + 1)) { + case 0x85: + case 0xa0: + return true; + + default: + return false; + } + + case 3: + switch (*str) { + case 0xe1: + { + unsigned char c2 = *(str + 1); + unsigned char c3 = *(str + 2); + if ((c2 == 0x9a && c3 == 0x80) || (c2 == 0xa0 && c3 == 0x8e)) { + return true; + } else { + return false; + } + } + + case 0xe2: + switch (*(str + 1)) { + case 0x80: + switch (*(str + 2)) { + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8a: + case 0xa8: + case 0xa9: + case 0xaf: + return true; + } + case 0x81: + if (*(str + 2) == 0x9f) { + return true; + } else { + return false; + } + + default: + return false; + } + case 0xe3: + if (*(str + 1) == 0x80 && *(str + 2) == 0x80) { + return true; + } else { + return false; + } + + default: + return false; + } + + default: + return false; + } +} + +bool CopyChar(xmlChar* out, + xmlChar* in) +{ + int size = xmlUTF8Size(in); + switch (size) { + case 6: + out[5] = in[5]; + case 5: + out[4] = in[4]; + case 4: + out[3] = in[3]; + case 3: + out[2] = in[2]; + case 2: + out[1] = in[1]; + case 1: + out[0] = in[0]; + return true; + + default: + return false; + } +} + +//TODO temporary fix until commits the rewrite of this functionality. +void NormalizeString(DPL::String& str) +{ + DPL::Optional opt = str; + NormalizeString(opt); + str = *opt; +} + +void NormalizeString (DPL::Optional& txt) +{ + if (!!txt) { + std::string tmp = DPL::ToUTF8String(*txt); + const xmlChar* str = reinterpret_cast(tmp.c_str()); + if (!xmlCheckUTF8(str)) { + LogError("Not valid UTF8"); + return; + } + + int i = 0; + xmlChar* c; + while ((c = const_cast(xmlUTF8Strpos(str, i))) != NULL) { + if (!IsSpace(c)) { + break; + } + ++i; + } + + xmlChar* tmpnew = xmlUTF8Strndup(c, xmlUTF8Strlen(c) + 1); + bool first = false; + xmlChar* s = tmpnew; + while ((c = const_cast(xmlUTF8Strpos(str, i))) != NULL) { + if (IsSpace(c)) { + first = true; + ++i; + } else { + if (c[0] == 0x0) { + break; + } + if (first) { + xmlChar space[6] = { 0x20 }; + CopyChar(s, space); + s += xmlUTF8Size(s); + first = false; + } + CopyChar(s, c); + s += xmlUTF8Size(s); + ++i; + } + } + s[0] = 0x0; + txt = DPL::FromUTF8String(reinterpret_cast(tmpnew)); + xmlFree(tmpnew); + } +} + +bool ConfigParserData::Param::operator==(const Param& other) const +{ + return name == other.name && value == other.value; +} + +bool ConfigParserData::Param::operator!=(const Param& other) const +{ + return name != other.name || value != other.value; +} + +bool ConfigParserData::Param::operator >(const Param& other) const +{ + if (name == other.name) { + return value > other.value; + } else { + return name > other.name; + } +} + +bool ConfigParserData::Param::operator>=(const Param& other) const +{ + if (name >= other.name) { + return true; + } else { + return value >= other.value; + } +} + +bool ConfigParserData::Param::operator <(const Param& other) const +{ + if (name == other.name) { + return value < other.value; + } else { + return name < other.name; + } +} + +bool ConfigParserData::Param::operator<=(const Param& other) const +{ + if (name <= other.name) { + return true; + } else { + return value <= other.value; + } +} + +bool ConfigParserData::Feature::operator==(const Feature& other) const +{ + return name == other.name && paramsList == other.paramsList; +} + +bool ConfigParserData::Feature::operator!=(const Feature& other) const +{ + return name != other.name || paramsList != other.paramsList; +} + +bool ConfigParserData::Feature::operator >(const Feature& other) const +{ + if (name > other.name) { + return true; + } + if (name < other.name) { + return false; + } + return paramsList > other.paramsList; +} + +bool ConfigParserData::Feature::operator>=(const Feature& other) const +{ + if (name > other.name) { + return true; + } + if (name < other.name) { + return false; + } + return paramsList >= other.paramsList; +} + +bool ConfigParserData::Feature::operator <(const Feature& other) const +{ + if (name < other.name) { + return true; + } + if (name > other.name) { + return false; + } + return paramsList < other.paramsList; +} + +bool ConfigParserData::Feature::operator<=(const Feature& other) const +{ + if (name < other.name) { + return true; + } + if (name > other.name) { + return false; + } + return paramsList <= other.paramsList; +} + +bool ConfigParserData::Icon::operator==(const Icon& other) const +{ + return src == other.src; +} + +bool ConfigParserData::Icon::operator!=(const Icon& other) const +{ + return src != other.src; +} + +bool ConfigParserData::Icon::operator >(const Icon& other) const +{ + return src > other.src; +} + +bool ConfigParserData::Icon::operator>=(const Icon& other) const +{ + return src >= other.src; +} + +bool ConfigParserData::Icon::operator <(const Icon& other) const +{ + return src < other.src; +} + +bool ConfigParserData::Icon::operator<=(const Icon& other) const +{ + return src <= other.src; +} + +bool ConfigParserData::Preference::operator==(const Preference& other) const +{ + return name == other.name; +} + +bool ConfigParserData::Preference::operator!=(const Preference& other) const +{ + return name != other.name; +} + +bool ConfigParserData::Preference::operator >(const Preference& other) const +{ + return name > other.name; +} + +bool ConfigParserData::Preference::operator>=(const Preference& other) const +{ + return name >= other.name; +} + +bool ConfigParserData::Preference::operator <(const Preference& other) const +{ + return name < other.name; +} + +bool ConfigParserData::Preference::operator<=(const Preference& other) const +{ + return name <= other.name; +} + +bool ConfigParserData::AccessInfo::operator== (const AccessInfo& info) const +{ + return m_strIRI == info.m_strIRI && m_bSubDomainAccess == + info.m_bSubDomainAccess; +} + +bool ConfigParserData::AccessInfo::operator!= (const AccessInfo& info) const +{ + return m_strIRI != info.m_strIRI || m_bSubDomainAccess != + info.m_bSubDomainAccess; +} + +bool ConfigParserData::AccessInfo::operator <(const AccessInfo& info) const +{ + if (m_strIRI == info.m_strIRI) { + return m_bSubDomainAccess < info.m_bSubDomainAccess; + } else { + return m_strIRI < info.m_strIRI; + } +} + +bool ConfigParserData::Setting::operator==(const Setting& other) const +{ + return m_name == other.m_name && + m_value == other.m_value; +} + +bool ConfigParserData::Setting::operator!=(const Setting& other) const +{ + return m_name != other.m_name || + m_value != other.m_value; +} + +bool ConfigParserData::Setting::operator >(const Setting& other) const +{ + return m_name > other.m_name; +} + +bool ConfigParserData::Setting::operator>=(const Setting& other) const +{ + return m_name >= other.m_name; +} + +bool ConfigParserData::Setting::operator <(const Setting& other) const +{ + return m_name < other.m_name; +} + +bool ConfigParserData::Setting::operator<=(const Setting& other) const +{ + return m_name <= other.m_name; +} + +bool ConfigParserData::ServiceInfo::operator== (const ServiceInfo& info) const +{ + return m_src == info.m_src && + m_operation == info.m_operation && + m_scheme == info.m_scheme && + m_mime == info.m_mime; +} + +bool ConfigParserData::ServiceInfo::operator!= (const ServiceInfo& info) const +{ + return m_src != info.m_src && + m_operation != info.m_operation && + m_scheme != info.m_scheme && + m_mime != info.m_mime; +} +} // namespace WrtDB diff --git a/modules/widget_dao/dao/feature_dao.cpp b/modules/widget_dao/dao/feature_dao.cpp new file mode 100644 index 0000000..60c7151 --- /dev/null +++ b/modules/widget_dao/dao/feature_dao.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file contains the definition of feature dao class. + * + * @file widget_dao.cpp + * @author Jaroslaw Osmanski (j.osmanski@samsung.com) + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the definition of feature configuration. + */ + +#include +#include +#include +#include +#include + +namespace WrtDB { +namespace FeatureDAO { + +const int STRANGE_FEATURE_PROPERTIES_ID = 0; + +FeatureHandle RegisterFeature(const PluginMetafileData::Feature &feature, + const DbPluginHandle pluginHandle) +{ + Try + { + LogDebug("Registering Feature " << feature.m_name); + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + + if (FeatureDAOReadOnly::isFeatureInstalled(feature.m_name)) { + LogError(" >> Feature " << feature.m_name << + " is already registered."); + transaction.Commit(); + return -1; + } + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + //register feature + { + LogInfo(" |-- Registering feature " << feature.m_name); + + FeaturesList::Row row; + row.Set_FeatureName(DPL::FromUTF8String(feature.m_name)); + row.Set_PluginPropertiesId(pluginHandle); + + WRT_DB_INSERT(insert, FeaturesList, &WrtDatabase::interface()) + insert->Values(row); + insert->Execute(); + } + + FeatureHandle featureHandle = + FeatureDAOReadOnly(feature.m_name).GetFeatureHandle(); + + //register device capabilities + // Device Capabilities is unused in current version + FOREACH(itdev, feature.m_deviceCapabilities) + { + int deviceCapID; + + if (FeatureDAOReadOnly::isDeviceCapabilityInstalled(*itdev)) { + LogInfo(" | |--DeviceCap " << *itdev << + " already installed!"); + + WRT_DB_SELECT(select, DeviceCapabilities, &WrtDatabase::interface()) + + select->Where(Equals( + DPL::FromUTF8String(*itdev))); + + deviceCapID = + select->GetSingleValue(); + } else { + LogInfo(" | |--Register DeviceCap: " << *itdev); + + DeviceCapabilities::Row row; + row.Set_DeviceCapName(DPL::FromUTF8String(*itdev)); + + WRT_DB_INSERT(insert, DeviceCapabilities, &WrtDatabase::interface()) + insert->Values(row); + deviceCapID = insert->Execute(); + } + + FeatureDeviceCapProxy::Row row; + row.Set_FeatureUUID(featureHandle); + row.Set_DeviceCapID(deviceCapID); + + WRT_DB_INSERT(insert, FeatureDeviceCapProxy, &WrtDatabase::interface()) + insert->Values(row); + insert->Execute(); + } + + transaction.Commit(); + + return featureHandle; + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(FeatureDAOReadOnly::Exception::DatabaseError, + "Failure during Registering Feature"); + } +} + +FeatureHandle RegisterStrangeFeature(const std::string& featureName) +{ + Try + { + LogDebug("Registering Feature " << featureName); + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + + if (FeatureDAOReadOnly::isFeatureInstalled(featureName)) { + LogError(" >> Feature " << featureName << + " is already registered."); + transaction.Commit(); + return -1; + } + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + //register feature + LogInfo(" |-- Registering feature " << featureName); + + FeaturesList::Row row; + row.Set_FeatureName(DPL::FromUTF8String(featureName)); + + // PluginPropertiesId '0' is not used as PluginPropertiesId for normal features(calendar, contact....). + // PluginPropertiesId for normal features start from '1' + row.Set_PluginPropertiesId(STRANGE_FEATURE_PROPERTIES_ID); + + WRT_DB_INSERT(insert, FeaturesList, &WrtDatabase::interface()) + insert->Values(row); + insert->Execute(); + + FeatureHandle featureHandle = + FeatureDAOReadOnly(featureName).GetFeatureHandle(); + + transaction.Commit(); + + return featureHandle; + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(FeatureDAOReadOnly::Exception::DatabaseError, + "Failure during Registering Feature"); + } +} + +} // namespace FeatureDAO +} // namespace WrtDB diff --git a/modules/widget_dao/dao/feature_dao_read_only.cpp b/modules/widget_dao/dao/feature_dao_read_only.cpp new file mode 100644 index 0000000..adf11e9 --- /dev/null +++ b/modules/widget_dao/dao/feature_dao_read_only.cpp @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file feature_dao_read_only.cpp + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + * @brief This file contains the implementation of feature dao read only + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace WrtDB { + +FeatureDAOReadOnly::FeatureDAOReadOnly(FeatureHandle featureHandle) : + m_featureHandle(featureHandle) +{ + if (!isFeatureInstalled(m_featureHandle)) { + std::ostringstream exc; + exc << "Feature " << m_featureHandle << " not installed."; + LogError(exc.str()); + ThrowMsg(FeatureDAOReadOnly::Exception::FeatureNotExist, exc.str()); + } +} + +FeatureDAOReadOnly::FeatureDAOReadOnly(const std::string &featureName) +{ + LogDebug("FeatureDAOReadOnly ( " << featureName << " )"); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, FeaturesList, &WrtDatabase::interface()) + select->Where(Equals( + DPL::FromUTF8String(featureName))); + + m_featureHandle = select->GetSingleValue(); + LogDebug(" >> FeatureHandle retrieved: " << m_featureHandle); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(FeatureDAOReadOnly::Exception::DatabaseError, + "Failure during creating FeatureDAOReadOnly"); + } +} + +#define GET_PLUGIN_DATA(func) \ + Try { \ + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); \ + LogDebug(# func << ". FeatureHandle: " << m_featureHandle); \ + std::string ret = PluginDAOReadOnly(GetPluginHandle()).func(); \ + transaction.Commit(); \ + return ret; \ + } \ + Catch(DPL::DB::SqlConnection::Exception::Base) { \ + std::ostringstream failure("Failure during "); \ + failure << # func; \ + ReThrowMsg(FeatureDAOReadOnly::Exception::DatabaseError, \ + failure.str()); \ + } + +std::string FeatureDAOReadOnly::GetInstallURI() const +{ + GET_PLUGIN_DATA(getInstallURI) +} + +std::string FeatureDAOReadOnly::GetKeyCn() const +{ + GET_PLUGIN_DATA(getKeyCn) +} + +std::string FeatureDAOReadOnly::GetRootKey() const +{ + GET_PLUGIN_DATA(getRootKey) +} + +std::string FeatureDAOReadOnly::GetRootKeyFingerprint() const +{ + GET_PLUGIN_DATA(getRootKeyFingerprint) +} + +std::string FeatureDAOReadOnly::GetLibraryPath() const +{ + GET_PLUGIN_DATA(getLibraryPath) +} + +std::string FeatureDAOReadOnly::GetLibraryName() const +{ + GET_PLUGIN_DATA(getLibraryName) +} + +#undef GET_PLUGIN_DATA + +FeatureHandle FeatureDAOReadOnly::GetFeatureHandle() const +{ + return m_featureHandle; +} + +std::string FeatureDAOReadOnly::GetName() const +{ + LogDebug("Getting Feature Name. Handle: " << m_featureHandle); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, FeaturesList, &WrtDatabase::interface()) + select->Where(Equals(m_featureHandle)); + + std::string ret = DPL::ToUTF8String( + select->GetSingleValue< FeaturesList::FeatureName>()); + + LogDebug(" >> Feature name: " << ret); + return ret; + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(FeatureDAOReadOnly::Exception::DatabaseError, + "Failure during getting GetName"); + } +} + +DbPluginHandle FeatureDAOReadOnly::GetPluginHandle() const +{ + LogDebug("Getting Plugin handle. FHandle: " << m_featureHandle); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, FeaturesList, &WrtDatabase::interface()) + select->Where(Equals(m_featureHandle)); + + DbPluginHandle pluginHandle = + select->GetSingleValue< FeaturesList::PluginPropertiesId>(); + + LogDebug(" >> Plugin Handle: " << pluginHandle); + return pluginHandle; + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(FeatureDAOReadOnly::Exception::DatabaseError, + "Failure during getting Plugin Handle"); + } +} + +FeatureHandleList FeatureDAOReadOnly::GetHandleList() +{ + LogDebug("Getting FeatureHandle list."); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, FeaturesList, &WrtDatabase::interface()) + + FeatureHandleList ret = + select->GetValueList(); + + std::ostringstream handles; + FOREACH(it, ret) + handles << *it << " "; + LogDebug(" >> FeatureHandle list retrieved: (" << handles << ")"); + + return ret; + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(FeatureDAOReadOnly::Exception::DatabaseError, + "Failure during getting FeatureHandle list"); + } +} + +bool FeatureDAOReadOnly::isFeatureInstalled(const std::string &featureName) +{ + LogDebug("Check if Feature is installed. Name: " << featureName); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, FeaturesList, &WrtDatabase::interface()) + select->Where(Equals( + DPL::FromUTF8String(featureName))); + + FeaturesList::Select::RowList rows = select->GetRowList(); + + bool flag = !rows.empty(); + LogDebug(" >> Feature " << featureName << + (flag ? " found." : " not found.")); + + return flag; + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(FeatureDAOReadOnly::Exception::DatabaseError, + "Failure during checking if Feature is installed"); + } +} + +bool FeatureDAOReadOnly::isFeatureInstalled(FeatureHandle handle) +{ + LogDebug("Check if Feature is installed. Handle: " << handle); + Try + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, FeaturesList, &WrtDatabase::interface()) + select->Where(Equals(handle)); + + FeaturesList::Select::RowList rows = select->GetRowList(); + + bool flag = !rows.empty(); + LogDebug(" >> Feature " << handle << + (flag ? " found." : " not found.")); + + return flag; + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(FeatureDAOReadOnly::Exception::DatabaseError, + "Failure during checking if Feature is installed"); + } +} + +bool FeatureDAOReadOnly::isDeviceCapabilityInstalled( + const std::string &deviceCapName) +{ + LogDebug("Check if DeviceCap is installed. Name: " << deviceCapName); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, DeviceCapabilities, &WrtDatabase::interface()) + select->Where(Equals( + DPL::FromUTF8String(deviceCapName))); + + DeviceCapabilities::Select::RowList rows = select->GetRowList(); + + bool flag = !rows.empty(); + LogDebug(" >> Device Cap " << deviceCapName << + (flag ? "found." : "not found.")); + + return flag; + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(FeatureDAOReadOnly::Exception::DatabaseError, + "Failure during checking if DeviceCap is installed"); + } +} + +FeatureDAOReadOnly::DeviceCapabilitiesList +FeatureDAOReadOnly::GetDeviceCapabilities() const +{ + Try { + LogDebug("Get DeviceCap. FeatureHandle: " << m_featureHandle); + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(selectDevCP, FeatureDeviceCapProxy, &WrtDatabase::interface()) + selectDevCP->Where(Equals( + m_featureHandle)); + + DeviceCapabilitiesList devCap; + + std::list deviceIDs = + selectDevCP->GetValueList(); + FOREACH(devCId, deviceIDs) + { + WRT_DB_SELECT(selectDevC, DeviceCapabilities, &WrtDatabase::interface()) + selectDevC->Where(Equals(*devCId)); + + DPL::String devNames = + selectDevC->GetSingleValue(); + + devCap.insert(DPL::ToUTF8String(devNames)); + } + + transaction.Commit(); + return devCap; + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(FeatureDAOReadOnly::Exception::DatabaseError, + "Failure during getting DeviceCapabilities names"); + } +} + +FeatureHandleListPtr FeatureDAOReadOnly::GetFeatureHandleListForPlugin( + DbPluginHandle pluginHandle) +{ + LogDebug("Getting FeatureHandle list for pluginHandle: " << pluginHandle); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + WRT_DB_SELECT(select, FeaturesList, &WrtDatabase::interface()) + select->Where(Equals(pluginHandle)); + + FeatureHandleListPtr handles(new FeatureHandleList); + FeatureHandleList ret = + select->GetValueList(); + + FOREACH(it, ret) + { + LogDebug("feature handle: " << *it); + handles->push_back(*it); + } + + return handles; + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg( + FeatureDAOReadOnly::Exception::DatabaseError, + "Failure during getting FeatureHandle Set for plugin handle"); + } +} + +} // namespace WrtDB diff --git a/modules/widget_dao/dao/global_config.cpp b/modules/widget_dao/dao/global_config.cpp new file mode 100644 index 0000000..bf68cc4 --- /dev/null +++ b/modules/widget_dao/dao/global_config.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include +#include + +namespace WrtDB { +namespace GlobalConfig { +bool IsOCSPEnabled() +{ + static const char *val = getenv("WRT_OCSP_ENABLE"); + static bool enableOCSP = val && (strcmp(val, "1") == 0); + return enableOCSP; +} + +bool IsCRLEnabled() +{ + static const char *val = getenv("WRT_CRL_ENABLE"); + static bool enableOCSP = val && (strcmp(val, "1") == 0); + return enableOCSP; +} + +} // namespace GlobalConfig +} // namespace WrtDB + diff --git a/modules/widget_dao/dao/global_dao.cpp b/modules/widget_dao/dao/global_dao.cpp new file mode 100644 index 0000000..6f1f406 --- /dev/null +++ b/modules/widget_dao/dao/global_dao.cpp @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file global_dao.cpp + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the definition of global dao class. + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace WrtDB { + +void GlobalDAO::AddAdultBlackListElement(const DPL::String &url) +{ + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + ScopedTransaction transaction(&WrtDatabase::interface()); + if (IsElementOnAdultBlackList(url)) { + transaction.Commit(); + return; + } + + WRT_DB_INSERT(insert, ChildProtectionBlacklist, &WrtDatabase::interface()) + ChildProtectionBlacklist::Row row; + row.Set_url(url); + + insert->Values(row); + insert->Execute(); + transaction.Commit(); + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to add element to AdultBlackList"); + } +} + +void GlobalDAO::RemoveAdultBlackListElement(const DPL::String &url) +{ + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_DELETE(deleteFrom, ChildProtectionBlacklist, &WrtDatabase::interface()) + + deleteFrom->Where(Equals(url)); + deleteFrom->Execute(); + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to removed element from AdultBlackList"); + } +} + +void GlobalDAO::UpdateAdultBlackList(const DPL::String &oldUrl, + const DPL::String &newUrl) +{ + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + ScopedTransaction transaction(&WrtDatabase::interface()); + if (IsElementOnAdultBlackList(newUrl)) { + transaction.Commit(); + return; + } + WRT_DB_UPDATE(update, ChildProtectionBlacklist, &WrtDatabase::interface()) + ChildProtectionBlacklist::Row row; + row.Set_url(newUrl); + + update->Where(Equals(oldUrl)); + update->Values(row); + update->Execute(); + transaction.Commit(); + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to update an element in AdultBlackList"); + } +} + +void GlobalDAO::SetDeveloperMode(bool mode) +{ + LogDebug("updating Developer mode to:" << mode); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + GlobalProperties::Row row; + row.Set_developer_mode(mode); + + WRT_DB_UPDATE(update, GlobalProperties, &WrtDatabase::interface()) + update->Values(row); + update->Execute(); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to update Developer Mode"); + } +} + +void GlobalDAO::AddDefferedWidgetPackageInstallation(const DPL::String &path) +{ + LogDebug("Adding widget package as defered. Path: " << path); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + DefferedWidgetPackageInstallation::Row row; + row.Set_path(path); + + WRT_DB_INSERT(insert, DefferedWidgetPackageInstallation, &WrtDatabase::interface()) + insert->Values(row); + insert->Execute(); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to add defered widget package"); + } +} + +void GlobalDAO::RemoveDefferedWidgetPackageInstallation(const DPL::String &path) +{ + LogDebug("Remove widget package from differed list. Path: " << path); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_DELETE(del, DefferedWidgetPackageInstallation, &WrtDatabase::interface()) + del->Where(Equals(path)); + del->Execute(); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to remove defered widget package"); + } +} + +void GlobalDAO::SetParentalMode(bool parental_status) +{ + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + GlobalProperties::Row row; + row.Set_parental_mode(parental_status); + WRT_DB_UPDATE(update, GlobalProperties, &WrtDatabase::interface()) + + update->Values(row); + update->Execute(); + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to update Parental Mode"); + } +} + +void GlobalDAO::SetParentalAllowedAge(const DPL::OptionalInt& age) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + Try { + typedef wrt::GlobalProperties::Row ParentalAllowedAgeRow; + + ParentalAllowedAgeRow row; + row.Set_parental_allowed_age(age); + WRT_DB_UPDATE(update, GlobalProperties, &WrtDatabase::interface()) + update->Values(row); + update->Execute(); + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to update Parental Mode"); + } +} + +void GlobalDAO::SetSecureByDefault(bool secure) +{ + //If secure == true -> widget does not need to be signed + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + GlobalProperties::Row row; + row.Set_secure_by_default(secure); + WRT_DB_UPDATE(update, GlobalProperties, &WrtDatabase::interface()) + update->Values(row); + update->Execute(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to update secureByDefault"); + } +} + +void GlobalDAO::setComplianceMode(bool mode) +{ + LogDebug("Updating compliance mode to:" << mode); + Try{ + using namespace DPL::DB::ORM; using namespace DPL::DB::ORM::wrt; + GlobalProperties::Row row; + row.Set_compliance_mode(mode); + + WRT_DB_UPDATE(update, GlobalProperties, &WrtDatabase::interface()) + update->Values(row); + update->Execute(); + } + Catch (DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to update compliance mode"); + } +} + +void GlobalDAO::setComplianceFakeImei(const std::string &imei) +{ + LogDebug("Setting compliance fake IMEI: " << imei); + Try{ + using namespace DPL::DB::ORM; using namespace DPL::DB::ORM::wrt; + GlobalProperties::Row row; + row.Set_compliance_fake_imei(DPL::FromASCIIString(imei)); + + WRT_DB_UPDATE(update, GlobalProperties, &WrtDatabase::interface()) + update->Values(row); + update->Execute(); + } + Catch (DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to update compliance fake IMEI"); + } +} + +void GlobalDAO::setComplianceFakeMeid(const std::string &meid) +{ + LogDebug("Setting compliance fake MEID: " << meid); + Try{ + using namespace DPL::DB::ORM; using namespace DPL::DB::ORM::wrt; + GlobalProperties::Row row; + row.Set_compliance_fake_meid(DPL::FromASCIIString(meid)); + + WRT_DB_UPDATE(update, GlobalProperties, &WrtDatabase::interface()) + update->Values(row); + update->Execute(); + } + Catch (DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to update compliance fake MEID"); + } +} + +void GlobalDAO::AddCategoryRule(const ChildProtection::PowderRules:: + CategoryRule& powder) +{ + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + PowderRules::Row row; + row.Set_category(powder.category); + row.Set_level(powder.level); + row.Set_context(powder.context); + + wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + if (IsPowderRulePresent(powder)) { + transaction.Commit(); + return; + } + WRT_DB_INSERT(insert, PowderRules, &WrtDatabase::interface()) + + insert->Values(row); + insert->Execute(); + transaction.Commit(); + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to add POWDER rules"); + } +} + +void GlobalDAO::RemoveCategoryRule(const ChildProtection::PowderRules:: + CategoryRule& powder) +{ + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + if (!powder.context.IsNull()) { + WRT_DB_DELETE(delWithContext, PowderRules, &WrtDatabase::interface()) + + delWithContext->Where( + And(Equals(powder.category), + And(Equals(powder.level), + Equals(powder.context)))); + delWithContext->Execute(); + } else { + WRT_DB_DELETE(delWithoutContext, PowderRules, &WrtDatabase::interface()) + delWithoutContext->Where( + And(Equals(powder.category), + And(Equals(powder.level), + Is(powder.context)))); + delWithoutContext->Execute(); + } + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to remove POWDER rules"); + } +} + +void GlobalDAO::UpdateCategoryRule( + const ChildProtection::PowderRules::CategoryRule& oldRule, + const ChildProtection::PowderRules::CategoryRule& newRule) +{ + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + PowderRules::Row row; + row.Set_category(newRule.category); + row.Set_level(newRule.level); + row.Set_context(newRule.context); + + wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + + if (IsPowderRulePresent(newRule)) { + transaction.Commit(); + return; + } + + if (!oldRule.context.IsNull()) { + WRT_DB_UPDATE(updateWithContext, PowderRules, &WrtDatabase::interface()) + updateWithContext->Where( + And(Equals(oldRule.category), + And(Equals(oldRule.level), + Equals(oldRule.context)))); + updateWithContext->Values(row); + updateWithContext->Execute(); + } else { + WRT_DB_UPDATE(updateWithoutContext, PowderRules, &WrtDatabase::interface()) + updateWithoutContext->Where( + And(Equals(oldRule.category), + And(Equals(oldRule.level), + Is(oldRule.context)))); + updateWithoutContext->Values(row); + updateWithoutContext->Execute(); + } + transaction.Commit(); + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to update POWDER rules"); + } +} + +void GlobalDAO::SetHomeNetworkDataUsage(GlobalDAO::NetworkAccessMode newMode) +{ + LogDebug("updating home network data usage to:" << newMode); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + GlobalProperties::Row row; + row.Set_home_network_data_usage(static_cast(newMode)); + + WRT_DB_UPDATE(update, GlobalProperties, &WrtDatabase::interface()) + update->Values(row); + update->Execute(); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to update home network data usage"); + } +} + +void GlobalDAO::SetRoamingDataUsage(GlobalDAO::NetworkAccessMode newMode) +{ + LogDebug("updating roaming network data usage to:" << newMode); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + GlobalProperties::Row row; + row.Set_roaming_data_usage(static_cast(newMode)); + + WRT_DB_UPDATE(update, GlobalProperties, &WrtDatabase::interface()) + update->Values(row); + update->Execute(); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to update roaming network data usage"); + } +} + +void GlobalDAO::SetAutoSaveIdPasswd(const DPL::String &url, + const AutoSaveData &saveData) +{ + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + ScopedTransaction transaction(&WrtDatabase::interface()); + AutoSaveIdPasswd::Row row; + + row.Set_address(url); + row.Set_userId(saveData.userId); + row.Set_passwd(saveData.passwd); + + DPL::Optional savedData = + GetAutoSaveIdPasswd(url); + + if (!savedData.IsNull()) { + WRT_DB_UPDATE(update, AutoSaveIdPasswd, &WrtDatabase::interface()) + + update->Where(Equals(url)); + update->Values(row); + update->Execute(); + } else { + WRT_DB_INSERT(insert, AutoSaveIdPasswd, &WrtDatabase::interface()); + insert->Values(row); + insert->Execute(); + } + + transaction.Commit(); + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Fail to register id, passwd for autosave"); + } +} + +void GlobalDAO::AddWhiteURI(const std::string &uri, bool subDomain) +{ + LogDebug("Add White URI : " << uri); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WidgetWhiteURIList::Row row; + row.Set_uri(DPL::FromASCIIString(uri)); + row.Set_subdomain_access(static_cast(subDomain)); + wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + + WRT_DB_INSERT(insert, WidgetWhiteURIList, &WrtDatabase::interface()) + + insert->Values(row); + insert->Execute(); + transaction.Commit(); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to add white URI"); + } +} + +void GlobalDAO::RemoveWhiteURI(const std::string &uri) +{ + LogDebug("Remove White URI : " << uri); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + WRT_DB_DELETE(deleteFrom, WidgetWhiteURIList, &WrtDatabase::interface()) + deleteFrom->Where(Equals(DPL::FromASCIIString(uri))); + deleteFrom->Execute(); + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAO::Exception::DatabaseError, + "Failed to removed white URI from AdultBlackList"); + } +} + +} // namespace WrtDB diff --git a/modules/widget_dao/dao/global_dao_read_only.cpp b/modules/widget_dao/dao/global_dao_read_only.cpp new file mode 100644 index 0000000..cb26a1e --- /dev/null +++ b/modules/widget_dao/dao/global_dao_read_only.cpp @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file global_dao_read_only.cpp + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the definition of global dao class. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace WrtDB { + +ChildProtection::BlackList GlobalDAOReadOnly::GetAdultBlackList() +{ + LogDebug("Getting AdultBlackList"); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, ChildProtectionBlacklist, &WrtDatabase::interface()) + std::list list = + select->GetValueList(); + ChildProtection::BlackList blacklist(list.begin(), list.end()); + return blacklist; + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failed to get AdultBalckList"); + } +} + +bool GlobalDAOReadOnly::IsElementOnAdultBlackList(const DPL::String &url) +{ + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, ChildProtectionBlacklist, &WrtDatabase::interface()) + select->Where(Equals(url)); + + return !select->GetRowList().empty(); + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failure during checking if url is on pwoder black list"); + } +} + +bool GlobalDAOReadOnly::GetDeveloperMode() +{ + LogDebug("Getting Developer mode"); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, GlobalProperties, &WrtDatabase::interface()) + return select->GetSingleValue(); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failed to get developer mode"); + } +} + +WidgetPackageList GlobalDAOReadOnly::GetDefferedWidgetPackageInstallationList() +{ + LogDebug("Getting widget packages list defered for installation"); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, DefferedWidgetPackageInstallation, &WrtDatabase::interface()) + return select->GetValueList(); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failed to get defered widget packages list"); + } +} + +bool GlobalDAOReadOnly::GetParentalMode() +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, GlobalProperties, &WrtDatabase::interface()) + return select->GetSingleValue(); +} + +DPL::OptionalInt GlobalDAOReadOnly::GetParentalAllowedAge() +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, GlobalProperties, &WrtDatabase::interface()) + return select->GetSingleValue(); +} + +bool GlobalDAOReadOnly::GetSecureByDefault() +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, GlobalProperties, &WrtDatabase::interface()) + return select->GetSingleValue(); +} + +bool GlobalDAOReadOnly::getComplianceMode() +{ + LogDebug("Getting compliance mode"); + Try{ + using namespace DPL::DB::ORM; using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, GlobalProperties, &WrtDatabase::interface()) + return select->GetSingleValue(); + } + Catch (DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failed to get compliance mode"); + } +} + +std::string GlobalDAOReadOnly::getComplianceFakeImei() +{ + LogDebug("Getting compliance fake IMEI"); + Try{ + using namespace DPL::DB::ORM; using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, GlobalProperties, &WrtDatabase::interface()) + DPL::Optional result = + select->GetSingleValue(); + if (!result) { + return std::string(); + } + return DPL::ToUTF8String(*result); + } + Catch (DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failed to get compliance fake IMEI"); + } +} + +std::string GlobalDAOReadOnly::getComplianceFakeMeid() +{ + LogDebug("Getting compliance fake MEID"); + Try{ + using namespace DPL::DB::ORM; using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, GlobalProperties, &WrtDatabase::interface()) + DPL::Optional result = + select->GetSingleValue(); + if (!result) { + return std::string(); + } + return DPL::ToUTF8String(*result); + } + Catch (DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failed to get compliance fake MEID"); + } +} + +bool GlobalDAOReadOnly::IsValidSubTag(const DPL::String& tag, int type) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, iana_records, &WrtDatabase::interface()) + select->Where(Equals(tag)); + auto row = select->GetRowList(); + if (row.size() == 0 || row.front().Get_TYPE() != type) { + return false; + } else { + return true; + } +} + + +bool GlobalDAOReadOnly::IsPowderRulePresent( + const ChildProtection::PowderRules::CategoryRule& powder) +{ + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + if (!powder.context.IsNull()) { + WRT_DB_SELECT(selWithContext, PowderRules, &WrtDatabase::interface()) + + selWithContext->Where( + And(Equals(powder.category), + And(Equals(powder.level), + Equals(powder.context)))); + return !selWithContext->GetRowList().empty(); + } else { + WRT_DB_SELECT(selWithoutContext, PowderRules, &WrtDatabase::interface()) + selWithoutContext->Where( + And(Equals(powder.category), + And(Equals(powder.level), + Is(powder.context)))); + return !selWithoutContext->GetRowList().empty(); + } + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failure during checking if rule is present"); + } +} + +ChildProtection::PowderRules GlobalDAOReadOnly::GetPowderRules() +{ + ChildProtection::PowderRules powder; + powder.ageLimit = GetParentalAllowedAge(); + + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + typedef std::list PowderRulesList; + WRT_DB_SELECT(select, PowderRules, &WrtDatabase::interface()) + + PowderRulesList list = select->GetRowList(); + FOREACH(it, list) { + using namespace Powder; + powder.rules.push_back( + ChildProtection::PowderRules::CategoryRule( + it->Get_category(), + static_cast(it->Get_level()), + it->Get_context())); + } + return powder; + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failed to get POWDER rules"); + } +} + +GlobalDAOReadOnly::NetworkAccessMode + GlobalDAOReadOnly::GetHomeNetworkDataUsage() +{ + LogDebug("Getting home network data usage"); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, GlobalProperties, &WrtDatabase::interface()) + return static_cast( + select->GetSingleValue< + GlobalProperties::home_network_data_usage>()); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failed to get home network data usage"); + } +} + +GlobalDAOReadOnly::NetworkAccessMode GlobalDAOReadOnly::GetRoamingDataUsage() +{ + LogDebug("Getting roaming network data usage"); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, GlobalProperties, &WrtDatabase::interface()) + return static_cast( + select->GetSingleValue()); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failed to get roaming network data usage"); + } +} + +//user agent strings are stored in db... +//and it is configurable for test in development. +DPL::String GlobalDAOReadOnly::GetUserAgentValue(const DPL::String &key) +{ + LogDebug("Get User Agent Value : " << key); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, UserAgents, &WrtDatabase::interface()) + select->Where(Equals(key)); + DPL::Optional agent = + select->GetSingleValue(); + if (agent.IsNull()) { + return DPL::FromUTF8String(""); + } else { + return *agent; + } + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failed to get user agent string"); + } +} + +DeviceCapabilitySet GlobalDAOReadOnly::GetDeviceCapability( + const DPL::String &apifeature) +{ + // This could be done with one simply sql query but support for join is + // needed in orm. + Try{ + using namespace DPL::DB::ORM; using namespace DPL::DB::ORM::wrt; + + int featureUUID; + FeatureDeviceCapProxy::Select::RowList rows; + DeviceCapabilitySet result; + + { + WRT_DB_SELECT(select, FeaturesList, &WrtDatabase::interface()) + select->Where(Equals(apifeature)); + featureUUID = select->GetSingleValue(); + } + + { + WRT_DB_SELECT(select, FeatureDeviceCapProxy, &WrtDatabase::interface()) + select->Where(Equals( + featureUUID)); + rows = select->GetRowList(); + } + + FOREACH(it, rows){ + WRT_DB_SELECT(select, DeviceCapabilities, &WrtDatabase::interface()) + select->Where(Equals( + it->Get_DeviceCapID())); + result.insert(select-> + GetSingleValue()); + } + + return result; + }Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failed to update roaming network data usage"); + } +} + +DPL::Optional + GlobalDAOReadOnly::GetAutoSaveIdPasswd(const DPL::String &url) +{ + Try{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, AutoSaveIdPasswd, &WrtDatabase::interface()); + select->Where(Equals(url)); + AutoSaveIdPasswd::Select::RowList rows = select->GetRowList(); + + if (rows.empty()) { + return DPL::Optional::Null; + } + + GlobalDAOReadOnly::AutoSaveData saveData; + saveData.userId = rows.front().Get_userId(); + saveData.passwd = rows.front().Get_passwd(); + return saveData; + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failed to get autosave data string"); + } +} + +WidgetAccessInfoList GlobalDAOReadOnly::GetWhiteURIList() +{ + LogDebug("Getting WhiteURIList."); + Try{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetWhiteURIList, &WrtDatabase::interface()) + + WidgetAccessInfoList resultList; + typedef std::list RowList; + RowList list = select->GetRowList(); + + for (RowList::iterator i = list.begin(); i != list.end(); ++i) { + WidgetAccessInfo whiteURI; + whiteURI.strIRI = i->Get_uri(); + whiteURI.bSubDomains = i->Get_subdomain_access(); + resultList.push_back(whiteURI); + LogInfo("[uri] : " << whiteURI.strIRI << + ", [subdomain] : " << whiteURI.bSubDomains); + } + return resultList; + } Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(GlobalDAOReadOnly::Exception::DatabaseError, + "Failed to get whiteURI list"); + } +} + +} // namespace WrtDB diff --git a/modules/widget_dao/dao/path_builder.cpp b/modules/widget_dao/dao/path_builder.cpp new file mode 100644 index 0000000..4d91bbf --- /dev/null +++ b/modules/widget_dao/dao/path_builder.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file PathBuilder.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief Implementation file for PathBuilde class. + */ +#include +#include + +namespace WrtDB { +namespace { +const char PATH_SEPARATOR = '/'; +} + +class PathBuilderImpl : DPL::Noncopyable +{ + public: + PathBuilderImpl() + { + } + + explicit PathBuilderImpl(const std::string& path) : + m_stream(path, std::ios_base::app) + { + } + + void Append(const std::string& path) + { + // TODO Check additionally if last char is not separator. + if (m_stream.tellp() > 0) { + m_stream << PATH_SEPARATOR; + } + m_stream << path; + } + + void Concat(const std::string& arg) + { + m_stream << arg; + } + + void Concat(int arg) + { + m_stream << arg; + } + + void Reset() + { + m_stream.clear(); + m_stream.str(""); + } + + bool Empty() + { + return (m_stream.tellp() == 0); + } + + std::string GetFullPath() const + { + return m_stream.str(); + } + + private: + std::ostringstream m_stream; +}; + +PathBuilder::PathBuilder() : m_impl(new PathBuilderImpl()) +{ +} + +PathBuilder::PathBuilder(const std::string& path) : + m_impl(new PathBuilderImpl(path)) +{ +} + +PathBuilder::~PathBuilder() +{ + delete m_impl; +} + +PathBuilder& PathBuilder::Append(const std::string& path) +{ + m_impl->Append(path); + return *this; +} + +PathBuilder& PathBuilder::Concat(const std::string& arg) +{ + m_impl->Concat(arg); + return *this; +} + +PathBuilder& PathBuilder::Concat(int arg) +{ + m_impl->Concat(arg); + return *this; +} + +PathBuilder& PathBuilder::Reset() +{ + m_impl->Reset(); + return *this; +} + +bool PathBuilder::Empty() const +{ + return m_impl->Empty(); +} + +std::string PathBuilder::GetFullPath() const +{ + return m_impl->GetFullPath(); +} + +} // namespace WrtDB diff --git a/modules/widget_dao/dao/plugin_dao.cpp b/modules/widget_dao/dao/plugin_dao.cpp new file mode 100644 index 0000000..7941efb --- /dev/null +++ b/modules/widget_dao/dao/plugin_dao.cpp @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_dao.cpp + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @author Grzegorz Krawczyk (g.krawczyk@samsung.com) + * @version 1.0 + * @brief This file contains the definition of plugin dao class. + */ + +#include +#include +#include +#include +#include +#include + +namespace WrtDB { + +PluginDAO::PluginDAO(DbPluginHandle pluginHandle) : + PluginDAOReadOnly(pluginHandle) +{ +} + +PluginDAO::PluginDAO(const std::string &libraryName) : + PluginDAOReadOnly(libraryName) +{ +} + +DbPluginHandle PluginDAO::registerPlugin(const PluginMetafileData& metafile, + const std::string& pluginPath) +{ + LogDebug("Registering plugin. Path: " << pluginPath); + + Try { + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + DbPluginHandle handle; + + if (isPluginInstalled(metafile.m_libraryName)) { + handle = PluginDAO(metafile.m_libraryName).getPluginHandle(); + LogInfo(" >> Library " << metafile.m_libraryName << + " is already registered. Handle: " << handle); + } else { + LogDebug("Register Plugin: " << metafile.m_libraryName); + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + typedef PluginProperties::Row PluginPropertiesRow; + + PluginPropertiesRow row; + row.Set_PluginLibraryName( + DPL::FromUTF8String(metafile.m_libraryName)); + row.Set_InstallationState(INSTALLATION_IN_PROGRESS); + row.Set_PluginLibraryPath( + DPL::FromUTF8String(pluginPath)); + row.Set_InstallURI( + DPL::FromUTF8String(metafile.m_featuresInstallURI)); + row.Set_KeyCN( + DPL::FromUTF8String(metafile.m_featuresKeyCN)); + row.Set_RootKeyCN( + DPL::FromUTF8String(metafile.m_featuresRootCN)); + row.Set_RootKeyFingerprint( + DPL::FromUTF8String(metafile.m_featuresRootFingerprint)); + + WRT_DB_INSERT(insert, PluginProperties, &WrtDatabase::interface()) + insert->Values(row); + handle = insert->Execute(); + LogDebug(" >> Plugin Registered. Handle: " << handle); + } + transaction.Commit(); + return handle; + } + Catch(DPL::DB::SqlConnection::Exception::Base) + { + ReThrowMsg(PluginDAO::Exception::DatabaseError, + "Failed in RegisterPlugin"); + } +} + +void PluginDAO::registerPluginImplementedObject(const std::string& objectName, + DbPluginHandle pluginHandle) +{ + LogDebug("Registering plugin object: " << objectName); + + Try { + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + + LogDebug("Register Object: " << objectName); + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + typedef PluginImplementedObjects::Row PluginObjectsRow; + + PluginObjectsRow row; + row.Set_PluginObject(DPL::FromUTF8String(objectName)); + row.Set_PluginPropertiesId(pluginHandle); + + WRT_DB_INSERT(insert, PluginImplementedObjects, &WrtDatabase::interface()) + insert->Values(row); + insert->Execute(); + transaction.Commit(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) + { + ReThrowMsg(PluginDAO::Exception::DatabaseError, + "Failed in RegisterPluginObject"); + } +} + +void PluginDAO::registerPluginRequiredObject(const std::string& objectName, + DbPluginHandle pluginHandle) +{ + LogDebug("Registering plugin object: " << objectName); + + Try { + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + + LogDebug("Register Object: " << objectName); + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + typedef PluginRequiredObjects::Row PluginObjectsRow; + + PluginObjectsRow row; + row.Set_PluginPropertiesId(pluginHandle); + row.Set_PluginObject(DPL::FromUTF8String(objectName)); + + WRT_DB_INSERT(insert, PluginRequiredObjects, &WrtDatabase::interface()) + insert->Values(row); + insert->Execute(); + transaction.Commit(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) + { + ReThrowMsg(PluginDAO::Exception::DatabaseError, + "Failed in RegisterPluginObject"); + } +} + +void PluginDAO::registerPluginLibrariesDependencies( + DbPluginHandle pluginHandle, + const PluginHandleSetPtr& dependencies) +{ + LogDebug("Registering plugin library dependencies: " << pluginHandle); + + Try { + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + typedef PluginDependencies::Row PluginDependeciesRow; + PluginDependeciesRow row; + + FOREACH(it, *dependencies) + { + row.Set_PluginPropertiesId(pluginHandle); + row.Set_RequiredPluginPropertiesId(*it); + + WRT_DB_INSERT(insert, PluginDependencies, &WrtDatabase::interface()) + insert->Values(row); + insert->Execute(); + transaction.Commit(); + } + } + Catch(DPL::DB::SqlConnection::Exception::Base) + { + ReThrowMsg(PluginDAO::Exception::DatabaseError, + "Failed in RegisterPluginObject"); + } +} + +void PluginDAO::setPluginInstallationStatus(DbPluginHandle pluginHandle, + PluginInstallationState state) +{ + Try { + LogDebug( + "Set installation state: " << state << " handle " << pluginHandle); + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + ScopedTransaction transaction(&WrtDatabase::interface()); + + typedef wrt::PluginProperties::Row PluginPropertiesRow; + + PluginPropertiesRow row; + row.Set_InstallationState(state); + + WRT_DB_UPDATE(update, PluginProperties, &WrtDatabase::interface()) + update->Where( + Equals(pluginHandle)); + + update->Values(row); + update->Execute(); + transaction.Commit(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) + { + ReThrowMsg(PluginDAO::Exception::DatabaseError, + "Failed in RegisterLibraryDependencies"); + } +} + +} // namespace WrtDB diff --git a/modules/widget_dao/dao/plugin_dao_read_only.cpp b/modules/widget_dao/dao/plugin_dao_read_only.cpp new file mode 100644 index 0000000..edb376d --- /dev/null +++ b/modules/widget_dao/dao/plugin_dao_read_only.cpp @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_dao_read_only.cpp + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + * @brief This file contains the implementation of plugin dao read only + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace WrtDB { + +namespace { + +typedef DPL::DB::ORM::wrt::PluginProperties::Row PluginRow; + +PluginRow getPluginRow(DbPluginHandle pluginHandle) +{ + LogDebug("Getting plugin row. Handle: " << pluginHandle); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, PluginProperties, &WrtDatabase::interface()) + select->Where(Equals( + pluginHandle)); + + PluginProperties::Select::RowList rows = select->GetRowList(); + if (rows.empty()) { + ThrowMsg(PluginDAOReadOnly::Exception::PluginNotExist, + "Cannot find plugin. Handle: " + pluginHandle); + } + return rows.front(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(PluginDAOReadOnly::Exception::DatabaseError, + "Failed in GetPluginRow"); + } +} +} + +PluginDAOReadOnly::PluginDAOReadOnly(DbPluginHandle pluginHandle) : + m_pluginHandle(pluginHandle) +{ + if (!isPluginInstalled(m_pluginHandle)) { + LogError("Plugin " << m_pluginHandle << " not installed."); + Throw(PluginDAOReadOnly::Exception::PluginNotExist); + } + + checkInstallationCompleted(); +} + +PluginDAOReadOnly::PluginDAOReadOnly(const std::string &libraryName) +{ + LogDebug("PluginDAOReadOnly ( " << libraryName << " )"); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, PluginProperties, &WrtDatabase::interface()) + select->Where(Equals( + DPL::FromUTF8String(libraryName))); + + PluginProperties::Select::RowList rows = select->GetRowList(); + if (!rows.empty()) { + m_pluginHandle = rows.front().Get_PluginPropertiesId(); + } else { + ThrowMsg(PluginDAOReadOnly::Exception::PluginNotExist, + "Cannot find plugin: [" + libraryName + "]"); + } + LogDebug(" >> Handle for this plugin: " << m_pluginHandle); + + checkInstallationCompleted(); + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(PluginDAOReadOnly::Exception::DatabaseError, + "Failed to connect to database"); + } +} + +void PluginDAOReadOnly::checkInstallationCompleted() +{ + if (getInstallationStateForHandle(m_pluginHandle) + != PluginDAOReadOnly::INSTALLATION_COMPLETED) { + LogError("Plugin " << m_pluginHandle << " installation not completed"); + Throw(PluginDAOReadOnly::Exception::PluginInstallationNotCompleted); + } +} + +bool PluginDAOReadOnly::isPluginInstalled(const std::string &libraryName) +{ + LogDebug("Check if Library is installed. LibraryName: " << libraryName); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, PluginProperties, &WrtDatabase::interface()) + select->Where(Equals( + DPL::FromUTF8String(libraryName))); + + PluginProperties::Select::RowList rows = select->GetRowList(); + + bool flag = !rows.empty(); + LogDebug(" >> Plugin " << libraryName << + (flag ? " found." : " not found.")); + + return flag; + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(PluginDAOReadOnly::Exception::DatabaseError, + "Failed in isPluginInstalled"); + } +} + +PluginHandleList PluginDAOReadOnly::getPluginHandleList() +{ + LogDebug("Getting plugin handle list."); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, PluginProperties, &WrtDatabase::interface()) + + PluginHandleList ret = + select->GetValueList(); + + std::ostringstream handles; + FOREACH(it, ret) + handles << *it << " "; + LogDebug(" >> PluginHandle list retrieved: (" << handles << ")"); + + return ret; + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(PluginDAOReadOnly::Exception::DatabaseError, + "Failed in GetPluginHandleList"); + } +} + +DbPluginHandle PluginDAOReadOnly::getPluginHandle() const +{ + return m_pluginHandle; +} + + +//"value" cannot be null, as in registerPlugin it is always set +#define RETURN_STD_STRING(in, what) \ + std::string ret = ""; \ + if (!in.IsNull()) { \ + ret = DPL::ToUTF8String(*in); } \ + LogDebug(" >> Plugin " << what << ": " << ret); \ + return ret; + +std::string PluginDAOReadOnly::getLibraryPath() const +{ + LogDebug("Getting plugin library path. Handle: " << m_pluginHandle); + PluginRow row = getPluginRow(m_pluginHandle); + RETURN_STD_STRING(row.Get_PluginLibraryPath(), "library path") +} + +std::string PluginDAOReadOnly::getLibraryName() const +{ + LogDebug("Getting plugin library name. Handle: " << m_pluginHandle); + PluginRow row = getPluginRow(m_pluginHandle); + std::string ret = DPL::ToUTF8String(row.Get_PluginLibraryName()); + LogDebug(" >> Plugin library name: " << ret); + return ret; +} + +std::string PluginDAOReadOnly::getInstallURI() const +{ + LogDebug("Getting plugin install URI. Handle: " << m_pluginHandle); + PluginRow row = getPluginRow(m_pluginHandle); + RETURN_STD_STRING(row.Get_InstallURI(), "install URI") +} + +std::string PluginDAOReadOnly::getKeyCn() const +{ + LogDebug("Getting plugin KeyCn. Handle: " << m_pluginHandle); + PluginRow row = getPluginRow(m_pluginHandle); + RETURN_STD_STRING(row.Get_KeyCN(), "keyCN") +} + +std::string PluginDAOReadOnly::getRootKey() const +{ + LogDebug("Getting plugin rootKey. Handle: " << m_pluginHandle); + PluginRow row = getPluginRow(m_pluginHandle); + RETURN_STD_STRING(row.Get_RootKeyCN(), "rootKey") +} + +std::string PluginDAOReadOnly::getRootKeyFingerprint() const +{ + LogDebug("Getting plugin rootKeyFingerprint. Handle: " << m_pluginHandle); + PluginRow row = getPluginRow(m_pluginHandle); + RETURN_STD_STRING(row.Get_RootKeyFingerprint(), "rootKeyFingerprint") +} + +#undef RETURN_STD_STRING + +PluginHandleSetPtr PluginDAOReadOnly::getLibraryDependencies() const +{ + Try + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + PluginHandleSetPtr dependencies(new PluginHandleSet); + + WRT_DB_SELECT(select, PluginDependencies, &WrtDatabase::interface()) + select->Where( + Equals(m_pluginHandle)); + + PluginDependencies::Select::RowList rows = select->GetRowList(); + + if (!rows.empty()) { + FOREACH(it, rows) + { + dependencies->insert(it->Get_RequiredPluginPropertiesId()); + } + } + + return dependencies; + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(PluginDAOReadOnly::Exception::DatabaseError, + "Failed in GetLibraryDependencies"); + } +} + +DbPluginHandle PluginDAOReadOnly::getPluginHandleForImplementedObject( + const std::string& objectName) +{ + LogDebug("GetPluginHandle for object: " << objectName); + + Try + { + DbPluginHandle pluginHandle = INVALID_PLUGIN_HANDLE; + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + WRT_DB_SELECT(select, PluginImplementedObjects, &WrtDatabase::interface()) + select->Where( + Equals( + DPL::FromUTF8String(objectName))); + + PluginImplementedObjects::Select::RowList rows = select->GetRowList(); + + if (!rows.empty()) { + pluginHandle = rows.front().Get_PluginPropertiesId(); + } else { + LogWarning("PluginHandle for object not found"); + } + return pluginHandle; + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(PluginDAOReadOnly::Exception::DatabaseError, + "Failed in GetPluginHandleForImplementedObject"); + } +} + +ImplementedObjectsList PluginDAOReadOnly::getImplementedObjectsForPluginHandle( + DbPluginHandle handle) +{ + LogDebug("getImplementedObjects for pluginHandle: " << handle); + + Try + { + ImplementedObjectsList objectList; + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + WRT_DB_SELECT(select, PluginImplementedObjects, &WrtDatabase::interface()) + select->Where( + Equals(handle)); + + PluginImplementedObjects::Select::RowList rows = select->GetRowList(); + + if (!rows.empty()) { + FOREACH(it, rows) + { + objectList.push_back(DPL::ToUTF8String(it->Get_PluginObject())); + } + } else { + LogWarning("PluginHandle for object not found"); + } + return objectList; + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(PluginDAOReadOnly::Exception::DatabaseError, + "Failed in GetPluginHandleForImplementedObject"); + } +} + +PluginObjectsDAO::ObjectsPtr PluginDAOReadOnly::getRequiredObjectsForPluginHandle( + DbPluginHandle handle) +{ + Try + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + PluginObjectsDAO::ObjectsPtr objects = + PluginObjectsDAO::ObjectsPtr(new PluginObjectsDAO::Objects); + + WRT_DB_SELECT(select, PluginRequiredObjects, &WrtDatabase::interface()) + select->Where( + Equals(handle)); + + PluginRequiredObjects::Select::RowList rows = select->GetRowList(); + + if (!rows.empty()) { + FOREACH(it, rows) + { + objects->insert(DPL::ToUTF8String(it->Get_PluginObject())); + } + } + + return objects; + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(PluginDAOReadOnly::Exception::DatabaseError, + "Failed in GetRequiredObjectsForPluginHandle"); + } +} + +PluginHandleSetPtr PluginDAOReadOnly::getPluginHandleByStatus( + PluginInstallationState state) +{ + Try + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + PluginHandleSetPtr handleSet(new PluginHandleSet); + + WRT_DB_SELECT(select, PluginProperties, &WrtDatabase::interface()) + select->Where( + Equals(ToInt(state))); + + PluginProperties::Select::RowList rows = select->GetRowList(); + + if (!rows.empty()) { + FOREACH(it, rows) + { + handleSet->insert(it->Get_PluginPropertiesId()); + } + } + + return handleSet; + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(PluginDAOReadOnly::Exception::DatabaseError, + "Failed in GetPluginHandleByStatus"); + } +} + +PluginDAOReadOnly::PluginInstallationState PluginDAOReadOnly::getInstallationStatus() const +{ + PluginRow row = getPluginRow(m_pluginHandle); + return ToState(row.Get_InstallationState()); +} + +PluginDAOReadOnly::PluginInstallationState PluginDAOReadOnly::getInstallationStateForHandle( + DbPluginHandle handle) +{ + Try + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + WRT_DB_SELECT(select, PluginProperties, &WrtDatabase::interface()) + select->Where( + Equals(handle)); + + PluginProperties::Select::RowList rows = select->GetRowList(); + + if (!rows.empty()) { + return ToState(rows.front().Get_InstallationState()); + } + LogError("Data in DB are invalid. Missing field"); + return UNKNOWN_ERROR; + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(PluginDAOReadOnly::Exception::DatabaseError, + "Failed in GetStatusForHandle"); + } +} + +bool PluginDAOReadOnly::isPluginInstalled(DbPluginHandle pluginHandle) +{ + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, PluginProperties, &WrtDatabase::interface()) + select->Where( + Equals(pluginHandle)); + + PluginProperties::Select::RowList rows = select->GetRowList(); + + bool flag = !rows.empty(); + + return flag; + } + Catch(DPL::DB::SqlConnection::Exception::Base) { + ReThrowMsg(PluginDAOReadOnly::Exception::DatabaseError, + "Failed in isPluginInstalled"); + } +} + +} // namespace WrtDB diff --git a/modules/widget_dao/dao/property_dao.cpp b/modules/widget_dao/dao/property_dao.cpp new file mode 100644 index 0000000..95ba056 --- /dev/null +++ b/modules/widget_dao/dao/property_dao.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file property_dao.h + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the definition of property dao class. + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace WrtDB { +namespace PropertyDAO { + +void RemoveProperty(DbWidgetHandle widgetHandle, + const PropertyDAOReadOnly::WidgetPropertyKey &key) +{ + //TODO below there are two queries. + // First query asks if given property can be removed, + // Second removes it. Maybe it should be combined two one. + + LogDebug("Removing Property. Handle: " << widgetHandle << ", key: " << key); + Try { + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + + DPL::OptionalInt readonly = PropertyDAOReadOnly::CheckPropertyReadFlag( + widgetHandle, + key); + if (!readonly.IsNull() && *readonly == 1) { + LogError("'" << key << + "' key is readonly. Cannot remove property !"); + ThrowMsg(PropertyDAOReadOnly::Exception::ReadOnlyProperty, + "Property is readonly"); + } + + // Key is not readonly, or has no flag defined + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_DELETE(del, WidgetPreference, &WrtDatabase::interface()) + del->Where(And( + Equals(widgetHandle), + Equals(key))); + del->Execute(); + + transaction.Commit(); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(PropertyDAOReadOnly::Exception::DatabaseError, + "Failure during removing property"); + } +} + +void SetProperty(DbWidgetHandle widgetHandle, + const PropertyDAOReadOnly::WidgetPropertyKey &key, + const PropertyDAOReadOnly::WidgetPropertyValue &value, + bool readOnly) +{ + LogDebug("Setting/updating Property. Handle: " << widgetHandle << + ", key: " << key); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + + DPL::OptionalInt readonly = PropertyDAOReadOnly::CheckPropertyReadFlag( + widgetHandle, + key); + if (!readonly.IsNull() && *readonly == 1) { + LogError("'" << key << + "' key is readonly. Cannot remove property !"); + ThrowMsg(PropertyDAOReadOnly::Exception::ReadOnlyProperty, + "Property is readonly"); + } + + if (readonly.IsNull()) { + WidgetPreference::Row row; + row.Set_app_id(widgetHandle); + row.Set_key_name(key); + row.Set_key_value(value); + row.Set_readonly(readOnly ? 1 : 0); + + WRT_DB_INSERT(insert, WidgetPreference, &WrtDatabase::interface()) + insert->Values(row); + insert->Execute(); + } else { + WidgetPreference::Row row; + row.Set_key_value(value); + + WRT_DB_UPDATE(update, WidgetPreference, &WrtDatabase::interface()) + update->Where(And( + Equals(widgetHandle), + Equals(key))); + + update->Values(row); + update->Execute(); + } + transaction.Commit(); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(PropertyDAOReadOnly::Exception::DatabaseError, + "Failure during setting/updating property"); + } +} + +void RegisterProperties(DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info) +{ + LogDebug("Registering proferences for widget. Handle: " << widgetHandle); + + // Try-Catch in WidgetDAO + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + const ConfigParserData& widgetConfigurationInfo = regInfo.configInfo; + + FOREACH(it, widgetConfigurationInfo.preferencesList) + { + { // Insert into table Widget Preferences + WidgetPreference::Row row; + row.Set_app_id(widgetHandle); + row.Set_key_name(it->name); + row.Set_key_value(it->value); + int readonly = true == it->readonly ? 1 : 0; + row.Set_readonly(readonly); + + WRT_DB_INSERT(insert, WidgetPreference, &WrtDatabase::interface()) + insert->Values(row); + insert->Execute(); + } + } +} + +} // namespace PropertyDAO +} // namespace WrtDB diff --git a/modules/widget_dao/dao/property_dao_read_only.cpp b/modules/widget_dao/dao/property_dao_read_only.cpp new file mode 100644 index 0000000..74208af --- /dev/null +++ b/modules/widget_dao/dao/property_dao_read_only.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * property_dao_read_only.cpp + * + * Created on: Nov 16, 2011 + * Author: Krzysztof Jackiewicz(k.jackiewicz@samsung.com) + */ + +#include +#include +#include +#include +#include +#include + +namespace WrtDB { +namespace PropertyDAOReadOnly { + +namespace { + +typedef DPL::DB::ORM::wrt::WidgetPreference::key_name::ColumnType + ORMWidgetPropertyKey; +typedef DPL::DB::ORM::wrt::WidgetPreference::key_value::ColumnType + ORMWidgetPropertyValue; +typedef std::list ORMWidgetPreferenceList; +typedef std::list ORMWidgetPropertyKeyList; + + +void convertPropertyKey(const ORMWidgetPropertyKey& ormKey, + WidgetPropertyKey& key) +{ + key = ormKey; +} + +void convertPropertyValue(const ORMWidgetPropertyValue& ormPropertyVal, + WidgetPropertyValue& propertyVal) +{ + propertyVal = ormPropertyVal; +} + +void convertWidgetPreferenceRow(const ORMWidgetPreferenceList& ormWidgetPrefRow, + WidgetPreferenceList& prefRow) +{ + FOREACH(it, ormWidgetPrefRow) { + WidgetPreferenceRow row; + + row.app_id = it->Get_app_id(); + row.key_name = it->Get_key_name(); + row.key_value = it->Get_key_value(); + row.readonly = it->Get_readonly(); + + prefRow.push_back(row); + } +} + +void convertWidgetPropertyKeyList(const ORMWidgetPropertyKeyList& propKeyList, + WidgetPropertyKeyList& keyList) +{ + FOREACH(it, propKeyList) { + keyList.push_back(*it); + } +} + + +} + +WidgetPropertyKeyList GetPropertyKeyList(DbWidgetHandle widgetHandle) +{ + LogDebug("Get PropertyKey list. Handle: " << widgetHandle); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + ORMWidgetPropertyKeyList keyList; + WRT_DB_SELECT(select, WidgetPreference, &WrtDatabase::interface()) + select->Where(Equals(widgetHandle)); + keyList = select->GetValueList(); + + WidgetPropertyKeyList retKeyList; + + convertWidgetPropertyKeyList(keyList, retKeyList); + return retKeyList; + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(Exception::DatabaseError, + "Failure during getting propertykey list"); + } +} + +WidgetPreferenceList GetPropertyList(DbWidgetHandle widgetHandle) +{ + LogDebug("Get Property list. Handle: " << widgetHandle); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetPreference, &WrtDatabase::interface()) + select->Where(Equals(widgetHandle)); + + ORMWidgetPreferenceList ormPrefList = select->GetRowList(); + WidgetPreferenceList prefList; + convertWidgetPreferenceRow(ormPrefList, prefList); + + return prefList; + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(Exception::DatabaseError, + "Failure during getting property list"); + } +} + +WidgetPropertyValue GetPropertyValue(DbWidgetHandle widgetHandle, + const WidgetPropertyKey &key) +{ + LogDebug("Get Property value. Handle: " << widgetHandle << + ", key: " << key); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetPreference, &WrtDatabase::interface()) + select->Where(And(Equals(widgetHandle), + Equals(key))); + + ORMWidgetPropertyValue ormPropValue = + select->GetSingleValue(); + + WidgetPropertyValue propValue; + + convertPropertyValue(ormPropValue, propValue); + + return propValue; + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(Exception::DatabaseError, + "Failure during getting property"); + } +} + +DPL::OptionalInt CheckPropertyReadFlag(DbWidgetHandle widgetHandle, + const WidgetPropertyKey &key) +{ + LogDebug("Checking Property flag. Handle: " << widgetHandle << + ", key: " << key); + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetPreference, &WrtDatabase::interface()) + select->Where(And(Equals(widgetHandle), + Equals(key))); + + return select->GetSingleValue(); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(Exception::DatabaseError, + "Failure during checking readonly flag for property"); + } +} + +} // namespace PropertyDAOReadOnly +} // namespace WrtDB diff --git a/modules/widget_dao/dao/webruntime_database.cpp b/modules/widget_dao/dao/webruntime_database.cpp new file mode 100644 index 0000000..bc24fbe --- /dev/null +++ b/modules/widget_dao/dao/webruntime_database.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file webruntime_database.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file contains the definition of webruntime database + */ +#include + +DPL::Mutex g_wrtDbQueriesMutex; + diff --git a/modules/widget_dao/dao/widget_dao.cpp b/modules/widget_dao/dao/widget_dao.cpp new file mode 100644 index 0000000..be33379 --- /dev/null +++ b/modules/widget_dao/dao/widget_dao.cpp @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file contains the definition of widget dao class. + * + * @file widget_dao.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Bartosz Janiak (b.janiak@samsung.com) + * @author Yang Jie (jie2.yang@samsung.com) + * @author Koeun Choi(koeun.choi@samsung.com) + * @author Pawel Sikorski(p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the definition of Configuration. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace WrtDB { + +//TODO in current solution in each getter there exists a check +//"IsWidgetInstalled". Maybe it should be verified, if it could be done +//differently (check in WidgetDAO constructor) + +#define SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN Try + +#define SQL_CONNECTION_EXCEPTION_HANDLER_END(message) \ + Catch(DPL::DB::SqlConnection::Exception::Base) { \ + LogError(message); \ + ReThrowMsg(WidgetDAO::Exception::DatabaseError, \ + message); \ + } + +#define CHECK_WIDGET_EXISTENCE(macro_transaction, macro_handle) \ + if (!WidgetDAO::isWidgetInstalled(macro_handle)) \ + { \ + macro_transaction.Commit(); \ + LogWarning("Cannot find widget. Handle: " << macro_handle); \ + ThrowMsg(WidgetDAO::Exception::WidgetNotExist, \ + "Cannot find widget. Handle: " << macro_handle); \ + } + + +WidgetDAO::WidgetDAO(DbWidgetHandle widgetHandle) : + WidgetDAOReadOnly(widgetHandle) +{ +} + +WidgetDAO::WidgetDAO(DPL::OptionalString widgetGUID) : + WidgetDAOReadOnly(WidgetDAOReadOnly::getHandle(widgetGUID)) +{ +} + +WidgetDAO::~WidgetDAO() +{ +} + +void WidgetDAO::removeProperty( + const PropertyDAOReadOnly::WidgetPropertyKey &key) +{ + Try { + PropertyDAO::RemoveProperty(m_widgetHandle, key); + } + Catch(PropertyDAOReadOnly::Exception::ReadOnlyProperty){ + ReThrowMsg(WidgetDAO::Exception::DatabaseError, + "Failure during removing property"); + } +} + +void WidgetDAO::setProperty( + const PropertyDAOReadOnly::WidgetPropertyKey &key, + const PropertyDAOReadOnly::WidgetPropertyValue &value, + bool readOnly) +{ + Try { + PropertyDAO::SetProperty(m_widgetHandle, key, value, readOnly); + } + Catch(PropertyDAOReadOnly::Exception::ReadOnlyProperty){ + ReThrowMsg(WidgetDAO::Exception::DatabaseError, + "Failure during setting/updating property"); + } +} + +void WidgetDAO::setPkgName(const DPL::OptionalString& pkgName) +{ + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + + isWidgetInstalled(getHandle()); + + wrt::WidgetInfo::Row row; + row.Set_pkgname(pkgName); + + WRT_DB_UPDATE(update, wrt::WidgetInfo, &WrtDatabase::interface()) + update->Where( + Equals(getHandle())); + + update->Values(row); + update->Execute(); + transaction.Commit(); + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to register widget") +} + +DbWidgetHandle WidgetDAO::registerWidget(const WidgetRegisterInfo &widgetRegInfo, + const IWacSecurity &wacSecurity, + const LanguageTagsList& languageTags) +{ + LogDebug("Registering widget"); + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + + //Register into WidgetInfo has to be first + //as all other tables depend upon that + DbWidgetHandle widgetHandle = + registerWidgetInfo(widgetRegInfo, wacSecurity); + + registerWidgetExtendedInfo(widgetHandle, widgetRegInfo); + + registerWidgetLocalizedInfo(widgetHandle, widgetRegInfo); + + registerWidgetUserAgentLocales( + widgetHandle, widgetRegInfo, languageTags); + + registerWidgetIcons(widgetHandle, widgetRegInfo); + + registerWidgetStartFile(widgetHandle, widgetRegInfo); + + PropertyDAO::RegisterProperties(widgetHandle, widgetRegInfo); + + registerWidgetFeatures(widgetHandle, widgetRegInfo); + + registerWidgetWindowModes(widgetHandle, widgetRegInfo); + + registerWidgetWarpInfo(widgetHandle, widgetRegInfo); + + registerWidgetCertificates(widgetHandle, wacSecurity); + + CertificateChainList list; + wacSecurity.getCertificateChainList(list); + registerLaunchCertificates(widgetHandle,list); + + registerWidgetPowderData(widgetHandle, widgetRegInfo); + + registerWidgetSettings(widgetHandle, widgetRegInfo); + + registerAppService(widgetHandle, widgetRegInfo); + + transaction.Commit(); + + return widgetHandle; + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to register widget") +} + +#define DO_INSERT(row, table) \ + { \ + WRT_DB_INSERT(insert, table, &WrtDatabase::interface()) \ + insert->Values(row); \ + insert->Execute(); \ + } + +void WidgetDAO::registerWidgetExtendedInfo(DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info) +{ + //Try and transaction not needed + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + + WidgetExtendedInfo::Row row; + row.Set_app_id(widgetHandle); + // row.Set_share_href (DPL::FromUTF8String(regInfo.shareHref)); + row.Set_signature_type(regInfo.signatureType); + row.Set_factory_widget(regInfo.isFactoryWidget); + row.Set_test_widget(regInfo.isTestWidget); + row.Set_install_time(regInfo.installedTime); + + DO_INSERT(row, WidgetExtendedInfo) +} + +DbWidgetHandle WidgetDAO::registerWidgetInfo(const WidgetRegisterInfo ®Info, + const IWacSecurity &wacSecurity) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + const ConfigParserData& widgetConfigurationInfo = regInfo.configInfo; + + // TODO in wrt_db all Columns in WidgetInfo have DEFAULT VALUE set. + // Because of that, "Optional" is not used there + + WidgetInfo::Row row; + row.Set_widget_type(regInfo.type.appType); + row.Set_widget_id(widgetConfigurationInfo.widget_id); + row.Set_defaultlocale(widgetConfigurationInfo.defaultlocale); + row.Set_widget_version(widgetConfigurationInfo.version); + row.Set_widget_width(widgetConfigurationInfo.width); + row.Set_widget_height(widgetConfigurationInfo.height); + row.Set_author_name(widgetConfigurationInfo.authorName); + row.Set_author_email(widgetConfigurationInfo.authorEmail); + row.Set_author_href(widgetConfigurationInfo.authorHref); + row.Set_base_folder(DPL::FromUTF8String(regInfo.baseFolder)); + row.Set_webkit_plugins_required(widgetConfigurationInfo.flashNeeded); + row.Set_child_protection(1); + row.Set_recognized(wacSecurity.isRecognized()); + row.Set_wac_signed(wacSecurity.isWacSigned()); + row.Set_distributor_signed(wacSecurity.isDistributorSigned()); + { + std::stringstream tmp; + tmp << widgetConfigurationInfo.minVersionRequired; + row.Set_min_version(DPL::FromUTF8String(tmp.str())); + } + row.Set_back_supported(widgetConfigurationInfo.backSupported); + row.Set_access_network(widgetConfigurationInfo.accessNetwork); + row.Set_pkgname(regInfo.pkgname); + + wrt::WidgetInfo::app_id::ColumnType appID; + { + WRT_DB_INSERT(insert, WidgetInfo, &WrtDatabase::interface()) + insert->Values(row); + appID = insert->Execute(); + } + return appID; +} + +void WidgetDAO::registerWidgetLocalizedInfo(DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + const ConfigParserData& widgetConfigurationInfo = regInfo.configInfo; + + FOREACH(it, widgetConfigurationInfo.localizedDataSet) + { + const DPL::String& locale = it->first; + const ConfigParserData::LocalizedData& data = it->second; + + LocalizedWidgetInfo::Row row; + row.Set_app_id(widgetHandle); + row.Set_widget_locale(locale); + row.Set_widget_name(data.name); + row.Set_widget_shortname(data.shortName); + row.Set_widget_description(data.description); + row.Set_widget_license(data.license); + row.Set_widget_license_file(data.licenseFile); + row.Set_widget_license_href(data.licenseHref); + + DO_INSERT(row, LocalizedWidgetInfo) + } +} + +void WidgetDAO::registerWidgetUserAgentLocales( + DbWidgetHandle widgetHandle, + const WidgetRegisterInfo &/*regInfo*/, + const LanguageTagsList& languageTags) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + + FOREACH(i, languageTags) + { + wrt::WidgetUserAgentLocales::Row row; + row.Set_app_id(widgetHandle); + row.Set_language_tag(*i); + + DO_INSERT(row, wrt::WidgetUserAgentLocales) + } +} + +void WidgetDAO::registerWidgetIcons(DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + + FOREACH(i, regInfo.localizationData.icons) + { + wrt::WidgetIcon::icon_id::ColumnType icon_id; + { + wrt::WidgetIcon::Row row; + row.Set_app_id(widgetHandle); + row.Set_icon_src(i->src); + row.Set_icon_width(i->width); + row.Set_icon_height(i->height); + + WRT_DB_INSERT(insert, wrt::WidgetIcon, &WrtDatabase::interface()) + insert->Values(row); + icon_id = insert->Execute(); + } + + FOREACH(j, i->availableLocales) + { + WidgetLocalizedIcon::Row row; + row.Set_app_id(widgetHandle); + row.Set_icon_id(icon_id); + row.Set_widget_locale(*j); + WRT_DB_SELECT(select, WidgetLocalizedIcon, &WrtDatabase::interface()) + select->Where(And(Equals(widgetHandle), + Equals(*j))); + WidgetLocalizedIcon::Select::RowList rows = select->GetRowList(); + + bool flag = !rows.empty(); + + if(flag == true) + { + // already default icon value of same locale exists + WRT_DB_UPDATE(update, WidgetLocalizedIcon, &WrtDatabase::interface()) + update->Where(And(Equals(widgetHandle), + Equals(*j))); + update->Values(row); + update->Execute(); + }else{ + // any icon value of same locale doesn't exist + DO_INSERT(row, WidgetLocalizedIcon) + } + } + } +} + +void WidgetDAO::registerWidgetStartFile(DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + + FOREACH(i, regInfo.localizationData.startFiles) + { + WidgetStartFile::start_file_id::ColumnType startFileID; + { + WidgetStartFile::Row row; + row.Set_app_id(widgetHandle); + row.Set_src(i->path); + + WRT_DB_INSERT(insert, WidgetStartFile, &WrtDatabase::interface()) + insert->Values(row); + startFileID = insert->Execute(); + } + + FOREACH(j, i->propertiesForLocales) + { + WidgetLocalizedStartFile::Row row; + row.Set_app_id(widgetHandle); + row.Set_start_file_id(startFileID); + row.Set_widget_locale(j->first); + row.Set_type(j->second.type); + row.Set_encoding(j->second.encoding); + + DO_INSERT(row, WidgetLocalizedStartFile) + } + } +} + +void WidgetDAO::registerWidgetFeatures(DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info) +{ + using namespace DPL::DB::ORM; + const ConfigParserData& widgetConfigurationInfo = regInfo.configInfo; + + + FOREACH(pWidgetFeature, widgetConfigurationInfo.featuresList) + { + wrt::WidgetFeature::Row widgetFeature; + widgetFeature.Set_app_id(widgetHandle); + widgetFeature.Set_name(pWidgetFeature->name); + widgetFeature.Set_required(pWidgetFeature->required); + + wrt::WidgetFeature::widget_feature_id::ColumnType widgetFeatureID; + { + WRT_DB_INSERT(insert, wrt::WidgetFeature, &WrtDatabase::interface()) + insert->Values(widgetFeature); + widgetFeatureID = insert->Execute(); + } + + // Insert into table FeatureParam + wrt::FeatureParam::Row featureParam; + featureParam.Set_widget_feature_id(widgetFeatureID); + + ConfigParserData::ParamsList::const_iterator iter; + + FOREACH(iter, pWidgetFeature->paramsList) + { + featureParam.Set_name(iter->name); + featureParam.Set_value(iter->value); + + DO_INSERT(featureParam, wrt::FeatureParam) + } + } +} + +void WidgetDAO::registerWidgetWindowModes(DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + const ConfigParserData& widgetConfigurationInfo = regInfo.configInfo; + + + FOREACH(i, widgetConfigurationInfo.windowModes) + { + wrt::WidgetWindowModes::Row windowMode; + windowMode.Set_app_id(widgetHandle); + windowMode.Set_window_mode(*i); + + DO_INSERT(windowMode, wrt::WidgetWindowModes) + } +} + +void WidgetDAO::registerWidgetWarpInfo(DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + const ConfigParserData& widgetConfigurationInfo = regInfo.configInfo; + + + FOREACH(AccIt, widgetConfigurationInfo.accessInfoSet) + { + WidgetWARPInfo::Row row; + row.Set_app_id(widgetHandle); + row.Set_iri(AccIt->m_strIRI); + row.Set_subdomain_access(static_cast ( + AccIt->m_bSubDomainAccess)); + + DO_INSERT(row, WidgetWARPInfo) + } +} + +void WidgetDAO::registerWidgetCertificates(DbWidgetHandle widgetHandle, + const IWacSecurity &wacSecurity) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + + FOREACH(it, wacSecurity.getCertificateList()) + { + WidgetCertificateFingerprint::Row row; + row.Set_app_id(widgetHandle); + row.Set_owner(it->owner); + row.Set_chainid(it->chainId); + row.Set_type(it->type); + row.Set_md5_fingerprint(DPL::FromUTF8String(it->strMD5Fingerprint)); + row.Set_sha1_fingerprint(DPL::FromUTF8String(it->strSHA1Fingerprint)); + row.Set_common_name(it->strCommonName); + + DO_INSERT(row, WidgetCertificateFingerprint) + } +} + +void WidgetDAO::registerWidgetPowderData(DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + using namespace Powder; + + FOREACH(i, regInfo.powderDescription.categories) + { + const DPL::String& categoryName(i->first); + const Description::CategoryEntry& categoryEntry(i->second); + FOREACH(l, categoryEntry.levels) + { + PowderLevels::id::ColumnType powderID; + { + PowderLevels::Row row; + row.Set_app_id(widgetHandle); + row.Set_category(categoryName); + row.Set_level(l->level); + + WRT_DB_INSERT(insert, PowderLevels, &WrtDatabase::interface()) + insert->Values(row); + powderID = insert->Execute(); + } + + FOREACH(c, l->context) + { + PowderLevelContexts::Row row; + row.Set_levelId(powderID); + row.Set_context(*c); + + DO_INSERT(row, PowderLevelContexts) + } + } + } +} + +void WidgetDAO::registerLaunchCertificates(DbWidgetHandle widgetHandle, + const CertificateChainList &certificateChainList) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + FOREACH(certChain, certificateChainList) + { + WidgetCertificate::Row row; + row.Set_app_id(widgetHandle); + row.Set_encoded_chain(DPL::FromASCIIString(*certChain)); + + DO_INSERT(row, WidgetCertificate); + } +} + +void WidgetDAO::registerWidgetSettings(DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + const ConfigParserData& widgetConfigurationInfo = regInfo.configInfo; + + FOREACH(pWidgetSetting, widgetConfigurationInfo.settingsList) + { + SettginsList::Row row; + row.Set_appId(widgetHandle); + row.Set_settingName(pWidgetSetting->m_name); + row.Set_settingValue(pWidgetSetting->m_value); + + DO_INSERT(row, SettginsList) + } +} + +void WidgetDAO::registerAppService(DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info) +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + const ConfigParserData& widgetConfigurationInfo = regInfo.configInfo; + + FOREACH(ASIt, widgetConfigurationInfo.appServiceList) + { + ApplicationServiceInfo::Row row; + row.Set_app_id(widgetHandle); + row.Set_src(ASIt->m_src); + row.Set_operation(ASIt->m_operation); + row.Set_scheme(ASIt->m_scheme); + row.Set_mime(ASIt->m_mime); + + DO_INSERT(row, ApplicationServiceInfo) + } +} + +#undef DO_INSERT + +void WidgetDAO::unregisterWidget(DbWidgetHandle widgetHandle) +{ + LogDebug("Unregistering widget from DB. Handle: " << widgetHandle); + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + + CHECK_WIDGET_EXISTENCE(transaction, widgetHandle) + + // Delete from table Widget Info + { + WRT_DB_DELETE(del, WidgetInfo, &WrtDatabase::interface()) + del->Where(Equals(widgetHandle)); + del->Execute(); + } + + // Deleting in other tables is done via "delete cascade" in SQL + + transaction.Commit(); + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to unregister widget") +} + +#undef SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN +#undef SQL_CONNECTION_EXCEPTION_HANDLER_END +#undef CHECK_WIDGET_EXISTENCE + +} // namespace WrtDB diff --git a/modules/widget_dao/dao/widget_dao_read_only.cpp b/modules/widget_dao/dao/widget_dao_read_only.cpp new file mode 100644 index 0000000..d3c9377 --- /dev/null +++ b/modules/widget_dao/dao/widget_dao_read_only.cpp @@ -0,0 +1,1094 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file contains the declaration of widget dao class. + * + * @file widget_dao_read_only.cpp + * @author Yang Jie (jie2.yang@samsung.com) + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of widget dao + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace WrtDB { + +//TODO in current solution in each getter there exists a check +//"IsWidgetInstalled". Maybe it should be verified, if it could be done +//differently (check in WidgetDAOReadOnly constructor) + +#define SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN Try + +#define SQL_CONNECTION_EXCEPTION_HANDLER_END(message) \ + Catch(DPL::DB::SqlConnection::Exception::Base) { \ + LogError(message); \ + ReThrowMsg(WidgetDAOReadOnly::Exception::DatabaseError, \ + message); \ + } + +#define CHECK_WIDGET_EXISTENCE(macro_transaction, macro_handle) \ + if (!WidgetDAOReadOnly::isWidgetInstalled(macro_handle)) \ + { \ + macro_transaction.Commit(); \ + LogWarning("Cannot find widget. Handle: " << macro_handle); \ + ThrowMsg(WidgetDAOReadOnly::Exception::WidgetNotExist, \ + "Cannot find widget. Handle: " << macro_handle); \ + } + + +typedef DPL::DB::ORM::wrt::WidgetInfo::Row WidgetInfoRow; +typedef DPL::DB::ORM::wrt::WidgetFeature::widget_feature_id::ColumnType + WidgetFeatureId; + +namespace { + +WidgetInfoRow getWidgetInfoRow(int widgetHandle) +{ + LogDebug("Getting WidgetInfo row. Handle: " << widgetHandle); + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetInfo, &WrtDatabase::interface()) + select->Where(Equals(widgetHandle)); + + WidgetInfo::Select::RowList rows = select->GetRowList(); + if (rows.empty()) { + ThrowMsg(WidgetDAOReadOnly::Exception::WidgetNotExist, + "Cannot find widget. Handle: " << widgetHandle); + } + return rows.front(); + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed in GetWidgetInfoRow") +} + +} // namespace + + +IWacSecurity::~IWacSecurity() +{ +} + +WidgetDAOReadOnly::WidgetDAOReadOnly(DbWidgetHandle widgetHandle) : + m_widgetHandle(widgetHandle) +{ +} + +WidgetDAOReadOnly::~WidgetDAOReadOnly() +{ +} + +DbWidgetHandle WidgetDAOReadOnly::getHandle() const +{ + return m_widgetHandle; +} + +DbWidgetHandle WidgetDAOReadOnly::getHandle(const WidgetGUID GUID) +{ + LogDebug("Getting WidgetHandle by GUID [" << GUID << "]"); + + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetInfo, &WrtDatabase::interface()) + select->Where(Equals(GUID)); + WidgetInfo::Select::RowList rowList = select->GetRowList(); + + if (rowList.empty()) { + ThrowMsg(WidgetDAOReadOnly::Exception::WidgetNotExist, + "Failed to get widget by guid"); + } + return rowList.front().Get_app_id(); + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed in getHandle") + + ThrowMsg(WidgetDAOReadOnly::Exception::WidgetNotExist, + "Failed to get widget by guid"); +} + +DbWidgetHandle WidgetDAOReadOnly::getHandle(const DPL::String pkgName) +{ + LogDebug("Getting WidgetHandle by Package Name [" << pkgName << "]"); + + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetInfo, &WrtDatabase::interface()) + select->Where(Equals(pkgName)); + WidgetInfo::Select::RowList rowList = select->GetRowList(); + + if (rowList.empty()) { + ThrowMsg(WidgetDAOReadOnly::Exception::WidgetNotExist, + "Failed to get widget by package name"); + } + return rowList.front().Get_app_id(); + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed in getHandle") + + ThrowMsg(WidgetDAOReadOnly::Exception::WidgetNotExist, + "Failed to get widget by package name"); +} + +PropertyDAOReadOnly::WidgetPropertyKeyList +WidgetDAOReadOnly::getPropertyKeyList() const +{ + return PropertyDAOReadOnly::GetPropertyKeyList(m_widgetHandle); +} + +PropertyDAOReadOnly::WidgetPreferenceList +WidgetDAOReadOnly::getPropertyList() const +{ + return PropertyDAOReadOnly::GetPropertyList(m_widgetHandle); +} + +PropertyDAOReadOnly::WidgetPropertyValue WidgetDAOReadOnly::getPropertyValue( + const PropertyDAOReadOnly::WidgetPropertyKey &key) const +{ + return PropertyDAOReadOnly::GetPropertyValue(m_widgetHandle, key); +} + +DPL::OptionalInt WidgetDAOReadOnly::checkPropertyReadFlag( + const PropertyDAOReadOnly::WidgetPropertyKey &key) const +{ + return PropertyDAOReadOnly::CheckPropertyReadFlag(m_widgetHandle, key); +} + +DPL::String WidgetDAOReadOnly::getPath() const +{ + DPL::String path = DPL::FromUTF8String( + GlobalConfig::GetUserInstalledWidgetPath()); + DPL::String srcPath = DPL::FromUTF8String(GlobalConfig::GetWidgetSrcPath()); + + bool isFactoryWidget = isFactory(); + + if (isFactoryWidget) { + WidgetGUID widgetGUID = getGUID(); + if (!widgetGUID) { + Throw(WidgetDAOReadOnly::Exception::GUIDisNull); + } + path += L"/" + *widgetGUID + L"/"; + } else { + // if the widget is a "downloaded widget", + // use unique package name. + DPL::OStringStream strAppId; + strAppId << m_widgetHandle; + DPL::OptionalString pkgname = getPkgname(); + path += L"/" + *pkgname + L"/"; + path += srcPath + L"/"; + } + + return path; +} + +DPL::String WidgetDAOReadOnly::getFullPath() const +{ + return L"file://" + getPath(); +} + +WidgetLocalizedInfo + WidgetDAOReadOnly::getLocalizedInfo(const DPL::String& languageTag) + const +{ + LogDebug("Getting Localized Info. Handle: " << m_widgetHandle); + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + CHECK_WIDGET_EXISTENCE(transaction, m_widgetHandle) + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, LocalizedWidgetInfo, &WrtDatabase::interface()) + select->Where( + And(Equals(m_widgetHandle), + Equals(languageTag))); + LocalizedWidgetInfo::Row info = select->GetSingleRow(); + WidgetLocalizedInfo result; + + result.name = info.Get_widget_name(); + result.shortName = info.Get_widget_shortname(); + result.description = info.Get_widget_description(); + result.license = info.Get_widget_license(); + result.licenseHref = info.Get_widget_license_href(); + + transaction.Commit(); + return result; + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get localized info") +} + +DbWidgetFeatureSet WidgetDAOReadOnly::getFeaturesList() const +{ + LogDebug("Getting FeaturesList. Handle: " << m_widgetHandle); + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + CHECK_WIDGET_EXISTENCE(transaction, m_widgetHandle) + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, DPL::DB::ORM::wrt::WidgetFeature, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + + DbWidgetFeatureSet resultSet; + typedef std::list RowList; + RowList list = select->GetRowList(); + + for (RowList::iterator i = list.begin(); i != list.end(); ++i) { + DbWidgetFeature feature; + feature.name = i->Get_name(); + feature.required = i->Get_required(); + feature.params = getFeatureParams(i->Get_widget_feature_id()); + FeatureDAOReadOnly featureDao(DPL::ToUTF8String(i->Get_name())); + feature.pluginId = featureDao.GetPluginHandle(); + resultSet.insert(feature); + } + transaction.Commit(); + return resultSet; + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get features list") +} + +WidgetParamMap WidgetDAOReadOnly::getFeatureParams(int id) +{ + WidgetFeatureId widgetFeatureId(id); + LogDebug("Getting feature Params. featureId: " << widgetFeatureId); + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, DPL::DB::ORM::wrt::FeatureParam, &WrtDatabase::interface()) + select->Where(Equals( + widgetFeatureId)); + + WidgetParamMap resultMap; + typedef std::list RowList; + RowList list = select->GetRowList(); + + FOREACH(i, list) + resultMap.insert(std::make_pair(i->Get_name(), i->Get_value())); + + return resultMap; + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get feature params") +} + +bool WidgetDAOReadOnly::hasFeature(const std::string& featureName) const +{ + LogDebug( + "Checking if widget has feature: " << featureName << ". Handle: " << + m_widgetHandle); + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + CHECK_WIDGET_EXISTENCE(transaction, m_widgetHandle) + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, wrt::WidgetFeature, &WrtDatabase::interface()) + select->Where(And(Equals(m_widgetHandle), + Equals( + DPL::FromUTF8String(featureName)))); + + wrt::WidgetFeature::Select::RowList rows = select->GetRowList(); + transaction.Commit(); + return !rows.empty(); + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to check for feature") +} + +HostList WidgetDAOReadOnly::getAccessHostList() const +{ + LogDebug("Getting AccessHostList. Handle: " << m_widgetHandle); + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + CHECK_WIDGET_EXISTENCE(transaction, m_widgetHandle) + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetAccessHost, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + std::list values = + select->GetValueList(); + HostList ret; + FOREACH(it, values) + ret.push_back(DPL::ToUTF8String(*it)); + + transaction.Commit(); + return ret; + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get access host list") +} + +bool WidgetDAOReadOnly::getAccessNetworkMode() const +{ + //TODO there is no column access_network + //it was removed in "Widget localization overhaul + return true; +} + +DbWidgetHandleList WidgetDAOReadOnly::getHandleList() +{ + LogDebug("Getting DbWidgetHandle List"); + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetInfo, &WrtDatabase::interface()) + return select->GetValueList(); + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get handle list") +} + +bool WidgetDAOReadOnly::isWidgetInstalled(DbWidgetHandle handle) +{ + LogDebug("Checking if widget exist. Handle: " << handle); + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetInfo, &WrtDatabase::interface()) + select->Where(Equals(handle)); + + WidgetInfo::Select::RowList rows = select->GetRowList(); + + return !rows.empty(); + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to check if widget exist") +} + +bool WidgetDAOReadOnly::isWidgetInstalled(DPL::String pkgName) +{ + LogDebug("Checking if widget exist. package name " << pkgName); + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetInfo, &WrtDatabase::interface()) + select->Where(Equals(pkgName)); + + WidgetInfo::Select::RowList rows = select->GetRowList(); + + return !rows.empty(); + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to check if widget exist") +} + +CertificateChainList WidgetDAOReadOnly::getWidgetCertificate() const +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetCertificate, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + + std::list chainList = select->GetRowList(); + + CertificateChainList result; + + FOREACH(iter, chainList) + result.push_back(DPL::ToUTF8String(iter->Get_encoded_chain())); + return result; +} + +DbWidgetSize WidgetDAOReadOnly::getPreferredSize() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + + DbWidgetSize size; + size.width = row.Get_widget_width(); + size.height = row.Get_widget_height(); + + LogDebug("Return size wxh = " << + (!!size.width ? *size.width : -1) << " x " << + (!!size.height ? *size.height : -1)); + + return size; +} + +WidgetType WidgetDAOReadOnly::getWidgetType() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + DPL::OptionalInt result = row.Get_widget_type(); + return WidgetType(static_cast(*result)); +} + +WidgetGUID WidgetDAOReadOnly::getGUID() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + return row.Get_widget_id(); +} + +DPL::OptionalString WidgetDAOReadOnly::getPkgname() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + return row.Get_pkgname(); +} + +DPL::OptionalString WidgetDAOReadOnly::getDefaultlocale() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + return row.Get_defaultlocale(); +} + +DPL::Optional WidgetDAOReadOnly::getVersion() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + return row.Get_widget_version(); +} + +DPL::Optional WidgetDAOReadOnly::getAuthorName() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + return row.Get_author_name(); +} + +DPL::Optional WidgetDAOReadOnly::getAuthorEmail() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + return row.Get_author_email(); +} + +DPL::Optional WidgetDAOReadOnly::getAuthorHref() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + return row.Get_author_href(); +} + +DPL::Optional WidgetDAOReadOnly::getMinimumWacVersion() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + return row.Get_min_version(); +} + +std::string WidgetDAOReadOnly::getShareHref() const +{ + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetExtendedInfo, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + WidgetExtendedInfo::Select::RowList rows = select->GetRowList(); + + if (rows.empty()) { + ThrowMsg(WidgetDAOReadOnly::Exception::WidgetNotExist, + "Cannot find widget. Handle: " << m_widgetHandle); + } + + DPL::Optional value = rows.front().Get_share_href(); + std::string ret = ""; + if (!value.IsNull()) { + ret = DPL::ToUTF8String(*value); + } + return ret; + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get share HREF") +} + +bool WidgetDAOReadOnly::getBackSupported() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + return row.Get_back_supported(); +} + +bool WidgetDAOReadOnly::isRecognized() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + DPL::OptionalInt result = row.Get_recognized(); + if (result.IsNull()) { + return false; + } + return static_cast(*result); +} + +bool WidgetDAOReadOnly::isWacSigned() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + DPL::OptionalInt result = row.Get_wac_signed(); + if (result.IsNull()) { + return false; + } + return static_cast(*result); +} + +bool WidgetDAOReadOnly::isDistributorSigned() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + DPL::OptionalInt result = row.Get_distributor_signed(); + if (result.IsNull()) { + return false; + } + return static_cast(*result); +} + +bool WidgetDAOReadOnly::isTrusted() const +{ + // SP-2100 + // widget with verified distributor signature is trusted + return isDistributorSigned(); +} + +bool WidgetDAOReadOnly::isTestWidget() const +{ + Try { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetExtendedInfo, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + + WidgetExtendedInfo::Select::RowList rows = select->GetRowList(); + if (rows.empty()) { + ThrowMsg(WidgetDAOReadOnly::Exception::WidgetNotExist, + "Cannot find widget. Handle: " << m_widgetHandle); + } + + return static_cast(rows.front().Get_test_widget()); + } + Catch(DPL::DB::SqlConnection::Exception::Base){ + ReThrowMsg(WidgetDAOReadOnly::Exception::DatabaseError, + "Failed to check IsTestWidget"); + } +} + +bool WidgetDAOReadOnly::getWebkitPluginsRequired() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + DPL::OptionalInt ret = row.Get_webkit_plugins_required(); + + if (ret.IsNull() || *ret == 0) { return false; } else { return true; } +} + +bool WidgetDAOReadOnly::isFactory() const +{ + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetExtendedInfo, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + + WidgetExtendedInfo::Select::RowList rows = select->GetRowList(); + if (rows.empty()) { + ThrowMsg(WidgetDAOReadOnly::Exception::WidgetNotExist, + "Cannot find widget. Handle: " << m_widgetHandle); + } + + DPL::OptionalInt ret = rows.front().Get_factory_widget(); + if (ret.IsNull()) { + return false; + } + return static_cast(*ret); + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get isFactory") +} + +time_t WidgetDAOReadOnly::getInstallTime() const +{ + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetExtendedInfo, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + + WidgetExtendedInfo::Select::RowList rows = select->GetRowList(); + if (rows.empty()) { + ThrowMsg(WidgetDAOReadOnly::Exception::WidgetNotExist, + "Cannot find widget. Handle: " << m_widgetHandle); + } + + return static_cast(*rows.front().Get_install_time()); + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get widdget install time") +} + +WidgetDAOReadOnly::WidgetLocalizedIconList WidgetDAOReadOnly::getLocalizedIconList() const +{ + //TODO check widget existance?? + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetLocalizedIcon, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + + std::list list = + select->GetRowList(); + WidgetLocalizedIconList ret; + FOREACH(it,list) + { + WidgetLocalizedIconRow icon = {it->Get_app_id(), + it->Get_icon_id(), + it->Get_widget_locale()}; + ret.push_back(icon); + } + return ret; + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get icon data") +} + +WidgetDAOReadOnly::WidgetIconList WidgetDAOReadOnly::getIconList() const +{ + //TODO check widget existance + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, wrt::WidgetIcon, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + + std::list list = + select->GetRowList(); + WidgetIconList ret; + FOREACH(it,list) + { + WidgetIconRow icon = {it->Get_icon_id(), + it->Get_app_id(), + it->Get_icon_src(), + it->Get_icon_width(), + it->Get_icon_height()}; + ret.push_back(icon); + } + return ret; + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get icon data") +} + +WidgetDAOReadOnly::LocalizedStartFileList WidgetDAOReadOnly::getLocalizedStartFileList() const +{ + //TODO check widget existance + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetLocalizedStartFile, &WrtDatabase::interface()) + select->Where(Equals( + m_widgetHandle)); + select->OrderBy("start_file_id ASC"); + + std::list list = + select->GetRowList(); + LocalizedStartFileList ret; + FOREACH(it,list) + { + WidgetLocalizedStartFileRow file = {it->Get_start_file_id(), + it->Get_app_id(), + it->Get_widget_locale(), + it->Get_type(), + it->Get_encoding()}; + ret.push_back(file); + } + return ret; + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get start file list data") +} + +WidgetDAOReadOnly::WidgetStartFileList WidgetDAOReadOnly::getStartFileList() const +{ + //TODO check widget existance + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetStartFile, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + select->OrderBy("start_file_id ASC"); + + std::list list = + select->GetRowList(); + WidgetStartFileList ret; + FOREACH(it,list) + { + WidgetStartFileRow file = {it->Get_start_file_id(), + it->Get_app_id(), + it->Get_src()}; + ret.push_back(file); + } + return ret; + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get start file list data") +} + +WindowModeList WidgetDAOReadOnly::getWindowModes() const +{ + //TODO check widget existance + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetWindowModes, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + + return select->GetValueList(); + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get window modes") +} + +std::string WidgetDAOReadOnly::getBaseFolder() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + DPL::Optional ret = row.Get_base_folder(); + std::string baseFolder; + if (!ret.IsNull()) { + baseFolder = DPL::ToUTF8String(*ret); + } + + if (!baseFolder.empty()) { + baseFolder += "/"; + } + + return baseFolder; +} + +bool WidgetDAOReadOnly::isDeletable() const +{ + return !WidgetDAOReadOnly::isFactory(); +} + +WidgetCertificateDataList WidgetDAOReadOnly::getCertificateDataList() const +{ + //TODO check widget existance + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetCertificateFingerprint, &WrtDatabase::interface()) + select->Where(Equals( + m_widgetHandle)); + select->OrderBy("chainid"); + WidgetCertificateFingerprint::Select::RowList rows = + select->GetRowList(); + + WidgetCertificateDataList outlCertificateData; + FOREACH(it, rows) + { + WidgetCertificateData data; + + data.owner = + static_cast (it->Get_owner()); + data.type = + static_cast (it->Get_type()); + data.chainId = it->Get_chainid(); + DPL::Optional md5 = it->Get_md5_fingerprint(); + data.strMD5Fingerprint = + md5.IsNull() ? "" : DPL::ToUTF8String(*md5); + DPL::Optional sha1 = it->Get_sha1_fingerprint(); + data.strSHA1Fingerprint = + sha1.IsNull() ? "" : DPL::ToUTF8String(*sha1); + DPL::Optional cname = it->Get_common_name(); + data.strCommonName = + cname.IsNull() ? DPL::FromUTF8String("") : *cname; + + outlCertificateData.push_back(data); + } + return outlCertificateData; + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get fingerprint list") +} + +FingerPrintList WidgetDAOReadOnly::getKeyFingerprints( + WidgetCertificateData::Owner owner, + WidgetCertificateData::Type type) const +{ + //TODO check widget existance + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetCertificateFingerprint, &WrtDatabase::interface()) + select->Where(And(And( + Equals(m_widgetHandle), + Equals(owner)), + Equals(type))); + + WidgetCertificateFingerprint::Select::RowList rows = + select->GetRowList(); + + FingerPrintList keys; + FOREACH(it, rows) + { + DPL::Optional md5 = it->Get_md5_fingerprint(); + keys.push_back(md5.IsNull() ? "" : DPL::ToUTF8String(*md5)); + DPL::Optional sha1 = it->Get_sha1_fingerprint(); + keys.push_back(sha1.IsNull() ? "" : DPL::ToUTF8String(*sha1)); + } + return keys; + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get fingerprint list") +} + +WidgetCertificateCNList WidgetDAOReadOnly::getKeyCommonNameList( + WidgetCertificateData::Owner owner, + WidgetCertificateData::Type type) const +{ + //TODO check widget existance + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetCertificateFingerprint, &WrtDatabase::interface()) + select->Where(And(And( + Equals(m_widgetHandle), + Equals(owner)), + Equals(type))); + + WidgetCertificateFingerprint::Select::RowList rows = + select->GetRowList(); + + WidgetCertificateCNList out; + FOREACH(it, rows) + { + DPL::Optional cname = it->Get_common_name(); + out.push_back(cname.IsNull() ? "" : DPL::ToUTF8String(*cname)); + } + return out; + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get key common name") +} + +ResourceAttributeList WidgetDAOReadOnly::getResourceAttribute( + const std::string &resourceId) const +{ + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, DPL::DB::ORM::wrt::WidgetFeature, &WrtDatabase::interface()) + select->Where(And(Equals(m_widgetHandle), + Equals( + DPL::FromUTF8String(resourceId)))); + + std::list list = select->GetRowList(); + ResourceAttributeList result; + if (!list.empty()) { + int widgetFeatureId = list.begin()->Get_widget_feature_id(); + WidgetParamMap map = getFeatureParams(widgetFeatureId); + + FOREACH(i, map) + result.push_back(DPL::ToUTF8String(i->first)); + } + return result; +} + +void WidgetDAOReadOnly::getWidgetAccessInfo( + WidgetAccessInfoList& outAccessInfoList) const +{ + //TODO check widget existance + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetWARPInfo, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + + WidgetWARPInfo::Select::RowList rows = select->GetRowList(); + + FOREACH(it, rows) + { + WidgetAccessInfo info; + + info.strIRI = it->Get_iri(); + DPL::OptionalInt access = it->Get_subdomain_access(); + if (access.IsNull() || 0 == *access) { + info.bSubDomains = false; + } else if (1 == *access) { + info.bSubDomains = true; + } + + outAccessInfoList.push_back(info); + } + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get accessinfo list") +} + +LanguageTagList WidgetDAOReadOnly::getLanguageTags() const +{ + //TODO check widget existance + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, LocalizedWidgetInfo, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + return select->GetValueList(); + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get language tags") +} + +LanguageTagList WidgetDAOReadOnly::getIconLanguageTags() const +{ + //TODO check widget existance + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, WidgetLocalizedIcon, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + select->Distinct(); + return select->GetValueList(); +} + +std::string WidgetDAOReadOnly::getCookieDatabasePath() const +{ + using namespace WrtDB::WidgetConfig; + std::ostringstream path; + + DPL::OptionalString pkgname = getPkgname(); + + path << GetWidgetPersistentStoragePath(*pkgname); + path << "/"; + path << GlobalConfig::GetCookieDatabaseFile(); + + return path.str(); +} + +std::string WidgetDAOReadOnly::getPrivateLocalStoragePath() const +{ + std::ostringstream path; + DPL::OptionalString pkgname = getPkgname(); + path << WidgetConfig::GetWidgetWebLocalStoragePath(*pkgname); + path << "/"; + + if (isFactory()) { + WidgetGUID widgetGUID = getGUID(); + if (!widgetGUID) { + Throw(WidgetDAOReadOnly::Exception::GUIDisNull); + } + path << DPL::ToUTF8String(*widgetGUID); + } + + return path.str(); +} + +ChildProtection::Record WidgetDAOReadOnly::getChildProtection() const +{ + WidgetInfoRow row = getWidgetInfoRow(m_widgetHandle); + return ChildProtection::Record(row.Get_child_protection()); +} + +Powder::Description WidgetDAOReadOnly::getPowderDescription() const +{ + //TODO check widget existance + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace Powder; + Description description; + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, PowderLevels, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + typedef std::list RowList; + RowList list = select->GetRowList(); + + FOREACH(it, list) + { + Description::CategoryEntry& categoryEntry = + description.categories[it->Get_category()]; + Description::LevelEntry levelEntry( + (Description::LevelEnum)it->Get_level()); + + WRT_DB_SELECT(selectContexts, PowderLevelContexts, &WrtDatabase::interface()) + selectContexts->Where(Equals( + it->Get_id())); + typedef std::list ContextsRowList; + ContextsRowList contextsList = selectContexts->GetRowList(); + + FOREACH(c, contextsList) + levelEntry.context.insert(c->Get_context()); + + categoryEntry.levels.push_back(levelEntry); + } + return description; + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get powder description") +} + +void WidgetDAOReadOnly::getWidgetSettings( + WidgetSettings& outWidgetSettings) const +{ + //TODO check widget existance + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, SettginsList, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + + SettginsList::Select::RowList rows = select->GetRowList(); + + FOREACH(it, rows) + { + WidgetSetting info; + + info.settingName = it->Get_settingName(); + info.settingValue = it->Get_settingValue(); + outWidgetSettings.push_back(info); + } + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get settings list") +} + +void WidgetDAOReadOnly::getAppServiceList( + WidgetApplicationServiceList& outAppServiceList) const +{ + LogDebug("Getting getAppServiceList. Handle: " << m_widgetHandle); + SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN + { + DPL::DB::ORM::wrt::ScopedTransaction transaction(&WrtDatabase::interface()); + CHECK_WIDGET_EXISTENCE(transaction, m_widgetHandle) + + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::wrt; + WRT_DB_SELECT(select, ApplicationServiceInfo, &WrtDatabase::interface()) + select->Where(Equals(m_widgetHandle)); + + ApplicationServiceInfo::Select::RowList rows = select->GetRowList(); + + if (rows.empty()) { + LogDebug("Application Service list is empty. Handle: " << + m_widgetHandle); + } + + FOREACH(it, rows) { + WidgetApplicationService ret; + ret.src = it->Get_src(); + ret.operation = it->Get_operation(); + ret.scheme = it->Get_scheme(); + ret.mime = it->Get_mime(); + outAppServiceList.push_back(ret); + } + + transaction.Commit(); + } + SQL_CONNECTION_EXCEPTION_HANDLER_END("Failed to get access host list") +} + +#undef SQL_CONNECTION_EXCEPTION_HANDLER_BEGIN +#undef SQL_CONNECTION_EXCEPTION_HANDLER_END +#undef CHECK_WIDGET_EXISTENCE + +} // namespace WrtDB diff --git a/modules/widget_dao/include/dpl/wrt-dao-ro/WrtDatabase.h b/modules/widget_dao/include/dpl/wrt-dao-ro/WrtDatabase.h new file mode 100644 index 0000000..86a0980 --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-ro/WrtDatabase.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef WRT_SRC_CONFIGURATION_WRTDATABASE_H_ +#define WRT_SRC_CONFIGURATION_WRTDATABASE_H_ + +#include + +namespace WrtDB { + +class WrtDatabase +{ + public: + static const char *Address(); + static DPL::DB::SqlConnection::Flag::Type Flags(); + static void attachToThread(); + static void detachFromThread(); + static DPL::DB::ThreadDatabaseSupport& interface(); + static bool CheckTableExist(const char *name); + + private: + static DPL::DB::ThreadDatabaseSupport m_interface; +}; + +} + +#endif /* WRTDATABASE_H */ + diff --git a/modules/widget_dao/include/dpl/wrt-dao-ro/common_dao_types.h b/modules/widget_dao/include/dpl/wrt-dao-ro/common_dao_types.h new file mode 100644 index 0000000..6913766 --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-ro/common_dao_types.h @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * @file common_dao_types.h + * @author Michal Ciepielski (m.ciepielski@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of common data types for wrtdb + */ + +#ifndef WRT_SRC_CONFIGURATION_COMMON_DAO_TYPES_H_ +#define WRT_SRC_CONFIGURATION_COMMON_DAO_TYPES_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace WrtDB { +namespace Powder { + +typedef std::set StringSet; +//! Widget description +struct Description +{ + //!Content level + typedef enum + { + Level0 = 0, + Level1, + Level2, + Level3, + Level4, + Level5, + LevelUnknown + } LevelEnum; + struct LevelEntry + { + LevelEnum level; //!< content level + + typedef StringSet Context; + + //! POWDER context + //! xa This material appears in an artistic context + //! xb This material appears in an educational context + //! xc This material appears in a medical context + //! xd This material appears in a sports context + //! xe This material appears in a violent context + Context context; + explicit LevelEntry(LevelEnum level = LevelUnknown); + //! Function checks if context is valid + //! \param[in] level POWDER content level + //! \param[in] context POWDER context + bool isContextValid(LevelEnum level, + const DPL::OptionalString& context) const; + }; + + struct CategoryEntry + { + //! Levels entries for POWDER description + typedef std::vector LevelsContainer; + LevelsContainer levels; + //! Function checks if context is valid + //! \param[out] reason set if context invalid + //! \param[in] level POWDER content level + //! \param[in] context POWDER context + bool isCategoryValid(LevelEntry& reason, + LevelEnum level, + const DPL::OptionalString& context) const; + }; + + //! POWDER Category -> Category entry map for Widget + //! + //! nu Nudity + //! se Sex + //! vi Violence + //! la Potentially offensive language + //! dr Drug use + //! ga Gambling + //! ha Hate or harmful activities + //! ug Use of user-generated content + typedef std::map CategoryEntries; + + CategoryEntries categories; + + //! Age rating for widget + //! If Null not set + DPL::OptionalInt ageRating; +}; +} // namespace Powder + +namespace ChildProtection { + +//! Blacklist with forbidden URLs +//! It should be stored in WidgetDAO +typedef std::vector BlackList; + +//! Widget Child protection record +//! Record should be stored in WingetDAO +struct Record +{ + //! Child protection enabled + bool enabled; + explicit Record(bool enabled) : + enabled(enabled) + { + } +}; + +//! Powder processing +struct PowderRules +{ + //! Rule set by parent about forbidden category + //! Powder category + //! nu Nudity + //! se Sex + //! vi Violence + //! la Potentially offensive language + //! dr Drug use + //! ga Gambling + //! ha Hate or harmful activities + //! ug Use of user-generated content + //! Powder context + //! xa This material appears in an artistic conteaxt + //! xb This material appears in an educational context + //! xc This material appears in a medical context + //! xd This material appears in a sports context + //! xe This material appears in a violent context + struct CategoryRule + { + DPL::String category; + Powder::Description::LevelEnum level; + DPL::OptionalString context; + explicit CategoryRule(const DPL::String& category = DPL::String(), + Powder::Description::LevelEnum level = + Powder::Description::LevelUnknown, + const DPL::OptionalString& context = DPL::OptionalString()); + }; + + struct PowderResult + { + //! Reasoning outcome: part of POWDER description used to invalidate + Powder::Description::LevelEntry invalidDescription; + //! Reasoning outcome: rule set by parent not full filed by description + CategoryRule invalidRule; + + //! Reasoning outcome: type of invalidity + enum InvalidReason + { + InvalidRule, //!< One of rules was not fulfilled + InvalidAge, //!< Age is invalid + AgeRatingNotSet, //!< Age rating for widget is not set + Valid //!< Description valid + }; + InvalidReason reason; + explicit PowderResult(InvalidReason reason = Valid, + const Powder::Description::LevelEntry& invalidDescription = + Powder::Description::LevelEntry(), + const CategoryRule& invalidRule = CategoryRule()); + }; + + typedef std::pair ResultPair; + + //! Function checks if rule is fulfilled by description + //! \param[in] rule checked rule + //! \param[in] description + //! \retval true rule is valid + //! \retval false rule is invalid + ResultPair isRuleValidForDescription(const CategoryRule& rule, + const Powder::Description& description) const; + //! Function checks if age limit is fulfilled by description + //! \param[in] description + //! \retval true age is valid + //! \retval false age is invalid + ResultPair isAgeValidForDescription( + const Powder::Description& description) const; + + //! It is the maximum age rating valid for child + //! Uniform age is stored in WidgetDAO + DPL::OptionalInt ageLimit; + + //! Set to true if age rating is required + //! If ageLimit is not set value is ignored + bool isAgeRatingRequired; + + //! Set of rules configured by parent + //! Rules are stored in WidgetDAO and are uniform for all widgets + typedef std::vector RulesContainer; + RulesContainer rules; + + //! Function check if Widget description is valid for ChildProtection + //! configuration + //! \param description widget description + //! \retval true widget is valid + //! \retval false widget is invalid + ResultPair isDescriptionValid(const Powder::Description& description) + const; + + PowderRules() : + isAgeRatingRequired(false) + { + } +}; +} // namespace ChildProtection + +class PluginMetafileData +{ + public: + struct Feature + { + std::string m_name; + std::set m_deviceCapabilities; + + bool operator< (const Feature& obj) const + { + return m_name < obj.m_name; + } + }; + typedef std::set FeatureContainer; + + public: + + PluginMetafileData() + { + } + + std::string m_libraryName; + std::string m_featuresInstallURI; + std::string m_featuresKeyCN; + std::string m_featuresRootCN; + std::string m_featuresRootFingerprint; + + FeatureContainer m_featureContainer; +}; + +class PluginObjectsDAO +{ + public: + typedef std::set Objects; + typedef DPL::SharedPtr ObjectsPtr; + + public: + explicit PluginObjectsDAO() {} + + protected: + ObjectsPtr m_implemented; + ObjectsPtr m_dependent; +}; + +/** + * @brief Widget id describes web-runtime global widget identifier. + * + * Notice that only up to one widget can exist at the same time. + * DbWidgetHandle can be translated into corresponding WidgetModel by invoking + * FindWidgetModel routine. + */ +typedef int DbWidgetHandle; + +/** + * @brief Structure to hold the information of widget's size + */ +struct DbWidgetSize +{ + DPL::OptionalInt width; + DPL::OptionalInt height; + + DbWidgetSize(DPL::OptionalInt w = DPL::OptionalInt::Null, + DPL::OptionalInt h = DPL::OptionalInt::Null) : + width(w), + height(h) + { + } +}; + +inline bool operator ==(const DbWidgetSize &objA, const DbWidgetSize &objB) +{ + if (!objA.height || !objA.width || !objB.width || !objB.height) { + return false; + } else { + return *objA.height == *objB.height && *objA.width == *objB.width; + } +} + +/** + * Widget [G]lobal [U]nique [ID]entifier + * Orginated from appstore ID + */ +typedef DPL::OptionalString WidgetGUID; + +struct WidgetAccessInfo +{ + DPL::String strIRI; /* origin iri */ + bool bSubDomains; /* do we want access to subdomains ? */ + + bool operator ==(const WidgetAccessInfo& info) const + { + return info.strIRI == strIRI && + info.bSubDomains == bSubDomains; + } +}; + +typedef std::list WidgetAccessInfoList; + +typedef std::list WindowModeList; + +/** + * @brief Widget configuration parameter key + */ +typedef DPL::String WidgetParamKey; + +/** + * @brief Widget configuration parameter value + */ +typedef DPL::String WidgetParamValue; + +/** + * @brief A map of widget configuration parameters. + * + * Widget configuration parameters are read from database and are stored + * along with feature that they describe. + */ +typedef std::multimap WidgetParamMap; + +/** + * @brief Widget feature host information about possible javascript extensions + * that widget may use + * + * Widget features are declared in configuration file in widget installation + * package. Each declared special feature is contained in some wrt-plugin that + * declares to implement it. After widget launch wrt searches for proper plugin + * libraries and load needed features. + * + * Widget features can be required or optional. It is possible to start widget + * without missing feature. When required feature cannot be loaded widget will + * not start. + */ + +enum { + INVALID_PLUGIN_HANDLE = -1 +}; +typedef int DbPluginHandle; + +struct DbWidgetFeature +{ + DPL::String name; /// Feature name + bool required; /// Whether feature is required + DbPluginHandle pluginId; /// Plugin id that implement this feature + WidgetParamMap params; /// Widget's params + + DbWidgetFeature() : + required(false), + pluginId(INVALID_PLUGIN_HANDLE) + { + } +}; + +inline bool operator < (const DbWidgetFeature &objA, + const DbWidgetFeature &objB) +{ + return objA.name.compare(objB.name) < 0; +} + +inline bool operator==(const DbWidgetFeature &featureA, + const DbWidgetFeature &featureB) +{ + return featureA.name == featureB.name && + featureA.required == featureB.required && + featureA.pluginId == featureB.pluginId; +} + +/** + * @brief Default container for features list + */ +typedef std::multiset DbWidgetFeatureSet; + +/** + * @brief Default container with DbWidgetHandle's + */ +typedef std::list DbWidgetHandleList; + +/** + * @brief Widget specific type + * + * Widget type describes belowed in WAC, TIZEN WebApp + */ +enum AppType +{ + APP_TYPE_UNKNOWN = 0, // unknown + APP_TYPE_WAC10, // WAC 1.0 + APP_TYPE_WAC20, // WAC 2.0 + APP_TYPE_TIZENWEBAPP, // Tizen webapp +}; + +class WidgetType +{ + public: + WidgetType() + :appType(APP_TYPE_UNKNOWN) + { + } + WidgetType(const AppType type) + :appType(type) + { + } + bool operator== (const AppType& other) const + { + return appType == other; + } + std::string getApptypeToString() + { + switch (appType) { +#define X(x) case x: return #x; + X(APP_TYPE_UNKNOWN) + X(APP_TYPE_WAC10) + X(APP_TYPE_WAC20) + X(APP_TYPE_TIZENWEBAPP) +#undef X + default: + return "UNKNOWN"; + } + } + + AppType appType; +}; + +} // namespace WrtDB + +struct WidgetSetting +{ + DPL::String settingName; + DPL::String settingValue; + + bool operator ==(const WidgetSetting& info) const + { + return (info.settingName == settingName && + info.settingValue == settingValue); + } + bool operator !=(const WidgetSetting& info) const + { + return (info.settingName != settingName || + info.settingValue != settingValue); + } +}; + +typedef std::list WidgetSettings; + +/** + * @brief Widget Application Service + * + * Application sercvice describes details of behaviour + * when widget receives aul bundle data. + */ +struct WidgetApplicationService +{ + public: + DPL::String src; /* start uri */ + DPL::String operation; /* service name */ + DPL::String scheme; /* scheme type*/ + DPL::String mime; /* mime type */ + + bool operator== (const WidgetApplicationService& other) const + { + return src == other.src && + operation == other.operation && + scheme == other.scheme && + mime == other.mime; + } +}; + +typedef std::list WidgetApplicationServiceList; +#endif /* WRT_SRC_CONFIGURATION_COMMON_DAO_TYPES_H_ */ diff --git a/modules/widget_dao/include/dpl/wrt-dao-ro/config_parser_data.h b/modules/widget_dao/include/dpl/wrt-dao-ro/config_parser_data.h new file mode 100644 index 0000000..a2d92ef --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-ro/config_parser_data.h @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file config_parser_data.h + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 0.1 + * @brief + */ +#ifndef CONFIG_PARSER_DATA_H_ +#define CONFIG_PARSER_DATA_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace WrtDB { + +void NormalizeString(DPL::OptionalString& txt); +void NormalizeString(DPL::String& str); +DPL::String GetSingleAttributeValue(const DPL::String value); + +class WidgetConfigurationManager; + +class ConfigParserData +{ + public: + struct Param + { + Param(const DPL::String& name) : name(name) + { + } + DPL::String name; + DPL::String value; + bool operator==(const Param&) const; + bool operator!=(const Param&) const; + bool operator >(const Param&) const; + bool operator>=(const Param&) const; + bool operator <(const Param&) const; + bool operator<=(const Param&) const; + }; + typedef std::set ParamsList; + + struct Feature + { + Feature(const DPL::String& name, + bool required = true) : name(name), + required(required) + { + } + DPL::String name; + bool required; + ParamsList paramsList; + + bool operator==(const Feature&) const; + bool operator!=(const Feature&) const; + bool operator >(const Feature&) const; + bool operator>=(const Feature&) const; + bool operator <(const Feature&) const; + bool operator<=(const Feature&) const; + }; + typedef std::set FeaturesList; + + struct Icon + { + Icon(const DPL::String& src) : src(src) + { + } + DPL::String src; + DPL::OptionalInt width; + DPL::OptionalInt height; + bool operator==(const Icon&) const; + bool operator!=(const Icon&) const; + bool operator >(const Icon&) const; + bool operator>=(const Icon&) const; + bool operator <(const Icon&) const; + bool operator<=(const Icon&) const; + }; + typedef std::list IconsList; + + struct LocalizedData + { + DPL::OptionalString name; + DPL::OptionalString shortName; + + DPL::OptionalString description; + + DPL::OptionalString license; + DPL::OptionalString licenseFile; + DPL::OptionalString licenseHref; + }; + typedef std::map LocalizedDataSet; + + struct Preference + { + Preference(const DPL::String& name, + bool readonly = false) : + name(name), + value(), + readonly(readonly) + { + } + DPL::String name; + DPL::OptionalString value; + bool readonly; + bool operator==(const Preference&) const; + bool operator!=(const Preference&) const; + bool operator >(const Preference&) const; + bool operator>=(const Preference&) const; + bool operator <(const Preference&) const; + bool operator<=(const Preference&) const; + }; + typedef std::set PreferencesList; + typedef std::set StringsList; + + struct AccessInfo + { + AccessInfo(const DPL::String& strIRI, + bool bSubdomainAccess) : m_strIRI(strIRI), + m_bSubDomainAccess(bSubdomainAccess) + { + } + + bool operator==(const AccessInfo&) const; + bool operator!=(const AccessInfo&) const; + bool operator <(const AccessInfo&) const; + + DPL::String m_strIRI; + bool m_bSubDomainAccess; + }; + typedef std::set AccessInfoSet; + + struct Setting + { + Setting(const DPL::String& name, + const DPL::String& value) : + m_name(name), + m_value(value) + { + } + DPL::String m_name; + DPL::String m_value; + + bool operator==(const Setting&) const; + bool operator!=(const Setting&) const; + bool operator >(const Setting&) const; + bool operator>=(const Setting&) const; + bool operator <(const Setting&) const; + bool operator<=(const Setting&) const; + }; + + typedef std::set SettingsList; + + struct ServiceInfo + { + ServiceInfo( + const DPL::String& src, + const DPL::String& operation, + const DPL::String& scheme, + const DPL::String& mime) : + m_src(src), + m_operation(operation), + m_scheme(scheme), + m_mime(mime) + { + } + DPL::String m_src; + DPL::String m_operation; + DPL::String m_scheme; + DPL::String m_mime; + + bool operator==(const ServiceInfo&) const; + bool operator!=(const ServiceInfo&) const; + }; + typedef std::list ServiceInfoList; + + StringsList nameSpaces; + + LocalizedDataSet localizedDataSet; + + DPL::OptionalString authorName; + DPL::OptionalString authorHref; + DPL::OptionalString authorEmail; + + FeaturesList featuresList; + + SettingsList settingsList; + + DPL::OptionalInt width; + DPL::OptionalInt height; + + DPL::OptionalString widget_id; + DPL::OptionalString defaultlocale; + + PreferencesList preferencesList; + + DPL::OptionalString version; + StringsList windowModes; + + AccessInfoSet accessInfoSet; + + bool flashNeeded; + + DPL::OptionalFloat minVersionRequired; + DPL::OptionalInt minVersionRequiredFound; + StringsList powderDescriptionLinks; + + bool backSupported; + bool accessNetwork; + + // Unlocalized data, to be processed by WidgetConfigurationManager + bool startFileEncountered; + DPL::OptionalString startFile; + DPL::OptionalString startFileEncoding; + DPL::OptionalString startFileContentType; + IconsList iconsList; + + // pakcage name determined by operator for TIZEN webapp + DPL::OptionalString pkgname; + //Application service model list + ServiceInfoList appServiceList; + + ConfigParserData() : + flashNeeded(false), + minVersionRequired(1.0), + minVersionRequiredFound(), + backSupported(false), + accessNetwork(false), + startFileEncountered(false) + { + } +}; + +} // namespace WrtDB + +#endif //CONFIG_PARSER_DATA_H_ diff --git a/modules/widget_dao/include/dpl/wrt-dao-ro/feature_dao_read_only.h b/modules/widget_dao/include/dpl/wrt-dao-ro/feature_dao_read_only.h new file mode 100644 index 0000000..9bd8945 --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-ro/feature_dao_read_only.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file feature_dao_read_only.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of feature dao read only + */ + +#ifndef WRT_SRC_CONFIGURATION_FEATURE_DAO_READ_ONLY_H_ +#define WRT_SRC_CONFIGURATION_FEATURE_DAO_READ_ONLY_H_ + +#include +#include +#include +#include +#include "feature_model.h" +#include +#include + +namespace WrtDB { + +class FeatureDAOReadOnly +{ + public: + /** + * Exception classes + */ + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, DatabaseError) + DECLARE_EXCEPTION_TYPE(Base, FeatureNotExist) + }; + + typedef std::set DeviceCapabilitiesList; + + static bool isDeviceCapabilityInstalled(const std::string &deviceCapName); + + FeatureDAOReadOnly(FeatureHandle); + FeatureDAOReadOnly(const std::string &featureName); + + static FeatureHandleListPtr GetFeatureHandleListForPlugin( + DbPluginHandle pluginHandle); + + static bool isFeatureInstalled(const std::string &featureName); + static bool isFeatureInstalled(FeatureHandle handle); + static FeatureHandleList GetHandleList(); + + std::string GetName() const; + std::string GetInstallURI() const; + std::string GetKeyCn() const; + std::string GetRootKey() const; + std::string GetRootKeyFingerprint() const; + FeatureHandle GetFeatureHandle() const; + std::string GetLibraryPath() const; + std::string GetLibraryName() const; + DeviceCapabilitiesList GetDeviceCapabilities() const; + DbPluginHandle GetPluginHandle() const; + + protected: + FeatureHandle m_featureHandle; +}; + +} // namespace WrtDB + +#endif /* WRT_SRC_CONFIGURATION_FEATURE_DAO_READ_ONLY_H_ */ diff --git a/modules/widget_dao/include/dpl/wrt-dao-ro/feature_model.h b/modules/widget_dao/include/dpl/wrt-dao-ro/feature_model.h new file mode 100644 index 0000000..13b959c --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-ro/feature_model.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file feature_model.h + * @author Pawel Sikorski (p.sikorski@samgsung.com) + * @version + * @brief This file contains FeatureModel, FeatureHandle definitions. + */ +#ifndef FEATURE_MODEL_H +#define FEATURE_MODEL_H + +#include +#include +#include +#include +#include +#include +#include + +namespace WrtDB { + +typedef int FeatureHandle; +typedef std::list FeatureHandleList; +typedef DPL::SharedPtr FeatureHandleListPtr; + +typedef int FeatureSetHandle; +typedef std::list FeatureSetHandleList; + +class FeatureModel : public DPL::Event::Model +{ + public: + DPL::Event::Property FHandle; + DPL::Event::Property Name; + + DPL::Event::Property > DeviceCapabilities; + DPL::Event::Property PHandle; + + FeatureModel(FeatureHandle handle) : + FHandle(this, handle), + Name(this), + DeviceCapabilities(this), + PHandle(this, -1) + { + } +}; + +typedef DPL::SharedPtr FeatureModelPtr; + +} // namespace WrtDB + +#endif // FEATURE_MODEL_H diff --git a/modules/widget_dao/include/dpl/wrt-dao-ro/global_config.h b/modules/widget_dao/include/dpl/wrt-dao-ro/global_config.h new file mode 100644 index 0000000..bda207b --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-ro/global_config.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file global_config.h + * @author Yang Jie (jie2.yang@samsung.com) + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file contains global WRT config + */ +#ifndef GLOBAL_CONFIG_H +#define GLOBAL_CONFIG_H + +#include +#include + +namespace WrtDB { +namespace GlobalConfig { +/** + * WRT database path + */ +inline const char* GetWrtDatabaseFilePath() +{ + return "/opt/dbspace/.wrt.db"; +} + +/** + * WRT origin widget interface database path + */ +inline const char* GetWrtWidgetInterfaceDatabaseFilePath() +{ + return "/usr/share/wrt-plugins-common/widget_interface_db.sql"; +} + +/** + * WRT device plugin path + */ +inline const char* GetDevicePluginPath() +{ + return "/usr/lib/wrt-plugins"; +} + +/** + * WRT factory widgets that are loaded by default + */ +inline const char* GetFactoryInstalledWidgetPath() +{ + return "/opt/apps/widget/system"; +} + +/** + * WRT widgets that are downloaded and installed by user + */ +inline const char* GetUserInstalledWidgetPath() +{ + return "/opt/apps"; +} + +/** + * WRT widgets that are downloaded and installed by user + */ +inline const char* GetWidgetSrcPath() +{ + return "res/src"; +} + +/** + * Directory for WebKit local storage files + */ +inline const char* GetPublicVirtualRootPath() +{ + return "/opt/apps/widget/data/Public"; +} + +/** + * Directory for WebKit local storage files + */ +inline const char* GetWidgetLocalStoragePath() +{ + return "data/localStorage"; +} + +/** + * Directory for tests data (such as test widgets wgt) + */ +inline const char* GetTestsDataPath() +{ + return "/opt/apps/widget/tests"; +} + +/** + * widgets exec path + */ +inline const char* GetUserWidgetExecPath() +{ + return "bin"; +} + +/** + * widgets private data path + */ +inline const char* GetWidgetPrivateStoragePath() +{ + return "data"; +} + + +/** + * widgets desktop files path + */ +inline const char* GetUserWidgetDesktopPath() +{ + return "/opt/share/applications"; +} + +/** + * wrt-client exec path + */ +inline const char* GetWrtClientExec() +{ + return "/usr/bin/wrt-client"; +} + +/** + * widgets desktop icon path + */ +inline const char* GetUserWidgetDesktopIconPath() +{ + return "res/icons/default/small"; +} + +/** + * widgets default icon file + */ +inline const char* GetUserWidgetDefaultIconFile() +{ + return "/usr/share/wrt-engine/wrt_widget_default_icon.png"; +} + +/** + * WRT downloaded widgets + */ +// KW inline const char* GetDownloadedWidgetPath() +// KW { +// KW return "/opt/apps/widget/test-widgets"; +// KW } + +inline const char* GetSignatureXmlSchema() +{ + //TODO please rename, this filename is not descriptive enough + return "/usr/share/wrt-engine/schema.xsd"; +} + +inline const char* GetWAC20TestRootCAFilePath() +{ + return "/usr/share/wrt-engine/WAC2.0TestRootCA.cert"; +} + +/** + * Name of the w3c geolocation feature + */ +inline const char* GetW3CGeolocationFeatureName() +{ + return "http://www.w3.org/TR/geolocation-API/"; +} + +/** + * Prefix of package name for widgets + */ +inline const char* GetPkgnamePrefix() +{ + return "org.tizen."; +} + +/** + * Plugin Configuration Metafile name + */ +inline const char* GetPluginMetafileName() +{ + return "config.xml"; +} + +/** + * WRT device plugins installation required + * File which indicate that new plugins + * are available and should be installed + */ +inline const char* GetPluginInstallInitializerName() +{ + return "/opt/apps/widget/plugin-installation-required"; +} + +/** + * File with certificate fingerprints list. + */ + +inline const char* GetFingerprintListFile() +{ + return "/usr/share/wrt-engine/fingerprint_list.xml"; +} + +inline const char* GetFingerprintListSchema() +{ + return "/usr/share/wrt-engine/fingerprint_list.xsd"; +} + +inline const char* GetVCoreDatabaseFilePath() +{ + return "/opt/dbspace/.vcore.db"; +} +bool IsOCSPEnabled(); +bool IsCRLEnabled(); + +/** + * widgets cookie database file name + */ +inline const char* GetCookieDatabaseFile() +{ + return ".cookie.db"; +} + +/** + * widget interface database file name + */ +inline const char* GetWidgetInterfaceDatabaseFile() +{ + return ".widget_interface.db"; +} + +inline const char* GetTmpDirPath() +{ + return "/tmp"; +} +} // namespace GlobalConfig +} // namespace WrtDB + +#endif // GLOBAL_CONFIG_H diff --git a/modules/widget_dao/include/dpl/wrt-dao-ro/global_dao_read_only.h b/modules/widget_dao/include/dpl/wrt-dao-ro/global_dao_read_only.h new file mode 100644 index 0000000..2feafca --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-ro/global_dao_read_only.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file global_dao_read_only.h + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of global dao + */ + +#ifndef WRT_SRC_CONFIGURATION_GLOBAL_DAO_READ_ONLY_H_ +#define WRT_SRC_CONFIGURATION_GLOBAL_DAO_READ_ONLY_H_ + +#include +#include +#include + +#include +#include + +#include + +namespace WrtDB { + +typedef std::list WidgetPackageList; +typedef std::set DeviceCapabilitySet; + +//#ifdef USE_BROWSER_SETTING +//typedef DPL::DB::ORM::wrt::UserAgents::key_value::ColumnType UserAgent; +//#endif //USE_BROWSER_SETTING + +class GlobalDAOReadOnly +{ + public: + /** + * GlobalDAOReadOnly Exception classes + */ + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, DatabaseError) + }; + + public: + /** + * Retrieve Parental mode status + * + * @return true for Parental Mode on, false for Parental Mode off + */ + static bool GetParentalMode(); + + /** + * Retrieve Parental mode maximal allowed age + * + * @return NULL if allowed age not set, else pointer value is allowed age + */ + static DPL::OptionalInt GetParentalAllowedAge(); + /** + * Retrieve Parental mode maximal allowed age + * + * @return NULL if allowed age not set, else pointer value is allowed age + */ + + static bool IsValidSubTag(const DPL::String& tag, int type); + + static bool IsPowderRulePresent( + const ChildProtection::PowderRules::CategoryRule& rule); + + static ChildProtection::PowderRules GetPowderRules(); + + static ChildProtection::BlackList GetAdultBlackList(); + + static bool IsElementOnAdultBlackList(const DPL::String &url); + + /** + * Retrieve list of deffered widget packages to be installed + * + * @return Widget package list + */ + static WidgetPackageList GetDefferedWidgetPackageInstallationList(); + + static bool GetDeveloperMode(); + + static bool GetSecureByDefault(); + + static bool getComplianceMode(); + + static std::string getComplianceFakeImei(); + + static std::string getComplianceFakeMeid(); + + static WidgetAccessInfoList GetWhiteURIList(); + + enum NetworkAccessMode + { + NEVER_CONNECT, + ALWAYS_ASK, + CONNECT_AUTOMATICALLY + }; + + /** + * This method returns network access mode for home network. + * + * @return Access mode for home network. + */ + static NetworkAccessMode GetHomeNetworkDataUsage(); + + /** + * This method returns network access mode while roaming is enabled. + * + * @return Access mode for home network. + */ + static NetworkAccessMode GetRoamingDataUsage(); + + static DPL::String GetUserAgentValue(const DPL::String &key); + + /** + * This method returns set of device capabilities used by apifeature. + */ + static DeviceCapabilitySet GetDeviceCapability( + const DPL::String &apifeature); + + + /** + * This method gets Autofill for Webkit + */ + struct AutoSaveData + { + DPL::String userId; + DPL::String passwd; + }; + + static DPL::Optional GetAutoSaveIdPasswd( + const DPL::String &url); + + protected: + GlobalDAOReadOnly() + { + } +}; + +} // namespace WrtDB + +#endif // WRT_SRC_CONFIGURATION_GLOBAL_DAO_READ_ONLY_H_ diff --git a/modules/widget_dao/include/dpl/wrt-dao-ro/path_builder.h b/modules/widget_dao/include/dpl/wrt-dao-ro/path_builder.h new file mode 100644 index 0000000..18fcca3 --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-ro/path_builder.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file PathBuilder.h + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief Header file for PathBuilder class. + */ +#ifndef WRT_UTILS_PATHBUILDER_H +#define WRT_UTILS_PATHBUILDER_H + +#include +#include + +namespace WrtDB { + +class PathBuilderImpl; + +class PathBuilder : private DPL::Noncopyable +{ + public: + PathBuilder(); + explicit PathBuilder(const std::string& path); + + ~PathBuilder(); + + PathBuilder& Append(const std::string& path); + + PathBuilder& Concat(const std::string& arg); + PathBuilder& Concat(int arg); + + PathBuilder& Reset(); + + bool Empty() const; + + std::string GetFullPath() const; + + private: + PathBuilderImpl* m_impl; +}; + +} // namespace WrtDB + +#endif diff --git a/modules/widget_dao/include/dpl/wrt-dao-ro/plugin_dao_read_only.h b/modules/widget_dao/include/dpl/wrt-dao-ro/plugin_dao_read_only.h new file mode 100644 index 0000000..2341316 --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-ro/plugin_dao_read_only.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file plugin_dao_read_only.h + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of plugin dao read only + */ + + +#ifndef WRT_SRC_CONFIGURATION_PLUGIN_DAO_READ_ONLY_H_ +#define WRT_SRC_CONFIGURATION_PLUGIN_DAO_READ_ONLY_H_ + +#include +#include +#include +#include +#include + +namespace WrtDB { + +typedef std::list PluginHandleList; +typedef std::set PluginHandleSet; +typedef std::list ImplementedObjectsList; +typedef DPL::SharedPtr PluginHandleSetPtr; + +//TODO make it friend to FeatureDAO or inherit +class PluginDAOReadOnly +{ + public: + enum PluginInstallationState + { + INSTALLATION_DEFAULT, + //when plugin data are up to date and plugin model may be created + INSTALLATION_COMPLETED, + //installation is in progress, some data may not be valid + INSTALLATION_IN_PROGRESS, + //installation not completed due to missing dependency + INSTALLATION_WAITING, + + UNKNOWN_ERROR + }; + + static int ToInt(PluginInstallationState state) + { + return static_cast(state); + } + + static PluginInstallationState ToState(int state) + { + return static_cast(state); + } + + /** + * PluginDAO Exception classes + */ + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, DatabaseError) + DECLARE_EXCEPTION_TYPE(Base, PluginNotExist) + DECLARE_EXCEPTION_TYPE(Base, PluginInstallationNotCompleted) + }; + + public: + PluginDAOReadOnly(DbPluginHandle pluginHandle); + PluginDAOReadOnly(const std::string &libraryName); + + static PluginHandleList getPluginHandleList(); + + static bool isPluginInstalled(const std::string &libraryName); + static bool isPluginInstalled(DbPluginHandle pluginHandle); + + static PluginHandleSetPtr getPluginHandleByStatus( + PluginInstallationState state); + + static DbPluginHandle getPluginHandleForImplementedObject( + const std::string& objectName); + + static ImplementedObjectsList getImplementedObjectsForPluginHandle( + DbPluginHandle handle); + + static PluginObjectsDAO::ObjectsPtr getRequiredObjectsForPluginHandle( + DbPluginHandle handle); + + static PluginInstallationState getInstallationStateForHandle( + DbPluginHandle handle); + + DbPluginHandle getPluginHandle() const; + PluginInstallationState getInstallationStatus() const; + std::string getLibraryPath() const; + std::string getLibraryName() const; + std::string getInstallURI() const; + std::string getKeyCn() const; + std::string getRootKey() const; + std::string getRootKeyFingerprint() const; + PluginHandleSetPtr getLibraryDependencies() const; + + private: + DbPluginHandle m_pluginHandle; + + void checkInstallationCompleted(); +}; + +} // namespace WrtDB + +#endif /* WRT_SRC_CONFIGURATION_PLUGIN_DAO_READ_ONLY_H_ */ diff --git a/modules/widget_dao/include/dpl/wrt-dao-ro/property_dao_read_only.h b/modules/widget_dao/include/dpl/wrt-dao-ro/property_dao_read_only.h new file mode 100644 index 0000000..50b3dab --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-ro/property_dao_read_only.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * property_dao_read_only.h + * + * Created on: Nov 16, 2011 + * Author: Krzysztof Jackiewicz(k.jackiewicz@samsung.com) + */ + +#ifndef PROPERTY_DAO_READ_ONLY_H_ +#define PROPERTY_DAO_READ_ONLY_H_ + +#include +#include +#include +#include +#include + +namespace WrtDB { +namespace PropertyDAOReadOnly { + +typedef DPL::String WidgetPropertyKey; +typedef DPL::OptionalString WidgetPropertyValue; + +typedef std::list WidgetPropertyKeyList; + +struct WidgetPreferenceRow { + int app_id; + WidgetPropertyKey key_name; + WidgetPropertyValue key_value; + DPL::OptionalInt readonly; +}; + +typedef std::list WidgetPreferenceList; + +/** + * PropertyDAO Exception classes + */ +class Exception +{ + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, DatabaseError) + DECLARE_EXCEPTION_TYPE(Base, ReadOnlyProperty) +}; + +/* This method checks read only flag for given property + */ +DPL::OptionalInt CheckPropertyReadFlag(DbWidgetHandle widgetHandle, + const WidgetPropertyKey &key); + +/* This method gets widget property key list + */ +WidgetPropertyKeyList GetPropertyKeyList(DbWidgetHandle widgetHandle); + +/* This method gets widget property list + */ +WidgetPreferenceList GetPropertyList(DbWidgetHandle widgetHandle); + +/* This method get widget property value + */ +WidgetPropertyValue GetPropertyValue(DbWidgetHandle widgetHandle, + const WidgetPropertyKey &key); + +} // PropertyDAOReadOnly +} // namespace WrtDB + +#endif /* PROPERTY_DAO_READ_ONLY_H_ */ diff --git a/modules/widget_dao/include/dpl/wrt-dao-ro/webruntime_database.h b/modules/widget_dao/include/dpl/wrt-dao-ro/webruntime_database.h new file mode 100644 index 0000000..96afd44 --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-ro/webruntime_database.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file webruntime_database.h + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of webruntime database + */ +#ifndef WRT_ENGINE_SRC_CONFIGURATION_WEBRUNTIME_DATABASE_H +#define WRT_ENGINE_SRC_CONFIGURATION_WEBRUNTIME_DATABASE_H + +#include +#include + +extern DPL::Mutex g_wrtDbQueriesMutex; + +#define WRT_DB_INTERNAL(tlsCommand, InternalType, interface) \ + static DPL::ThreadLocalVariable *tlsCommand ## Ptr = NULL; \ + { \ + DPL::Mutex::ScopedLock lock(&g_wrtDbQueriesMutex); \ + if (!tlsCommand ## Ptr) { \ + static DPL::ThreadLocalVariable tmp; \ + tlsCommand ## Ptr = &tmp; \ + } \ + } \ + DPL::ThreadLocalVariable &tlsCommand = *tlsCommand ## Ptr; \ + if (tlsCommand.IsNull()) { tlsCommand = InternalType(interface); } + +#define WRT_DB_SELECT(name, type, interface) WRT_DB_INTERNAL(name, type::Select, interface) + +#define WRT_DB_INSERT(name, type, interface) WRT_DB_INTERNAL(name, type::Insert, interface) + +#define WRT_DB_UPDATE(name, type, interface) WRT_DB_INTERNAL(name, type::Update, interface) + +#define WRT_DB_DELETE(name, type, interface) WRT_DB_INTERNAL(name, type::Delete, interface) + +#endif // WRT_ENGINE_SRC_CONFIGURATION_WEBRUNTIME_DATABASE_H diff --git a/modules/widget_dao/include/dpl/wrt-dao-ro/widget_config.h b/modules/widget_dao/include/dpl/wrt-dao-ro/widget_config.h new file mode 100644 index 0000000..bd1ee00 --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-ro/widget_config.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file widget_config.h + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief Implementation file for widget config. + */ +#ifndef SRC_DOMAIN_WIDGET_CONFIG_H +#define SRC_DOMAIN_WIDGET_CONFIG_H + +#include +#include + +#include +#include +#include + +namespace WrtDB { +namespace WidgetConfig { + +inline std::string GetWidgetBasePath(DPL::String pkgName) +{ + return PathBuilder() + .Append(GlobalConfig::GetUserInstalledWidgetPath()) + .Append(DPL::ToUTF8String(pkgName)) + .GetFullPath(); +} + +inline std::string GetWidgetWebLocalStoragePath(DPL::String pkgName) +{ + return PathBuilder(GetWidgetBasePath(pkgName)) + .Append(GlobalConfig::GetWidgetLocalStoragePath()) + .GetFullPath(); +} + +inline std::string GetWidgetPersistentStoragePath(DPL::String pkgName) +{ + return PathBuilder(GetWidgetBasePath(pkgName)) + .Append(GlobalConfig::GetWidgetPrivateStoragePath()) + .GetFullPath(); +} + +inline std::string GetWidgetTemporaryStoragePath(DPL::String pkgName) +{ + return PathBuilder() + .Append(GlobalConfig::GetTmpDirPath()) + .Append(DPL::ToUTF8String(pkgName)) + .GetFullPath(); +} + +inline std::string GetWidgetDesktopFilePath(DPL::String pkgName) +{ + return PathBuilder() + .Append(GlobalConfig::GetUserWidgetDesktopPath()) + .Append(DPL::ToUTF8String(pkgName)) + .Concat(".desktop") + .GetFullPath(); +} +} // namespace WidgetConfig +} // namespace WrtDB + +#endif diff --git a/modules/widget_dao/include/dpl/wrt-dao-ro/widget_dao_read_only.h b/modules/widget_dao/include/dpl/wrt-dao-ro/widget_dao_read_only.h new file mode 100644 index 0000000..9dbd580 --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-ro/widget_dao_read_only.h @@ -0,0 +1,709 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file contains the declaration of widget dao class. + * + * @file widget_dao_read_only.h + * @author Yang Jie (jie2.yang@samsung.com) + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of widget dao + */ + +#ifndef _WRT_SRC_CONFIGURATION_WIDGET_DAO_READ_ONLY_H_ +#define _WRT_SRC_CONFIGURATION_WIDGET_DAO_READ_ONLY_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace WrtDB { + +/** + * Widget's signature enum. + * This enumerates signature type of widget. + */ +enum WidgetSignatureType +{ + SIGNATURE_TYPE_CARRIER = 0, + SIGNATURE_TYPE_IDENTIFIED, + SIGNATURE_TYPE_UNIDENTIFIED +}; + +typedef std::list StringList; + +struct WidgetLocalizedInfo +{ + DPL::OptionalString name; + DPL::OptionalString shortName; + DPL::OptionalString description; + DPL::OptionalString license; + DPL::OptionalString licenseHref; +}; + +/** + * CertificateData + * A structure to hold certificate fingerprints. + */ +struct WidgetCertificateData +{ + enum Owner { AUTHOR, DISTRIBUTOR, UNKNOWN }; + enum Type { ROOT, ENDENTITY }; + + // type of signature: author/distributor + Owner owner; + // indicates whether this is ca certificate + Type type; + + // chain id number: relative BASE, where BASE is signatureBASE.xml + int chainId; + // certificate fingerprint digested by md5 + std::string strMD5Fingerprint; + // certificate fingerprint digestef by sha1 + std::string strSHA1Fingerprint; + // Common name field in certificate + DPL::String strCommonName; + + bool operator== (const WidgetCertificateData& certData) const { + return certData.chainId == chainId && + certData.owner == owner && + certData.strCommonName == strCommonName && + certData.strMD5Fingerprint == strMD5Fingerprint && + certData.strSHA1Fingerprint == strSHA1Fingerprint; + } +}; + +typedef std::list WidgetCertificateDataList; + +typedef DPL::String Locale; +typedef std::set LocaleSet; + +/** + * WidgetRegisterInfo + * A structure to hold widget's information needed to be registered. + * @see WidgetConfigurationInfo + */ +struct WidgetRegisterInfo +{ + struct LocalizedIcon : public ConfigParserData::Icon + { + LocalizedIcon(const ConfigParserData::Icon& icon, + const LocaleSet& _availableLocales) : + ConfigParserData::Icon(icon), + availableLocales(_availableLocales) + { + } + + LocaleSet availableLocales; + }; + + struct StartFileProperties + { + DPL::String encoding; + DPL::String type; + }; + + typedef std::map StartFilePropertiesForLocalesMap; + struct LocalizedStartFile + { + DPL::String path; + StartFilePropertiesForLocalesMap propertiesForLocales; + }; + + typedef std::list LocalizedIconList; + typedef std::list LocalizedStartFileList; + struct LocalizationData + { + LocalizedIconList icons; + LocalizedStartFileList startFiles; + }; + + enum SecurityDomain + { + Untrusted, + Trusted, + Operator, + None + }; + + //Constructor + WidgetRegisterInfo() : + type(APP_TYPE_UNKNOWN), + signatureType(SIGNATURE_TYPE_UNIDENTIFIED), + isFactoryWidget(0), + isTestWidget(0), + configInfo() + { + } + + WidgetType type; + DPL::OptionalString guid; + DPL::OptionalString version; + std::string shareHref; + std::string baseFolder; + WidgetSignatureType signatureType; + int isFactoryWidget; + int isTestWidget; + ConfigParserData configInfo; + Powder::Description powderDescription; + LocalizationData localizationData; + DPL::OptionalString pkgname; + time_t installedTime; +}; + +typedef std::list CertificateChainList; +class IWacSecurity +{ + public: + virtual ~IWacSecurity(); + + virtual const WidgetCertificateDataList& getCertificateList() const = 0; + + virtual bool isRecognized() const = 0; + + virtual bool isDistributorSigned() const = 0; + + virtual bool isWacSigned() const = 0; + + virtual void getCertificateChainList(CertificateChainList& list) const = 0; +}; + +/** + * WidgetAuthorInfo. + * Structure to hold the information of widget's author. + */ +struct WidgetAuthorInfo +{ + DPL::OptionalString name; + DPL::OptionalString email; + DPL::OptionalString href; +}; + +/** + * Widget update policy + */ +enum WidgetUpdatePolicy +{ + WIDGET_UPDATE_MONTHLY = 0, //< monthly update + WIDGET_UPDATE_WEEKLY, //< weekly update + WIDGET_UPDATE_DAILY, //< daily update + WIDGET_UPDATE_STARTUP, //< update when cell phone boots + WIDGET_UPDATE_NEVER //< never update +}; + +typedef std::list WidgetCertificateCNList; +typedef std::list LanguageTagList; +typedef std::list HostList; +typedef std::list FingerPrintList; +typedef std::list ResourceAttributeList; + +class WidgetDAOReadOnly +{ + public: + /** + * WidgetDAO Exception classes + */ + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, DatabaseError) + DECLARE_EXCEPTION_TYPE(Base, ReadOnlyProperty) + DECLARE_EXCEPTION_TYPE(Base, GUIDisNull) + DECLARE_EXCEPTION_TYPE(Base, UnexpectedEmptyResult) + DECLARE_EXCEPTION_TYPE(Base, WidgetNotExist) + DECLARE_EXCEPTION_TYPE(Base, AlreadyRegistered) + }; + + protected: + DbWidgetHandle m_widgetHandle; + + public: + struct WidgetLocalizedIconRow + { + int appId; + int iconId; + DPL::String widgetLocale; + }; + typedef std::list WidgetLocalizedIconList; + + struct WidgetIconRow + { + int iconId; + int appId; + DPL::String iconSrc; + DPL::OptionalInt iconWidth; + DPL::OptionalInt iconHeight; + }; + typedef std::list WidgetIconList; + + struct WidgetStartFileRow + { + int startFileId; + int appId; + DPL::String src; + }; + typedef std::list WidgetStartFileList; + + struct WidgetLocalizedStartFileRow + { + int startFileId; + int appId; + DPL::String widgetLocale; + DPL::String type; + DPL::String encoding; + }; + typedef std::list LocalizedStartFileList; + + + /** + * This is a constructor. + * + * @param[in] widgetHandle application id of widget. + */ + WidgetDAOReadOnly(DbWidgetHandle widgetHandle); + + /** + * Destructor + */ + virtual ~WidgetDAOReadOnly(); + + /** + * This method returns widget handle(m_widgetHandle). + * + * @return widget handle(m_widgetHandle). + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in DB table. + */ + DbWidgetHandle getHandle() const; + static DbWidgetHandle getHandle(const WidgetGUID GUID); + static DbWidgetHandle getHandle(const DPL::String pkgName); + + /** + * This method returns the root directory of widget resource. + * + * @return path name of root directory. + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + DPL::String getPath() const; + + DPL::String getFullPath() const; + + /** + * This method returns the preferred size of the widget, + * including width and height. + * + * @see DbWidgetSize + * @return An structure type variable to hold widget's size. + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching + * DB table. + */ + DbWidgetSize getPreferredSize() const; + + /** + * This method returns the type of the widget. + * + * @return WidgetType + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching + records in DB table. + */ + WidgetType getWidgetType() const; + + /** + * This method returns the id of the widget. + * + * @return widget id + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + WidgetGUID getGUID() const; + + /** + * This method returns the Package name of the widget. + * + * @return pkgname + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in DB table. + */ + DPL::OptionalString getPkgname() const; + + /** + * This method returns the defaultlocale for the widget. + * + * @return defaultlocale + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + DPL::OptionalString getDefaultlocale() const; + + /** + * This method returns list of localized icons files; + * + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + WidgetLocalizedIconList getLocalizedIconList() const; + + /** + * This method returns list of icons files; + * + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + WidgetIconList getIconList() const; + + /** + * This method returns list of localized start files; + * + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + LocalizedStartFileList getLocalizedStartFileList() const; + + /** + * This method returns list of start files; + * + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + WidgetStartFileList getStartFileList() const; + + /** + * @param[out] outAccessInfoList list filled with access info structures + */ + void getWidgetAccessInfo(WidgetAccessInfoList& outAccessInfoList) const; + + /** + * WAC 2.0 extension + * @return recognized status + */ + bool isRecognized() const; + + /** + * WAC 2.0 extension + * @return + */ + bool isWacSigned() const; + + /** + * WAC 2.0 extension + * @return + */ + bool isDistributorSigned() const; + + /** + * WAC 2.0 extension + * @return trusted status + */ + bool isTrusted() const; + + /** + * WAC 2.0 extension + * @return is WAC test widget + */ + bool isTestWidget() const; + + /** + * This method returns window mode of widget. + * + * @return window modes of widget + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + WindowModeList getWindowModes() const; + + /** + * This method returns the version of the widget. + * + * @return version of widget + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + DPL::OptionalString getVersion() const; + + /** + * This method returns list filed with Common Name entries from certificate. + * + * @return Common Name of Distribuotor End Entity certificate. + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + WidgetCertificateCNList getKeyCommonNameList( + WidgetCertificateData::Owner owner, + WidgetCertificateData::Type type) const; + + /** + * given a certificate owner (author / distributor) and type of certificate + * (end entity / ca) + * function returns list of matching fingerprints + */ + FingerPrintList getKeyFingerprints( + WidgetCertificateData::Owner owner, + WidgetCertificateData::Type type) const; + + /* + * This method gets certificate data list for a widget from database. + */ + WidgetCertificateDataList getCertificateDataList() const; + + /** + * This method returns a list of widget features. + * + * @see WidgetFeature + * @see FreeFeatureList() + * @return list of widget features, type of list element is structure + * WidgetFeature + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + DbWidgetFeatureSet getFeaturesList() const; + + static WidgetParamMap getFeatureParams(int widgetFeatureId); + /** + * This method checks whether widget has specified feature. + * + * @return true if has, false if has not + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + */ + bool hasFeature(const std::string& featureName) const; + + /** + * This method gets host list that widget can connect to. + * + * @return See above comment + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + */ + HostList getAccessHostList() const; + + /** + * This method gets widget's access on network: true or false. + * + * @return true: widget can access network; false: widget can not access + * network. + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + bool getAccessNetworkMode() const; + + /** + * This method gets if widget needs webkit plugins enabled + * + * @return true: widget needs webkit plugins enabled + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + bool getWebkitPluginsRequired() const; + + /** + * This method returns a list of all the installed widgets. + * + * @return list of installed widgets' app id. + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + static DbWidgetHandleList getHandleList(); + + /** + * This method removes a widget's information from EmDB. + * + * @see RegisterWidget() + * @param[in] widgetHandle widget's app id + * @return true if succeed, false if fail. + */ + static void unregisterWidget(DbWidgetHandle widgetHandle); + + /** + * This method gets author's infomation of a widget which is parsed from + * configiration document. + * + * @see WidgetAuthorInfo + * @param[out] pAuthorInfo + * @return true if succeed, false if fail. + */ + WidgetAuthorInfo getAuthorInfo() const; + + /** + * This method gets author's name of a widget which is parsed from + * configiration document. + * + * @param[out] pAuthorInfo + * @return author's name. + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + DPL::OptionalString getAuthorName() const; + + /** + * This method gets author's email of a widget which is parsed from + * configiration document. + * + * @param[out] pAuthorInfo + * @return author's email. + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + DPL::OptionalString getAuthorEmail() const; + + /** + * This method gets author's email of a widget which is parsed from + * configiration document. + * + * @param[out] pAuthorInfo + * @return author's email. + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + DPL::OptionalString getAuthorHref() const; + + /** + * This method returns minimum version of WAC that WRT has to be compliant + * to to run this widget + * + * @return Minimum version + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + DPL::OptionalString getMinimumWacVersion() const; + + /* This method get widget' share href. + * + * @return widget's share href + */ + std::string getShareHref() const; + + /** + * This method checks whether specified widget is a factory widget. + * + * @param[in] widgetHandle widget's app id + * @return true if yes, false if no. + */ + bool isFactory() const; + + /** + * This method get widget installed time + * + * @return time_t : return widget's install time + */ + time_t getInstallTime() const; + + /** + * This method gets widget base folder. + * + * @return widget base folder. + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + std::string getBaseFolder() const; + + /** + * This method gets deletable property of widget. + * + * @return true: can be deleted; false: can not be deleted + * @exception WRT_CONF_ERR_GCONF_FAILURE + * @exception WRT_CONF_ERR_EMDB_FAILURE + * @exception WRT_CONF_ERR_EMDB_NO_RECORD + */ + bool isDeletable() const; + + /* This method gets the parameter list for resource. + */ + ResourceAttributeList getResourceAttribute( + const std::string &resourceId) const; + + /* This method checks read only flag for given property + */ + DPL::OptionalInt checkPropertyReadFlag( + const PropertyDAOReadOnly::WidgetPropertyKey &key) const; + + /* This method gets widget property key list + */ + PropertyDAOReadOnly::WidgetPropertyKeyList getPropertyKeyList() const; + + /* This method gets widget property list + */ + PropertyDAOReadOnly::WidgetPreferenceList getPropertyList() const; + + /* This method get widget property value + */ + PropertyDAOReadOnly::WidgetPropertyValue getPropertyValue( + const PropertyDAOReadOnly::WidgetPropertyKey &key) const; + + LanguageTagList getLanguageTags() const; + LanguageTagList getIconLanguageTags() const; + + WidgetLocalizedInfo getLocalizedInfo(const DPL::String& languageTag) const; + std::string getCookieDatabasePath() const; + // Local storage + std::string getPrivateLocalStoragePath() const; + + ChildProtection::Record getChildProtection() const; + + Powder::Description getPowderDescription() const; + + bool getBackSupported() const; + + static bool isWidgetInstalled(DbWidgetHandle handle); + static bool isWidgetInstalled(DPL::String pkgName); + + CertificateChainList getWidgetCertificate() const; + + void getWidgetSettings(WidgetSettings& outWidgetSettings) const; + + /** + * This method gets application service list that define AUL value + * + * @return See above comment + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + */ + void getAppServiceList( + WidgetApplicationServiceList& outAppServiceList) const; +}; + +} // namespace WrtDB + +#endif // _WRT_SRC_CONFIGURATION_WIDGET_DAO_READ_ONLY_H_ + diff --git a/modules/widget_dao/include/dpl/wrt-dao-rw/feature_dao.h b/modules/widget_dao/include/dpl/wrt-dao-rw/feature_dao.h new file mode 100644 index 0000000..94b5cb7 --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-rw/feature_dao.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file contains the declaration of feature dao class. + * + * @file feature_dao.h + * @author Jaroslaw Osmanski (j.osmanski@samsung.com) + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of feature dao + */ +#ifndef _FEATURE_DAO_H +#define _FEATURE_DAO_H + +#include + +namespace WrtDB { +namespace FeatureDAO { + FeatureHandle RegisterFeature(const PluginMetafileData::Feature &feature, + const DbPluginHandle pluginHandle); + + FeatureHandle RegisterStrangeFeature(const std::string& featureName); +} // namespace FeatureDB +} // namespace WrtDB + +#endif /* _FEATURE_DAO_H */ + diff --git a/modules/widget_dao/include/dpl/wrt-dao-rw/global_dao.h b/modules/widget_dao/include/dpl/wrt-dao-rw/global_dao.h new file mode 100644 index 0000000..51c9f38 --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-rw/global_dao.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file global_dao.h + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of global dao + */ + +#ifndef WRT_SRC_CONFIGURATION_GLOBAL_DAO_H_ +#define WRT_SRC_CONFIGURATION_GLOBAL_DAO_H_ + +#include + +namespace WrtDB { + +class GlobalDAO : public GlobalDAOReadOnly +{ + public: + /** + * Set new Parental mode status + */ + static void SetParentalMode(bool parental_status); + + /** + * Retrieve Parental mode maximal allowed age + * + * @return NULL if allowed age not set, else pointer value is allowed age + */ + static void SetParentalAllowedAge(const DPL::OptionalInt& age); + + static void AddCategoryRule( + const ChildProtection::PowderRules::CategoryRule& powder); + + static void RemoveCategoryRule( + const ChildProtection::PowderRules::CategoryRule& powder); + + static void UpdateCategoryRule( + const ChildProtection::PowderRules::CategoryRule& oldRule, + const ChildProtection::PowderRules::CategoryRule& newRule); + + static void AddAdultBlackListElement(const DPL::String &url); + + static void RemoveAdultBlackListElement(const DPL::String &url); + + static void UpdateAdultBlackList(const DPL::String &oldUrl, + const DPL::String &newUrl); + + /** + * Add deffered widget packages to be installed + */ + static void AddDefferedWidgetPackageInstallation(const DPL::String &path); + + /** + * Remove deffered widget packages to be installed + */ + static void RemoveDefferedWidgetPackageInstallation(const DPL::String &arg); + + static void SetDeveloperMode(bool mode); + + static void SetSecureByDefault(bool secureByDefault); + + static void setComplianceMode(bool mode); + + static void setComplianceFakeImei(const std::string &imei); + + static void setComplianceFakeMeid(const std::string &meid); + + static void AddWhiteURI(const std::string &value, bool subDomain); + + static void RemoveWhiteURI(const std::string &uri); + + /** + * This method changes network access mode while roaming is enabled. + * + */ + static void SetHomeNetworkDataUsage(NetworkAccessMode newMode); + + /** + * This method changes network access mode while roaming is enabled. + * + */ + static void SetRoamingDataUsage(NetworkAccessMode newMode); + + /** + * This method sets Autofill for Webkit + */ + static void SetAutoSaveIdPasswd( + const DPL::String &url, const AutoSaveData &saveData); + + + private: + GlobalDAO() + { + } +}; + +} // namespace WrtDB + +#endif /* WRT_SRC_CONFIGURATION_GLOBAL_DAO_H_ */ diff --git a/modules/widget_dao/include/dpl/wrt-dao-rw/plugin_dao.h b/modules/widget_dao/include/dpl/wrt-dao-rw/plugin_dao.h new file mode 100644 index 0000000..e8a86d3 --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-rw/plugin_dao.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file contains the declaration of plugin dao class. + * + * @file plugin_dao.h + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of plugin dao + */ + +#ifndef WRT_SRC_CONFIGURATION_PLUGIN_DAO_H_ +#define WRT_SRC_CONFIGURATION_PLUGIN_DAO_H_ + +#include + +namespace WrtDB { + +class PluginDAO : public PluginDAOReadOnly +{ + public: + PluginDAO(DbPluginHandle pluginHandle); + PluginDAO(const std::string &libraryName); + + static DbPluginHandle registerPlugin( + const PluginMetafileData& metafile, + const std::string& pluginPath); + + static void registerPluginImplementedObject( + const std::string& objectName, + DbPluginHandle pluginHandle); + + static void registerPluginRequiredObject( + const std::string& objectName, + DbPluginHandle pluginHandle); + + static void registerPluginLibrariesDependencies( + DbPluginHandle plugin, + const PluginHandleSetPtr& dependencies); + + static void setPluginInstallationStatus( + DbPluginHandle, + PluginInstallationState); +}; + +} // namespace WrtDB + +#endif /* WRT_SRC_CONFIGURATION_PLUGIN_DAO_H_ */ diff --git a/modules/widget_dao/include/dpl/wrt-dao-rw/property_dao.h b/modules/widget_dao/include/dpl/wrt-dao-rw/property_dao.h new file mode 100644 index 0000000..d75dc59 --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-rw/property_dao.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file property_dao.h + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of property dao + */ + +#ifndef WRT_SRC_CONFIGURATION_PROPERTY_DAO_H_ +#define WRT_SRC_CONFIGURATION_PROPERTY_DAO_H_ + +#include + +namespace WrtDB { + +struct WidgetRegisterInfo; //forward declaration + +namespace PropertyDAO { + +void RemoveProperty(DbWidgetHandle widgetHandle, + const PropertyDAOReadOnly::WidgetPropertyKey &key); + +/* This method sets widget property + */ +void SetProperty(DbWidgetHandle widgetHandle, + const PropertyDAOReadOnly::WidgetPropertyKey &key, + const PropertyDAOReadOnly::WidgetPropertyValue &value, + bool readOnly = false); + +/* This method registers properties for widget. + * Properties unregistering is done via "delete cascade" mechanism in SQL + */ +void RegisterProperties(DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info); + +} // namespace PropertyDAO +} // namespace WrtDB + +#endif /* WRT_SRC_CONFIGURATION_PROPERTY_DAO_H_ */ diff --git a/modules/widget_dao/include/dpl/wrt-dao-rw/widget_dao.h b/modules/widget_dao/include/dpl/wrt-dao-rw/widget_dao.h new file mode 100644 index 0000000..1b8b507 --- /dev/null +++ b/modules/widget_dao/include/dpl/wrt-dao-rw/widget_dao.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file contains the declaration of widget dao class. + * + * @file widget_dao.h + * @author Yang Jie (jie2.yang@samsung.com) + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of widget dao + */ +#ifndef WIDGET_DAO_H +#define WIDGET_DAO_H + +#include +#include +#include +#include +#include +#include +#include + +namespace WrtDB { + +class WidgetDAO : public WidgetDAOReadOnly +{ + public: + typedef std::list LanguageTagsList; + + /** + * This is a constructor. + * + * @param[in] widgetHandle application id of widget. + * @param[in] widgetGUID application guid of widget. + */ + WidgetDAO(DbWidgetHandle widgetHandle); + WidgetDAO(DPL::OptionalString widgetGUID); + + /** + * Destructor + */ + virtual ~WidgetDAO(); + + /** + * This method registers the widget information to the DB when it is installed. + * + * @see WidgetRegisterInfo + * @see UnRegisterWidget() + * @param[in] pWidgetRegisterInfo Specified the widget's information needed to be registered. + * @return widget's app id issued by app manager; 0 represents a failure during register. + */ + static DbWidgetHandle registerWidget( + const WidgetRegisterInfo &pWidgetRegisterInfo, + const IWacSecurity &wacSecurity, + const LanguageTagsList& languageTags); + + /** + * This method removes a widget's information from EmDB. + * + * @see RegisterWidget() + * @param[in] widgetHandle widget's app id + * @return true if succeed, false if fail. + */ + static void unregisterWidget(DbWidgetHandle widgetHandle); + + /* This method removes widget property + */ + void removeProperty(const PropertyDAOReadOnly::WidgetPropertyKey &key); + + /* This method sets widget property + */ + void setProperty(const PropertyDAOReadOnly::WidgetPropertyKey &key, + const PropertyDAOReadOnly::WidgetPropertyValue &value, + bool readOnly = false); + + /* set PkgName + */ + void setPkgName(const DPL::OptionalString& pkgName); + + private: + //Methods used during widget registering + static DbWidgetHandle registerWidgetInfo( + const WidgetRegisterInfo ®Info, + const IWacSecurity &wacSecurity); + static void registerWidgetExtendedInfo( + DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info); + static void registerWidgetLocalizedInfo( + DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info); + static void registerWidgetUserAgentLocales( + DbWidgetHandle widgetHandle, + const WidgetRegisterInfo &rInf, + const LanguageTagsList& languageTags); + static void registerWidgetIcons( + DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info); + static void registerWidgetStartFile( + DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info); + static void registerWidgetPreferences( + DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info); + static void registerWidgetFeatures( + DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info); + static void registerWidgetWindowModes( + DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info); + static void registerWidgetWarpInfo( + DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info); + static void registerWidgetCertificates( + DbWidgetHandle widgetHandle, + const IWacSecurity &wacSecurity); + static void registerWidgetPowderData( + DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info); + static void registerLaunchCertificates( + DbWidgetHandle widgetHandle, + const CertificateChainList &list); + static void registerWidgetSettings( + DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info); + static void registerAppService( + DbWidgetHandle widgetHandle, + const WidgetRegisterInfo ®Info); +}; + +} // namespace WrtDB + +#endif // WIDGET_DAO_H diff --git a/modules/widget_dao/orm/gen_db_md5.sh b/modules/widget_dao/orm/gen_db_md5.sh new file mode 100755 index 0000000..38587b7 --- /dev/null +++ b/modules/widget_dao/orm/gen_db_md5.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +CHECKSUM=`cat ${2} ${3} 2>/dev/null | md5sum 2>/dev/null | cut -d\ -f1 2>/dev/null` +echo "#define DB_CHECKSUM DB_VERSION_${CHECKSUM}" > ${1} +echo "#define DB_CHECKSUM_STR \"DB_VERSION_${CHECKSUM}\"" >> ${1} + diff --git a/modules/widget_dao/orm/iana_db b/modules/widget_dao/orm/iana_db new file mode 100644 index 0000000..ffd41ea --- /dev/null +++ b/modules/widget_dao/orm/iana_db @@ -0,0 +1,8957 @@ +SQL(BEGIN IMMEDIATE TRANSACTION;) +CREATE_TABLE(iana_records) + COLUMN_NOT_NULL(REC_ID, INT,) + COLUMN_NOT_NULL(TYPE, INT,) + COLUMN(TAG, VARCHAR(256),) + COLUMN(SUBTAG, VARCHAR(256),) + COLUMN(DESCRIPTION, VARCHAR(256),) + COLUMN(ADDED, VARCHAR(256),) + COLUMN(DEPRECATED, INT,) + COLUMN(PREFERRED_VALUE, INT,) + COLUMN(PREFIX, VARCHAR(256),) + COLUMN(SUPPRESS_SCRIPT, VARCHAR(256),) + COLUMN(MACRO_LANGUAGE, VARCHAR(256),) + COLUMN(SCOPE, VARCHAR(256),) + COLUMN(COMMENTS, VARCHAR(256),) +CREATE_TABLE_END() +SQL( +INSERT INTO "iana_records" VALUES(0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1,0,NULL,'aa','afar','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2,0,NULL,'ab','abkhazian','1129420800',NULL,NULL,NULL,'cyrl',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3,0,NULL,'ae','avestan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4,0,NULL,'af','afrikaans','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5,0,NULL,'ak','akan','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(6,0,NULL,'am','amharic','1129420800',NULL,NULL,NULL,'ethi',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7,0,NULL,'an','aragonese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8,0,NULL,'ar','arabic','1129420800',NULL,NULL,NULL,'arab',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(9,0,NULL,'as','assamese','1129420800',NULL,NULL,NULL,'beng',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(10,0,NULL,'av','avaric','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(11,0,NULL,'ay','aymara','1129420800',NULL,NULL,NULL,'latn',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(12,0,NULL,'az','azerbaijani','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(13,0,NULL,'ba','bashkir','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(14,0,NULL,'be','belarusian','1129420800',NULL,NULL,NULL,'cyrl',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(15,0,NULL,'bg','bulgarian','1129420800',NULL,NULL,NULL,'cyrl',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(16,0,NULL,'bh','bihari languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(17,0,NULL,'bi','bislama','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(18,0,NULL,'bm','bambara','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(19,0,NULL,'bn','bengali','1129420800',NULL,NULL,NULL,'beng',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(20,0,NULL,'bo','tibetan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(21,0,NULL,'br','breton','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(22,0,NULL,'bs','bosnian','1129420800',NULL,NULL,NULL,'latn','sh',NULL,NULL); +INSERT INTO "iana_records" VALUES(23,0,NULL,'ca','catalan','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(23,0,NULL,'ca','valencian','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(24,0,NULL,'ce','chechen','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(25,0,NULL,'ch','chamorro','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(26,0,NULL,'co','corsican','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(27,0,NULL,'cr','cree','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(28,0,NULL,'cs','czech','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(29,0,NULL,'cu','church slavic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(29,0,NULL,'cu','church slavonic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(29,0,NULL,'cu','old bulgarian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(29,0,NULL,'cu','old church slavonic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(29,0,NULL,'cu','old slavonic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(30,0,NULL,'cv','chuvash','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(31,0,NULL,'cy','welsh','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(32,0,NULL,'da','danish','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(33,0,NULL,'de','german','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(34,0,NULL,'dv','dhivehi','1129420800',NULL,NULL,NULL,'thaa',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(34,0,NULL,'dv','divehi','1129420800',NULL,NULL,NULL,'thaa',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(34,0,NULL,'dv','maldivian','1129420800',NULL,NULL,NULL,'thaa',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(35,0,NULL,'dz','dzongkha','1129420800',NULL,NULL,NULL,'tibt',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(36,0,NULL,'ee','ewe','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(37,0,NULL,'el','modern greek (1453-)','1129420800',NULL,NULL,NULL,'grek',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(38,0,NULL,'en','english','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(39,0,NULL,'eo','esperanto','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(40,0,NULL,'es','castilian','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(40,0,NULL,'es','spanish','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(41,0,NULL,'et','estonian','1129420800',NULL,NULL,NULL,'latn',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(42,0,NULL,'eu','basque','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(43,0,NULL,'fa','persian','1129420800',NULL,NULL,NULL,'arab',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(44,0,NULL,'ff','fulah','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(45,0,NULL,'fi','finnish','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(46,0,NULL,'fj','fijian','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(47,0,NULL,'fo','faroese','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(48,0,NULL,'fr','french','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(49,0,NULL,'fy','western frisian','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(50,0,NULL,'ga','irish','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(51,0,NULL,'gd','gaelic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(51,0,NULL,'gd','scottish gaelic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(52,0,NULL,'gl','galician','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(53,0,NULL,'gn','guarani','1129420800',NULL,NULL,NULL,'latn',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(54,0,NULL,'gu','gujarati','1129420800',NULL,NULL,NULL,'gujr',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(55,0,NULL,'gv','manx','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(56,0,NULL,'ha','hausa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(57,0,NULL,'he','hebrew','1129420800',NULL,NULL,NULL,'hebr',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(58,0,NULL,'hi','hindi','1129420800',NULL,NULL,NULL,'deva',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(59,0,NULL,'ho','hiri motu','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(60,0,NULL,'hr','croatian','1129420800',NULL,NULL,NULL,'latn','sh',NULL,NULL); +INSERT INTO "iana_records" VALUES(61,0,NULL,'ht','haitian','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(61,0,NULL,'ht','haitian creole','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(62,0,NULL,'hu','hungarian','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(63,0,NULL,'hy','armenian','1129420800',NULL,NULL,NULL,'armn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(64,0,NULL,'hz','herero','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(65,0,NULL,'ia','interlingua (international auxiliary language association)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(66,0,NULL,'id','indonesian','1129420800',NULL,NULL,NULL,'latn','ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(67,0,NULL,'ie','interlingue','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(67,0,NULL,'ie','occidental','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(68,0,NULL,'ig','igbo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(69,0,NULL,'ii','nuosu','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(69,0,NULL,'ii','sichuan yi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(70,0,NULL,'ik','inupiaq','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(71,0,NULL,'in','indonesian','1129420800',599616000,'id',NULL,'latn','ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(72,0,NULL,'io','ido','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(73,0,NULL,'is','icelandic','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(74,0,NULL,'it','italian','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(75,0,NULL,'iu','inuktitut','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(76,0,NULL,'iw','hebrew','1129420800',599616000,'he',NULL,'hebr',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(77,0,NULL,'ja','japanese','1129420800',NULL,NULL,NULL,'jpan',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(78,0,NULL,'ji','yiddish','1129420800',599616000,'yi',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(79,0,NULL,'jv','javanese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(80,0,NULL,'jw','javanese','1129420800',997660800,'jv',NULL,NULL,NULL,NULL,'published by error in table 1 of iso 639:1988'); +INSERT INTO "iana_records" VALUES(81,0,NULL,'ka','georgian','1129420800',NULL,NULL,NULL,'geor',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(82,0,NULL,'kg','kongo','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(83,0,NULL,'ki','gikuyu','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(83,0,NULL,'ki','kikuyu','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(84,0,NULL,'kj','kuanyama','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(84,0,NULL,'kj','kwanyama','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(85,0,NULL,'kk','kazakh','1129420800',NULL,NULL,NULL,'cyrl',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(86,0,NULL,'kl','greenlandic','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(86,0,NULL,'kl','kalaallisut','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(87,0,NULL,'km','central khmer','1129420800',NULL,NULL,NULL,'khmr',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(88,0,NULL,'kn','kannada','1129420800',NULL,NULL,NULL,'knda',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(89,0,NULL,'ko','korean','1129420800',NULL,NULL,NULL,'kore',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(90,0,NULL,'kr','kanuri','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(91,0,NULL,'ks','kashmiri','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(92,0,NULL,'ku','kurdish','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(93,0,NULL,'kv','komi','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(94,0,NULL,'kw','cornish','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(95,0,NULL,'ky','kirghiz','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(95,0,NULL,'ky','kyrgyz','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(96,0,NULL,'la','latin','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(97,0,NULL,'lb','letzeburgesch','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(97,0,NULL,'lb','luxembourgish','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(98,0,NULL,'lg','ganda','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(99,0,NULL,'li','limburgan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(99,0,NULL,'li','limburger','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(99,0,NULL,'li','limburgish','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(100,0,NULL,'ln','lingala','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(101,0,NULL,'lo','lao','1129420800',NULL,NULL,NULL,'laoo',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(102,0,NULL,'lt','lithuanian','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(103,0,NULL,'lu','luba-katanga','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(104,0,NULL,'lv','latvian','1129420800',NULL,NULL,NULL,'latn',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(105,0,NULL,'mg','malagasy','1129420800',NULL,NULL,NULL,'latn',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(106,0,NULL,'mh','marshallese','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(107,0,NULL,'mi','maori','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(108,0,NULL,'mk','macedonian','1129420800',NULL,NULL,NULL,'cyrl',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(109,0,NULL,'ml','malayalam','1129420800',NULL,NULL,NULL,'mlym',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(110,0,NULL,'mn','mongolian','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(111,0,NULL,'mo','moldavian','1129420800',1227312000,'ro',NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(111,0,NULL,'mo','moldovan','1129420800',1227312000,'ro',NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(112,0,NULL,'mr','marathi','1129420800',NULL,NULL,NULL,'deva',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(113,0,NULL,'ms','malay (macrolanguage)','1129420800',NULL,NULL,NULL,'latn',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(114,0,NULL,'mt','maltese','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(115,0,NULL,'my','burmese','1129420800',NULL,NULL,NULL,'mymr',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(116,0,NULL,'na','nauru','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(117,0,NULL,'nb','norwegian bokmÃ¥l','1129420800',NULL,NULL,NULL,'latn','no',NULL,NULL); +INSERT INTO "iana_records" VALUES(118,0,NULL,'nd','north ndebele','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(119,0,NULL,'ne','nepali','1129420800',NULL,NULL,NULL,'deva',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(120,0,NULL,'ng','ndonga','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(121,0,NULL,'nl','dutch','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(121,0,NULL,'nl','flemish','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(122,0,NULL,'nn','norwegian nynorsk','1129420800',NULL,NULL,NULL,'latn','no',NULL,NULL); +INSERT INTO "iana_records" VALUES(123,0,NULL,'no','norwegian','1129420800',NULL,NULL,NULL,'latn',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(124,0,NULL,'nr','south ndebele','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(125,0,NULL,'nv','navaho','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(125,0,NULL,'nv','navajo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(126,0,NULL,'ny','chewa','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(126,0,NULL,'ny','chichewa','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(126,0,NULL,'ny','nyanja','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(127,0,NULL,'oc','occitan (post 1500)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(128,0,NULL,'oj','ojibwa','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(129,0,NULL,'om','oromo','1129420800',NULL,NULL,NULL,'latn',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(130,0,NULL,'or','oriya','1129420800',NULL,NULL,NULL,'orya',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(131,0,NULL,'os','ossetian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(131,0,NULL,'os','ossetic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(132,0,NULL,'pa','panjabi','1129420800',NULL,NULL,NULL,'guru',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(132,0,NULL,'pa','punjabi','1129420800',NULL,NULL,NULL,'guru',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(133,0,NULL,'pi','pali','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(134,0,NULL,'pl','polish','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(135,0,NULL,'ps','pashto','1129420800',NULL,NULL,NULL,'arab',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(135,0,NULL,'ps','pushto','1129420800',NULL,NULL,NULL,'arab',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(136,0,NULL,'pt','portuguese','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(137,0,NULL,'qu','quechua','1129420800',NULL,NULL,NULL,'latn',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(138,0,NULL,'rm','romansh','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(139,0,NULL,'rn','rundi','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(140,0,NULL,'ro','moldavian','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(140,0,NULL,'ro','moldovan','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(140,0,NULL,'ro','romanian','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(141,0,NULL,'ru','russian','1129420800',NULL,NULL,NULL,'cyrl',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(142,0,NULL,'rw','kinyarwanda','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(143,0,NULL,'sa','sanskrit','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(144,0,NULL,'sc','sardinian','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(145,0,NULL,'sd','sindhi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(146,0,NULL,'se','northern sami','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(147,0,NULL,'sg','sango','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(148,0,NULL,'sh','serbo-croatian','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage','sr, hr, bs are preferred for most modern uses'); +INSERT INTO "iana_records" VALUES(149,0,NULL,'si','sinhala','1129420800',NULL,NULL,NULL,'sinh',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(149,0,NULL,'si','sinhalese','1129420800',NULL,NULL,NULL,'sinh',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(150,0,NULL,'sk','slovak','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(151,0,NULL,'sl','slovenian','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(152,0,NULL,'sm','samoan','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(153,0,NULL,'sn','shona','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(154,0,NULL,'so','somali','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(155,0,NULL,'sq','albanian','1129420800',NULL,NULL,NULL,'latn',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(156,0,NULL,'sr','serbian','1129420800',NULL,NULL,NULL,NULL,'sh',NULL,NULL); +INSERT INTO "iana_records" VALUES(157,0,NULL,'ss','swati','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(158,0,NULL,'st','southern sotho','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(159,0,NULL,'su','sundanese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(160,0,NULL,'sv','swedish','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(161,0,NULL,'sw','swahili (macrolanguage)','1129420800',NULL,NULL,NULL,'latn',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(162,0,NULL,'ta','tamil','1129420800',NULL,NULL,NULL,'taml',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(163,0,NULL,'te','telugu','1129420800',NULL,NULL,NULL,'telu',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(164,0,NULL,'tg','tajik','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(165,0,NULL,'th','thai','1129420800',NULL,NULL,NULL,'thai',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(166,0,NULL,'ti','tigrinya','1129420800',NULL,NULL,NULL,'ethi',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(167,0,NULL,'tk','turkmen','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(168,0,NULL,'tl','tagalog','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(169,0,NULL,'tn','tswana','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(170,0,NULL,'to','tonga (tonga islands)','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(171,0,NULL,'tr','turkish','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(172,0,NULL,'ts','tsonga','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(173,0,NULL,'tt','tatar','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(174,0,NULL,'tw','twi','1129420800',NULL,NULL,NULL,NULL,'ak',NULL,NULL); +INSERT INTO "iana_records" VALUES(175,0,NULL,'ty','tahitian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(176,0,NULL,'ug','uighur','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(176,0,NULL,'ug','uyghur','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(177,0,NULL,'uk','ukrainian','1129420800',NULL,NULL,NULL,'cyrl',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(178,0,NULL,'ur','urdu','1129420800',NULL,NULL,NULL,'arab',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(179,0,NULL,'uz','uzbek','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(180,0,NULL,'ve','venda','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(181,0,NULL,'vi','vietnamese','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(182,0,NULL,'vo','volapük','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(183,0,NULL,'wa','walloon','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(184,0,NULL,'wo','wolof','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(185,0,NULL,'xh','xhosa','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(186,0,NULL,'yi','yiddish','1129420800',NULL,NULL,NULL,'hebr',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(187,0,NULL,'yo','yoruba','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(188,0,NULL,'za','chuang','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(188,0,NULL,'za','zhuang','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(189,0,NULL,'zh','chinese','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(190,0,NULL,'zu','zulu','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(191,0,NULL,'aaa','ghotuo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(192,0,NULL,'aab','alumu-tesu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(193,0,NULL,'aac','ari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(194,0,NULL,'aad','amal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(195,0,NULL,'aae','arbëreshë albanian','1248825600',NULL,NULL,NULL,NULL,'sq',NULL,NULL); +INSERT INTO "iana_records" VALUES(196,0,NULL,'aaf','aranadan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(197,0,NULL,'aag','ambrak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(198,0,NULL,'aah','abu'' arapesh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(199,0,NULL,'aai','arifama-miniafia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(200,0,NULL,'aak','ankave','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(201,0,NULL,'aal','afade','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(202,0,NULL,'aam','aramanik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(203,0,NULL,'aan','anambé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(204,0,NULL,'aao','algerian saharan arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(205,0,NULL,'aap','pará arára','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(206,0,NULL,'aaq','eastern abnaki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(207,0,NULL,'aas','aasáx','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(208,0,NULL,'aat','arvanitika albanian','1248825600',NULL,NULL,NULL,NULL,'sq',NULL,NULL); +INSERT INTO "iana_records" VALUES(209,0,NULL,'aau','abau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(210,0,NULL,'aav','austro-asiatic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(211,0,NULL,'aaw','solong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(212,0,NULL,'aax','mandobo atas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(213,0,NULL,'aaz','amarasi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(214,0,NULL,'aba','abé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(215,0,NULL,'abb','bankon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(216,0,NULL,'abc','ambala ayta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(217,0,NULL,'abd','manide','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(218,0,NULL,'abe','western abnaki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(219,0,NULL,'abf','abai sungai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(220,0,NULL,'abg','abaga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(221,0,NULL,'abh','tajiki arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(222,0,NULL,'abi','abidji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(223,0,NULL,'abj','aka-bea','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(224,0,NULL,'abl','lampung nyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(225,0,NULL,'abm','abanyom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(226,0,NULL,'abn','abua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(227,0,NULL,'abo','abon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(228,0,NULL,'abp','abellen ayta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(229,0,NULL,'abq','abaza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(230,0,NULL,'abr','abron','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(231,0,NULL,'abs','ambonese malay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(232,0,NULL,'abt','ambulas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(233,0,NULL,'abu','abure','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(234,0,NULL,'abv','baharna arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(235,0,NULL,'abw','pal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(236,0,NULL,'abx','inabaknon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(237,0,NULL,'aby','aneme wake','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(238,0,NULL,'abz','abui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(239,0,NULL,'aca','achagua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(240,0,NULL,'acb','Áncá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(241,0,NULL,'acd','gikyode','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(242,0,NULL,'ace','achinese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(243,0,NULL,'acf','saint lucian creole french','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(244,0,NULL,'ach','acoli','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(245,0,NULL,'aci','aka-cari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(246,0,NULL,'ack','aka-kora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(247,0,NULL,'acl','akar-bale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(248,0,NULL,'acm','mesopotamian arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(249,0,NULL,'acn','achang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(250,0,NULL,'acp','eastern acipa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(251,0,NULL,'acq','ta''izzi-adeni arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(252,0,NULL,'acr','achi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(253,0,NULL,'acs','acroá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(254,0,NULL,'act','achterhoeks','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(255,0,NULL,'acu','achuar-shiwiar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(256,0,NULL,'acv','achumawi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(257,0,NULL,'acw','hijazi arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(258,0,NULL,'acx','omani arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(259,0,NULL,'acy','cypriot arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(260,0,NULL,'acz','acheron','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(261,0,NULL,'ada','adangme','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(262,0,NULL,'adb','adabe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(263,0,NULL,'add','dzodinka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(264,0,NULL,'ade','adele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(265,0,NULL,'adf','dhofari arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(266,0,NULL,'adg','andegerebinha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(267,0,NULL,'adh','adhola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(268,0,NULL,'adi','adi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(269,0,NULL,'adj','adioukrou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(270,0,NULL,'adl','galo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(271,0,NULL,'adn','adang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(272,0,NULL,'ado','abu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(273,0,NULL,'adp','adap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(274,0,NULL,'adq','adangbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(275,0,NULL,'adr','adonara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(276,0,NULL,'ads','adamorobe sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(277,0,NULL,'adt','adnyamathanha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(278,0,NULL,'adu','aduge','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(279,0,NULL,'adw','amundava','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(280,0,NULL,'adx','amdo tibetan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(281,0,NULL,'ady','adygei','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(281,0,NULL,'ady','adyghe','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(282,0,NULL,'adz','adzera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(283,0,NULL,'aea','areba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(284,0,NULL,'aeb','tunisian arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(285,0,NULL,'aec','saidi arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(286,0,NULL,'aed','argentine sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(287,0,NULL,'aee','northeast pashayi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(288,0,NULL,'aek','haeke','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(289,0,NULL,'ael','ambele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(290,0,NULL,'aem','arem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(291,0,NULL,'aen','armenian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(292,0,NULL,'aeq','aer','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(293,0,NULL,'aer','eastern arrernte','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(294,0,NULL,'aes','alsea','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(295,0,NULL,'aeu','akeu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(296,0,NULL,'aew','ambakich','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(297,0,NULL,'aey','amele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(298,0,NULL,'aez','aeka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(299,0,NULL,'afa','afro-asiatic languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(300,0,NULL,'afb','gulf arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(301,0,NULL,'afd','andai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(302,0,NULL,'afe','putukwam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(303,0,NULL,'afg','afghan sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(304,0,NULL,'afh','afrihili','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(305,0,NULL,'afi','akrukay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(306,0,NULL,'afk','nanubae','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(307,0,NULL,'afn','defaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(308,0,NULL,'afo','eloyi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(309,0,NULL,'afp','tapei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(310,0,NULL,'afs','afro-seminole creole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(311,0,NULL,'aft','afitti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(312,0,NULL,'afu','awutu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(313,0,NULL,'afz','obokuitai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(314,0,NULL,'aga','aguano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(315,0,NULL,'agb','legbo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(316,0,NULL,'agc','agatu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(317,0,NULL,'agd','agarabi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(318,0,NULL,'age','angal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(319,0,NULL,'agf','arguni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(320,0,NULL,'agg','angor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(321,0,NULL,'agh','ngelima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(322,0,NULL,'agi','agariya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(323,0,NULL,'agj','argobba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(324,0,NULL,'agk','isarog agta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(325,0,NULL,'agl','fembe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(326,0,NULL,'agm','angaataha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(327,0,NULL,'agn','agutaynen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(328,0,NULL,'ago','tainae','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(329,0,NULL,'agp','paranan','1248825600',1268265600,NULL,NULL,NULL,NULL,NULL,'see apf, prf'); +INSERT INTO "iana_records" VALUES(330,0,NULL,'agq','aghem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(331,0,NULL,'agr','aguaruna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(332,0,NULL,'ags','esimbi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(333,0,NULL,'agt','central cagayan agta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(334,0,NULL,'agu','aguacateco','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(335,0,NULL,'agv','remontado dumagat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(336,0,NULL,'agw','kahua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(337,0,NULL,'agx','aghul','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(338,0,NULL,'agy','southern alta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(339,0,NULL,'agz','mt. iriga agta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(340,0,NULL,'aha','ahanta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(341,0,NULL,'ahb','axamb','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(342,0,NULL,'ahg','qimant','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(343,0,NULL,'ahh','aghu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(344,0,NULL,'ahi','tiagbamrin aizi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(345,0,NULL,'ahk','akha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(346,0,NULL,'ahl','igo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(347,0,NULL,'ahm','mobumrin aizi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(348,0,NULL,'ahn','Àhàn','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(349,0,NULL,'aho','ahom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(350,0,NULL,'ahp','aproumu aizi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(351,0,NULL,'ahr','ahirani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(352,0,NULL,'ahs','ashe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(353,0,NULL,'aht','ahtena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(354,0,NULL,'aia','arosi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(355,0,NULL,'aib','ainu (china)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(356,0,NULL,'aic','ainbai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(357,0,NULL,'aid','alngith','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(358,0,NULL,'aie','amara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(359,0,NULL,'aif','agi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(360,0,NULL,'aig','antigua and barbuda creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(361,0,NULL,'aih','ai-cham','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(362,0,NULL,'aii','assyrian neo-aramaic','1248825600',NULL,NULL,NULL,NULL,'syr',NULL,NULL); +INSERT INTO "iana_records" VALUES(363,0,NULL,'aij','lishanid noshan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(364,0,NULL,'aik','ake','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(365,0,NULL,'ail','aimele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(366,0,NULL,'aim','aimol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(367,0,NULL,'ain','ainu (japan)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(368,0,NULL,'aio','aiton','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(369,0,NULL,'aip','burumakok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(370,0,NULL,'aiq','aimaq','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(371,0,NULL,'air','airoran','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(372,0,NULL,'ais','nataoran amis','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(373,0,NULL,'ait','arikem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(374,0,NULL,'aiw','aari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(375,0,NULL,'aix','aighon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(376,0,NULL,'aiy','ali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(377,0,NULL,'aja','aja (sudan)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(378,0,NULL,'ajg','aja (benin)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(379,0,NULL,'aji','ajië','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(380,0,NULL,'ajp','south levantine arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(381,0,NULL,'ajt','judeo-tunisian arabic','1248825600',NULL,NULL,NULL,NULL,'jrb',NULL,NULL); +INSERT INTO "iana_records" VALUES(382,0,NULL,'aju','judeo-moroccan arabic','1248825600',NULL,NULL,NULL,NULL,'jrb',NULL,NULL); +INSERT INTO "iana_records" VALUES(383,0,NULL,'ajw','ajawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(384,0,NULL,'ajz','amri karbi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(385,0,NULL,'akb','batak angkola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(386,0,NULL,'akc','mpur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(387,0,NULL,'akd','ukpet-ehom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(388,0,NULL,'ake','akawaio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(389,0,NULL,'akf','akpa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(390,0,NULL,'akg','anakalangu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(391,0,NULL,'akh','angal heneng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(392,0,NULL,'aki','aiome','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(393,0,NULL,'akj','aka-jeru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(394,0,NULL,'akk','akkadian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(395,0,NULL,'akl','aklanon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(396,0,NULL,'akm','aka-bo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(397,0,NULL,'ako','akurio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(398,0,NULL,'akp','siwu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(399,0,NULL,'akq','ak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(400,0,NULL,'akr','araki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(401,0,NULL,'aks','akaselem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(402,0,NULL,'akt','akolet','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(403,0,NULL,'aku','akum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(404,0,NULL,'akv','akhvakh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(405,0,NULL,'akw','akwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(406,0,NULL,'akx','aka-kede','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(407,0,NULL,'aky','aka-kol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(408,0,NULL,'akz','alabama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(409,0,NULL,'ala','alago','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(410,0,NULL,'alc','qawasqar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(411,0,NULL,'ald','alladian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(412,0,NULL,'ale','aleut','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(413,0,NULL,'alf','alege','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(414,0,NULL,'alg','algonquian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(415,0,NULL,'alh','alawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(416,0,NULL,'ali','amaimon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(417,0,NULL,'alj','alangan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(418,0,NULL,'alk','alak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(419,0,NULL,'all','allar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(420,0,NULL,'alm','amblong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(421,0,NULL,'aln','gheg albanian','1248825600',NULL,NULL,NULL,NULL,'sq',NULL,NULL); +INSERT INTO "iana_records" VALUES(422,0,NULL,'alo','larike-wakasihu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(423,0,NULL,'alp','alune','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(424,0,NULL,'alq','algonquin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(425,0,NULL,'alr','alutor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(426,0,NULL,'als','tosk albanian','1248825600',NULL,NULL,NULL,NULL,'sq',NULL,NULL); +INSERT INTO "iana_records" VALUES(427,0,NULL,'alt','southern altai','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(428,0,NULL,'alu','''are''are','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(429,0,NULL,'alv','atlantic-congo languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(430,0,NULL,'alw','alaba-k’abeena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(430,0,NULL,'alw','wanbasana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(431,0,NULL,'alx','amol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(432,0,NULL,'aly','alyawarr','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(433,0,NULL,'alz','alur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(434,0,NULL,'ama','amanayé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(435,0,NULL,'amb','ambo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(436,0,NULL,'amc','amahuaca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(437,0,NULL,'ame','yanesha''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(438,0,NULL,'amf','hamer-banna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(439,0,NULL,'amg','amarag','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(440,0,NULL,'ami','amis','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(441,0,NULL,'amj','amdang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(442,0,NULL,'amk','ambai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(443,0,NULL,'aml','war-jaintia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(444,0,NULL,'amm','ama (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(445,0,NULL,'amn','amanab','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(446,0,NULL,'amo','amo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(447,0,NULL,'amp','alamblak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(448,0,NULL,'amq','amahai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(449,0,NULL,'amr','amarakaeri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(450,0,NULL,'ams','southern amami-oshima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(451,0,NULL,'amt','amto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(452,0,NULL,'amu','guerrero amuzgo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(453,0,NULL,'amv','ambelau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(454,0,NULL,'amw','western neo-aramaic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(455,0,NULL,'amx','anmatyerre','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(456,0,NULL,'amy','ami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(457,0,NULL,'amz','atampaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(458,0,NULL,'ana','andaqui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(459,0,NULL,'anb','andoa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(460,0,NULL,'anc','ngas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(461,0,NULL,'and','ansus','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(462,0,NULL,'ane','xârâcùù','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(463,0,NULL,'anf','animere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(464,0,NULL,'ang','old english (ca. 450-1100)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(465,0,NULL,'anh','nend','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(466,0,NULL,'ani','andi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(467,0,NULL,'anj','anor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(468,0,NULL,'ank','goemai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(469,0,NULL,'anl','anu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(470,0,NULL,'anm','anal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(471,0,NULL,'ann','obolo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(472,0,NULL,'ano','andoque','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(473,0,NULL,'anp','angika','1141776000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(474,0,NULL,'anq','jarawa (india)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(475,0,NULL,'anr','andh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(476,0,NULL,'ans','anserma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(477,0,NULL,'ant','antakarinya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(478,0,NULL,'anu','anuak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(479,0,NULL,'anv','denya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(480,0,NULL,'anw','anaang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(481,0,NULL,'anx','andra-hus','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(482,0,NULL,'any','anyin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(483,0,NULL,'anz','anem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(484,0,NULL,'aoa','angolar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(485,0,NULL,'aob','abom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(486,0,NULL,'aoc','pemon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(487,0,NULL,'aod','andarum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(488,0,NULL,'aoe','angal enen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(489,0,NULL,'aof','bragat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(490,0,NULL,'aog','angoram','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(491,0,NULL,'aoh','arma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(492,0,NULL,'aoi','anindilyakwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(493,0,NULL,'aoj','mufian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(494,0,NULL,'aok','arhö','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(495,0,NULL,'aol','alor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(496,0,NULL,'aom','Ömie','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(497,0,NULL,'aon','bumbita arapesh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(498,0,NULL,'aor','aore','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(499,0,NULL,'aos','taikat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(500,0,NULL,'aot','a''tong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(501,0,NULL,'aox','atorada','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(502,0,NULL,'aoz','uab meto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(503,0,NULL,'apa','apache languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(504,0,NULL,'apb','sa''a','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(505,0,NULL,'apc','north levantine arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(506,0,NULL,'apd','sudanese arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(507,0,NULL,'ape','bukiyip','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(508,0,NULL,'apf','pahanan agta','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(509,0,NULL,'apg','ampanang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(510,0,NULL,'aph','athpariya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(511,0,NULL,'api','apiaká','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(512,0,NULL,'apj','jicarilla apache','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(513,0,NULL,'apk','kiowa apache','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(514,0,NULL,'apl','lipan apache','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(515,0,NULL,'apm','mescalero-chiricahua apache','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(516,0,NULL,'apn','apinayé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(517,0,NULL,'apo','apalik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(518,0,NULL,'app','apma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(519,0,NULL,'apq','a-pucikwar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(520,0,NULL,'apr','arop-lokep','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(521,0,NULL,'aps','arop-sissano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(522,0,NULL,'apt','apatani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(523,0,NULL,'apu','apurinã','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(524,0,NULL,'apv','alapmunte','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(525,0,NULL,'apw','western apache','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(526,0,NULL,'apx','aputai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(527,0,NULL,'apy','apalaí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(528,0,NULL,'apz','safeyoka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(529,0,NULL,'aqa','alacalufan languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(530,0,NULL,'aqc','archi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(531,0,NULL,'aqg','arigidi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(532,0,NULL,'aql','algic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(533,0,NULL,'aqm','atohwaim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(534,0,NULL,'aqn','northern alta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(535,0,NULL,'aqp','atakapa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(536,0,NULL,'aqr','arhâ','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(537,0,NULL,'aqz','akuntsu','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(538,0,NULL,'arb','standard arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(539,0,NULL,'arc','imperial aramaic (700-300 bce)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(539,0,NULL,'arc','official aramaic (700-300 bce)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(540,0,NULL,'ard','arabana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(541,0,NULL,'are','western arrarnta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(542,0,NULL,'arh','arhuaco','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(543,0,NULL,'ari','arikara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(544,0,NULL,'arj','arapaso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(545,0,NULL,'ark','arikapú','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(546,0,NULL,'arl','arabela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(547,0,NULL,'arn','mapuche','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(547,0,NULL,'arn','mapudungun','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(548,0,NULL,'aro','araona','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(549,0,NULL,'arp','arapaho','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(550,0,NULL,'arq','algerian arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(551,0,NULL,'arr','karo (brazil)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(552,0,NULL,'ars','najdi arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(553,0,NULL,'art','artificial languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(554,0,NULL,'aru','arawá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(554,0,NULL,'aru','aruá (amazonas state)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(555,0,NULL,'arv','arbore','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(556,0,NULL,'arw','arawak','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(557,0,NULL,'arx','aruá (rodonia state)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(558,0,NULL,'ary','moroccan arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(559,0,NULL,'arz','egyptian arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(560,0,NULL,'asa','asu (tanzania)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(561,0,NULL,'asb','assiniboine','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(562,0,NULL,'asc','casuarina coast asmat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(563,0,NULL,'asd','asas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(564,0,NULL,'ase','american sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(565,0,NULL,'asf','australian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(566,0,NULL,'asg','cishingini','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(567,0,NULL,'ash','abishira','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(568,0,NULL,'asi','buruwai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(569,0,NULL,'asj','nsari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(570,0,NULL,'ask','ashkun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(571,0,NULL,'asl','asilulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(572,0,NULL,'asn','xingú asuriní','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(573,0,NULL,'aso','dano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(574,0,NULL,'asp','algerian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(575,0,NULL,'asq','austrian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(576,0,NULL,'asr','asuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(577,0,NULL,'ass','ipulo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(578,0,NULL,'ast','asturian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(578,0,NULL,'ast','asturleonese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(578,0,NULL,'ast','bable','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(578,0,NULL,'ast','leonese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(579,0,NULL,'asu','tocantins asurini','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(580,0,NULL,'asv','asoa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(581,0,NULL,'asw','australian aborigines sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(582,0,NULL,'asx','muratayak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(583,0,NULL,'asy','yaosakor asmat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(584,0,NULL,'asz','as','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(585,0,NULL,'ata','pele-ata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(586,0,NULL,'atb','zaiwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(587,0,NULL,'atc','atsahuaca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(588,0,NULL,'atd','ata manobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(589,0,NULL,'ate','atemble','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(590,0,NULL,'atg','ivbie north-okpela-arhe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(591,0,NULL,'ath','athapascan languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(592,0,NULL,'ati','attié','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(593,0,NULL,'atj','atikamekw','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(594,0,NULL,'atk','ati','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(595,0,NULL,'atl','mt. iraya agta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(596,0,NULL,'atm','ata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(597,0,NULL,'atn','ashtiani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(598,0,NULL,'ato','atong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(599,0,NULL,'atp','pudtol atta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(600,0,NULL,'atq','aralle-tabulahan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(601,0,NULL,'atr','waimiri-atroari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(602,0,NULL,'ats','gros ventre','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(603,0,NULL,'att','pamplona atta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(604,0,NULL,'atu','reel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(605,0,NULL,'atv','northern altai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(606,0,NULL,'atw','atsugewi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(607,0,NULL,'atx','arutani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(608,0,NULL,'aty','aneityum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(609,0,NULL,'atz','arta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(610,0,NULL,'aua','asumboa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(611,0,NULL,'aub','alugu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(612,0,NULL,'auc','waorani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(613,0,NULL,'aud','anuta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(614,0,NULL,'aue','=/kx''au//''ein','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(615,0,NULL,'auf','arauan languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(616,0,NULL,'aug','aguna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(617,0,NULL,'auh','aushi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(618,0,NULL,'aui','anuki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(619,0,NULL,'auj','awjilah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(620,0,NULL,'auk','heyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(621,0,NULL,'aul','aulua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(622,0,NULL,'aum','asu (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(623,0,NULL,'aun','molmo one','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(624,0,NULL,'auo','auyokawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(625,0,NULL,'aup','makayam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(626,0,NULL,'auq','anus','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(626,0,NULL,'auq','korur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(627,0,NULL,'aur','aruek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(628,0,NULL,'aus','australian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(629,0,NULL,'aut','austral','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(630,0,NULL,'auu','auye','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(631,0,NULL,'auw','awyi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(632,0,NULL,'aux','aurá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(633,0,NULL,'auy','awiyaana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(634,0,NULL,'auz','uzbeki arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(635,0,NULL,'avb','avau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(636,0,NULL,'avd','alviri-vidari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(637,0,NULL,'avi','avikam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(638,0,NULL,'avk','kotava','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(639,0,NULL,'avl','eastern egyptian bedawi arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(640,0,NULL,'avn','avatime','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(641,0,NULL,'avo','agavotaguerra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(642,0,NULL,'avs','aushiri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(643,0,NULL,'avt','au','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(644,0,NULL,'avu','avokaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(645,0,NULL,'avv','avá-canoeiro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(646,0,NULL,'awa','awadhi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(647,0,NULL,'awb','awa (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(648,0,NULL,'awc','cicipu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(649,0,NULL,'awd','arawakan languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(650,0,NULL,'awe','awetí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(651,0,NULL,'awh','awbono','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(652,0,NULL,'awi','aekyom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(653,0,NULL,'awk','awabakal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(654,0,NULL,'awm','arawum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(655,0,NULL,'awn','awngi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(656,0,NULL,'awo','awak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(657,0,NULL,'awr','awera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(658,0,NULL,'aws','south awyu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(659,0,NULL,'awt','araweté','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(660,0,NULL,'awu','central awyu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(661,0,NULL,'awv','jair awyu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(662,0,NULL,'aww','awun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(663,0,NULL,'awx','awara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(664,0,NULL,'awy','edera awyu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(665,0,NULL,'axb','abipon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(666,0,NULL,'axg','mato grosso arára','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(667,0,NULL,'axk','yaka (central african republic)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(668,0,NULL,'axm','middle armenian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(669,0,NULL,'axx','xaragure','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(670,0,NULL,'aya','awar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(671,0,NULL,'ayb','ayizo gbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(672,0,NULL,'ayc','southern aymara','1248825600',NULL,NULL,NULL,NULL,'ay',NULL,NULL); +INSERT INTO "iana_records" VALUES(673,0,NULL,'ayd','ayabadhu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(674,0,NULL,'aye','ayere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(675,0,NULL,'ayg','ginyanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(676,0,NULL,'ayh','hadrami arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(677,0,NULL,'ayi','leyigha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(678,0,NULL,'ayk','akuku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(679,0,NULL,'ayl','libyan arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(680,0,NULL,'ayn','sanaani arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(681,0,NULL,'ayo','ayoreo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(682,0,NULL,'ayp','north mesopotamian arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(683,0,NULL,'ayq','ayi (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(684,0,NULL,'ayr','central aymara','1248825600',NULL,NULL,NULL,NULL,'ay',NULL,NULL); +INSERT INTO "iana_records" VALUES(685,0,NULL,'ays','sorsogon ayta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(686,0,NULL,'ayt','magbukun ayta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(687,0,NULL,'ayu','ayu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(688,0,NULL,'ayx','ayi (china)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(689,0,NULL,'ayy','tayabas ayta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(690,0,NULL,'ayz','mai brat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(691,0,NULL,'aza','azha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(692,0,NULL,'azb','south azerbaijani','1248825600',NULL,NULL,NULL,NULL,'az',NULL,NULL); +INSERT INTO "iana_records" VALUES(693,0,NULL,'azc','uto-aztecan languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(694,0,NULL,'azg','san pedro amuzgos amuzgo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(695,0,NULL,'azj','north azerbaijani','1248825600',NULL,NULL,NULL,NULL,'az',NULL,NULL); +INSERT INTO "iana_records" VALUES(696,0,NULL,'azm','ipalapa amuzgo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(697,0,NULL,'azo','awing','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(698,0,NULL,'azt','faire atta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(699,0,NULL,'azz','highland puebla nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(700,0,NULL,'baa','babatana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(701,0,NULL,'bab','bainouk-gunyuño','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(702,0,NULL,'bac','badui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(703,0,NULL,'bad','banda languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(704,0,NULL,'bae','baré','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(705,0,NULL,'baf','nubaca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(706,0,NULL,'bag','tuki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(707,0,NULL,'bah','bahamas creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(708,0,NULL,'bai','bamileke languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(709,0,NULL,'baj','barakai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(710,0,NULL,'bal','baluchi','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(711,0,NULL,'ban','balinese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(712,0,NULL,'bao','waimaha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(713,0,NULL,'bap','bantawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(714,0,NULL,'bar','bavarian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(715,0,NULL,'bas','basa (cameroon)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(716,0,NULL,'bat','baltic languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(717,0,NULL,'bau','bada (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(718,0,NULL,'bav','vengo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(719,0,NULL,'baw','bambili-bambui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(720,0,NULL,'bax','bamun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(721,0,NULL,'bay','batuley','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(722,0,NULL,'baz','tunen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(723,0,NULL,'bba','baatonum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(724,0,NULL,'bbb','barai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(725,0,NULL,'bbc','batak toba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(726,0,NULL,'bbd','bau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(727,0,NULL,'bbe','bangba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(728,0,NULL,'bbf','baibai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(729,0,NULL,'bbg','barama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(730,0,NULL,'bbh','bugan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(731,0,NULL,'bbi','barombi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(732,0,NULL,'bbj','ghomálá''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(733,0,NULL,'bbk','babanki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(734,0,NULL,'bbl','bats','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(735,0,NULL,'bbm','babango','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(736,0,NULL,'bbn','uneapa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(737,0,NULL,'bbo','konabéré','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(737,0,NULL,'bbo','northern bobo madaré','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(738,0,NULL,'bbp','west central banda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(739,0,NULL,'bbq','bamali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(740,0,NULL,'bbr','girawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(741,0,NULL,'bbs','bakpinka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(742,0,NULL,'bbt','mburku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(743,0,NULL,'bbu','kulung (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(744,0,NULL,'bbv','karnai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(745,0,NULL,'bbw','baba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(746,0,NULL,'bbx','bubia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(747,0,NULL,'bby','befang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(748,0,NULL,'bbz','babalia creole arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(749,0,NULL,'bca','central bai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(750,0,NULL,'bcb','bainouk-samik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(751,0,NULL,'bcc','southern balochi','1248825600',NULL,NULL,NULL,NULL,'bal',NULL,NULL); +INSERT INTO "iana_records" VALUES(752,0,NULL,'bcd','north babar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(753,0,NULL,'bce','bamenyam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(754,0,NULL,'bcf','bamu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(755,0,NULL,'bcg','baga binari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(756,0,NULL,'bch','bariai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(757,0,NULL,'bci','baoulé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(758,0,NULL,'bcj','bardi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(759,0,NULL,'bck','bunaba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(760,0,NULL,'bcl','central bicolano','1248825600',NULL,NULL,NULL,NULL,'bik',NULL,NULL); +INSERT INTO "iana_records" VALUES(761,0,NULL,'bcm','bannoni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(762,0,NULL,'bcn','bali (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(763,0,NULL,'bco','kaluli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(764,0,NULL,'bcp','bali (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(765,0,NULL,'bcq','bench','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(766,0,NULL,'bcr','babine','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(767,0,NULL,'bcs','kohumono','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(768,0,NULL,'bct','bendi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(769,0,NULL,'bcu','awad bing','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(770,0,NULL,'bcv','shoo-minda-nye','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(771,0,NULL,'bcw','bana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(772,0,NULL,'bcy','bacama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(773,0,NULL,'bcz','bainouk-gunyaamolo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(774,0,NULL,'bda','bayot','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(775,0,NULL,'bdb','basap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(776,0,NULL,'bdc','emberá-baudó','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(777,0,NULL,'bdd','bunama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(778,0,NULL,'bde','bade','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(779,0,NULL,'bdf','biage','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(780,0,NULL,'bdg','bonggi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(781,0,NULL,'bdh','baka (sudan)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(782,0,NULL,'bdi','burun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(783,0,NULL,'bdj','bai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(784,0,NULL,'bdk','budukh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(785,0,NULL,'bdl','indonesian bajau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(786,0,NULL,'bdm','buduma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(787,0,NULL,'bdn','baldemu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(788,0,NULL,'bdo','morom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(789,0,NULL,'bdp','bende','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(790,0,NULL,'bdq','bahnar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(791,0,NULL,'bdr','west coast bajau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(792,0,NULL,'bds','burunge','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(793,0,NULL,'bdt','bokoto','1248825600',NULL,NULL,NULL,NULL,'gba',NULL,NULL); +INSERT INTO "iana_records" VALUES(794,0,NULL,'bdu','oroko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(795,0,NULL,'bdv','bodo parja','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(796,0,NULL,'bdw','baham','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(797,0,NULL,'bdx','budong-budong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(798,0,NULL,'bdy','bandjalang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(799,0,NULL,'bdz','badeshi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(800,0,NULL,'bea','beaver','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(801,0,NULL,'beb','bebele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(802,0,NULL,'bec','iceve-maci','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(803,0,NULL,'bed','bedoanas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(804,0,NULL,'bee','byangsi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(805,0,NULL,'bef','benabena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(806,0,NULL,'beg','belait','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(807,0,NULL,'beh','biali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(808,0,NULL,'bei','bekati''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(809,0,NULL,'bej','bedawiyet','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(809,0,NULL,'bej','beja','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(810,0,NULL,'bek','bebeli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(811,0,NULL,'bem','bemba (zambia)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(812,0,NULL,'beo','beami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(813,0,NULL,'bep','besoa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(814,0,NULL,'beq','beembe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(815,0,NULL,'ber','berber languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(816,0,NULL,'bes','besme','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(817,0,NULL,'bet','guiberoua béte','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(818,0,NULL,'beu','blagar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(819,0,NULL,'bev','daloa bété','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(820,0,NULL,'bew','betawi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(821,0,NULL,'bex','jur modo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(822,0,NULL,'bey','beli (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(823,0,NULL,'bez','bena (tanzania)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(824,0,NULL,'bfa','bari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(825,0,NULL,'bfb','pauri bareli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(826,0,NULL,'bfc','northern bai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(827,0,NULL,'bfd','bafut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(828,0,NULL,'bfe','betaf','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(828,0,NULL,'bfe','tena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(829,0,NULL,'bff','bofi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(830,0,NULL,'bfg','busang kayan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(831,0,NULL,'bfh','blafe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(832,0,NULL,'bfi','british sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(833,0,NULL,'bfj','bafanji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(834,0,NULL,'bfk','ban khor sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(835,0,NULL,'bfl','banda-ndélé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(836,0,NULL,'bfm','mmen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(837,0,NULL,'bfn','bunak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(838,0,NULL,'bfo','malba birifor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(839,0,NULL,'bfp','beba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(840,0,NULL,'bfq','badaga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(841,0,NULL,'bfr','bazigar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(842,0,NULL,'bfs','southern bai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(843,0,NULL,'bft','balti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(844,0,NULL,'bfu','gahri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(845,0,NULL,'bfw','bondo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(846,0,NULL,'bfx','bantayanon','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(847,0,NULL,'bfy','bagheli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(848,0,NULL,'bfz','mahasu pahari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(849,0,NULL,'bga','gwamhi-wuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(850,0,NULL,'bgb','bobongko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(851,0,NULL,'bgc','haryanvi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(852,0,NULL,'bgd','rathwi bareli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(853,0,NULL,'bge','bauria','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(854,0,NULL,'bgf','bangandu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(855,0,NULL,'bgg','bugun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(856,0,NULL,'bgi','giangan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(857,0,NULL,'bgj','bangolan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(858,0,NULL,'bgk','bit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(858,0,NULL,'bgk','buxinhua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(859,0,NULL,'bgl','bo (laos)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(860,0,NULL,'bgm','baga mboteni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(861,0,NULL,'bgn','western balochi','1248825600',NULL,NULL,NULL,NULL,'bal',NULL,NULL); +INSERT INTO "iana_records" VALUES(862,0,NULL,'bgo','baga koga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(863,0,NULL,'bgp','eastern balochi','1248825600',NULL,NULL,NULL,NULL,'bal',NULL,NULL); +INSERT INTO "iana_records" VALUES(864,0,NULL,'bgq','bagri','1248825600',NULL,NULL,NULL,NULL,'raj',NULL,NULL); +INSERT INTO "iana_records" VALUES(865,0,NULL,'bgr','bawm chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(866,0,NULL,'bgs','tagabawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(867,0,NULL,'bgt','bughotu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(868,0,NULL,'bgu','mbongno','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(869,0,NULL,'bgv','warkay-bipim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(870,0,NULL,'bgw','bhatri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(871,0,NULL,'bgx','balkan gagauz turkish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(872,0,NULL,'bgy','benggoi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(873,0,NULL,'bgz','banggai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(874,0,NULL,'bha','bharia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(875,0,NULL,'bhb','bhili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(876,0,NULL,'bhc','biga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(877,0,NULL,'bhd','bhadrawahi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(878,0,NULL,'bhe','bhaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(879,0,NULL,'bhf','odiai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(880,0,NULL,'bhg','binandere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(881,0,NULL,'bhh','bukharic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(882,0,NULL,'bhi','bhilali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(883,0,NULL,'bhj','bahing','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(884,0,NULL,'bhk','albay bicolano','1248825600',1268265600,NULL,NULL,NULL,'bik',NULL,'see fbl, lbl, rbl, ubl'); +INSERT INTO "iana_records" VALUES(885,0,NULL,'bhl','bimin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(886,0,NULL,'bhm','bathari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(887,0,NULL,'bhn','bohtan neo-aramaic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(888,0,NULL,'bho','bhojpuri','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(889,0,NULL,'bhp','bima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(890,0,NULL,'bhq','tukang besi south','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(891,0,NULL,'bhr','bara malagasy','1248825600',NULL,NULL,NULL,NULL,'mg',NULL,NULL); +INSERT INTO "iana_records" VALUES(892,0,NULL,'bhs','buwal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(893,0,NULL,'bht','bhattiyali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(894,0,NULL,'bhu','bhunjia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(895,0,NULL,'bhv','bahau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(896,0,NULL,'bhw','biak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(897,0,NULL,'bhx','bhalay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(898,0,NULL,'bhy','bhele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(899,0,NULL,'bhz','bada (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(900,0,NULL,'bia','badimaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(901,0,NULL,'bib','bissa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(902,0,NULL,'bic','bikaru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(903,0,NULL,'bid','bidiyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(904,0,NULL,'bie','bepour','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(905,0,NULL,'bif','biafada','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(906,0,NULL,'big','biangai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(907,0,NULL,'bij','vaghat-ya-bijim-legeri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(908,0,NULL,'bik','bikol','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(909,0,NULL,'bil','bile','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(910,0,NULL,'bim','bimoba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(911,0,NULL,'bin','bini','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(911,0,NULL,'bin','edo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(912,0,NULL,'bio','nai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(913,0,NULL,'bip','bila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(914,0,NULL,'biq','bipi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(915,0,NULL,'bir','bisorio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(916,0,NULL,'bit','berinomo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(917,0,NULL,'biu','biete','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(918,0,NULL,'biv','southern birifor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(919,0,NULL,'biw','kol (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(920,0,NULL,'bix','bijori','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(921,0,NULL,'biy','birhor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(922,0,NULL,'biz','baloi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(923,0,NULL,'bja','budza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(924,0,NULL,'bjb','banggarla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(925,0,NULL,'bjc','bariji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(926,0,NULL,'bjd','bandjigali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(927,0,NULL,'bje','biao-jiao mien','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(928,0,NULL,'bjf','barzani jewish neo-aramaic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(929,0,NULL,'bjg','bidyogo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(930,0,NULL,'bjh','bahinemo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(931,0,NULL,'bji','burji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(932,0,NULL,'bjj','kanauji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(933,0,NULL,'bjk','barok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(934,0,NULL,'bjl','bulu (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(935,0,NULL,'bjm','bajelani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(936,0,NULL,'bjn','banjar','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(937,0,NULL,'bjo','mid-southern banda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(938,0,NULL,'bjq','southern betsimisaraka malagasy','1248825600',NULL,NULL,NULL,NULL,'mg',NULL,NULL); +INSERT INTO "iana_records" VALUES(939,0,NULL,'bjr','binumarien','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(940,0,NULL,'bjs','bajan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(941,0,NULL,'bjt','balanta-ganja','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(942,0,NULL,'bju','busuu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(943,0,NULL,'bjv','bedjond','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(944,0,NULL,'bjw','bakwé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(945,0,NULL,'bjx','banao itneg','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(946,0,NULL,'bjy','bayali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(947,0,NULL,'bjz','baruga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(948,0,NULL,'bka','kyak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(949,0,NULL,'bkb','finallig','1248825600',1268265600,NULL,NULL,NULL,NULL,NULL,'see ebk, obk'); +INSERT INTO "iana_records" VALUES(950,0,NULL,'bkc','baka (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(951,0,NULL,'bkd','binukid','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(951,0,NULL,'bkd','talaandig','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(952,0,NULL,'bkf','beeke','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(953,0,NULL,'bkg','buraka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(954,0,NULL,'bkh','bakoko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(955,0,NULL,'bki','baki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(956,0,NULL,'bkj','pande','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(957,0,NULL,'bkk','brokskat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(958,0,NULL,'bkl','berik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(959,0,NULL,'bkm','kom (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(960,0,NULL,'bkn','bukitan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(961,0,NULL,'bko','kwa''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(962,0,NULL,'bkp','boko (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(963,0,NULL,'bkq','bakairí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(964,0,NULL,'bkr','bakumpai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(965,0,NULL,'bks','northern sorsoganon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(966,0,NULL,'bkt','boloki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(967,0,NULL,'bku','buhid','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(968,0,NULL,'bkv','bekwarra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(969,0,NULL,'bkw','bekwil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(970,0,NULL,'bkx','baikeno','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(971,0,NULL,'bky','bokyi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(972,0,NULL,'bkz','bungku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(973,0,NULL,'bla','siksika','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(974,0,NULL,'blb','bilua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(975,0,NULL,'blc','bella coola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(976,0,NULL,'bld','bolango','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(977,0,NULL,'ble','balanta-kentohe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(978,0,NULL,'blf','buol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(979,0,NULL,'blg','balau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(980,0,NULL,'blh','kuwaa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(981,0,NULL,'bli','bolia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(982,0,NULL,'blj','bolongan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(983,0,NULL,'blk','pa''o karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(984,0,NULL,'bll','biloxi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(985,0,NULL,'blm','beli (sudan)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(986,0,NULL,'bln','southern catanduanes bicolano','1248825600',NULL,NULL,NULL,NULL,'bik',NULL,NULL); +INSERT INTO "iana_records" VALUES(987,0,NULL,'blo','anii','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(988,0,NULL,'blp','blablanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(989,0,NULL,'blq','baluan-pam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(990,0,NULL,'blr','blang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(991,0,NULL,'bls','balaesang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(992,0,NULL,'blt','tai dam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(993,0,NULL,'blv','bolo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(994,0,NULL,'blw','balangao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(995,0,NULL,'blx','mag-indi ayta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(996,0,NULL,'bly','notre','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(997,0,NULL,'blz','balantak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(998,0,NULL,'bma','lame','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(999,0,NULL,'bmb','bembe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1000,0,NULL,'bmc','biem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1001,0,NULL,'bmd','baga manduri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1002,0,NULL,'bme','limassa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1003,0,NULL,'bmf','bom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1004,0,NULL,'bmg','bamwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1005,0,NULL,'bmh','kein','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1006,0,NULL,'bmi','bagirmi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1007,0,NULL,'bmj','bote-majhi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1008,0,NULL,'bmk','ghayavi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1009,0,NULL,'bml','bomboli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1010,0,NULL,'bmm','northern betsimisaraka malagasy','1248825600',NULL,NULL,NULL,NULL,'mg',NULL,NULL); +INSERT INTO "iana_records" VALUES(1011,0,NULL,'bmn','bina (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1012,0,NULL,'bmo','bambalang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1013,0,NULL,'bmp','bulgebi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1014,0,NULL,'bmq','bomu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1015,0,NULL,'bmr','muinane','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1016,0,NULL,'bms','bilma kanuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1017,0,NULL,'bmt','biao mon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1018,0,NULL,'bmu','burum-mindik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1019,0,NULL,'bmv','bum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1020,0,NULL,'bmw','bomwali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1021,0,NULL,'bmx','baimak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1022,0,NULL,'bmy','bemba (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1023,0,NULL,'bmz','baramu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1024,0,NULL,'bna','bonerate','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1025,0,NULL,'bnb','bookan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1026,0,NULL,'bnc','bontok','1248825600',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(1027,0,NULL,'bnd','banda (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1028,0,NULL,'bne','bintauna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1029,0,NULL,'bnf','masiwang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1030,0,NULL,'bng','benga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1031,0,NULL,'bni','bangi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1032,0,NULL,'bnj','eastern tawbuid','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1033,0,NULL,'bnk','bierebo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1034,0,NULL,'bnl','boon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1035,0,NULL,'bnm','batanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1036,0,NULL,'bnn','bunun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1037,0,NULL,'bno','bantoanon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1038,0,NULL,'bnp','bola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1039,0,NULL,'bnq','bantik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1040,0,NULL,'bnr','butmas-tur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1041,0,NULL,'bns','bundeli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1042,0,NULL,'bnt','bantu languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1043,0,NULL,'bnu','bentong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1044,0,NULL,'bnv','beneraf','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1044,0,NULL,'bnv','bonerif','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1044,0,NULL,'bnv','edwas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1045,0,NULL,'bnw','bisis','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1046,0,NULL,'bnx','bangubangu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1047,0,NULL,'bny','bintulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1048,0,NULL,'bnz','beezen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1049,0,NULL,'boa','bora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1050,0,NULL,'bob','aweer','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1051,0,NULL,'boe','mundabli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1052,0,NULL,'bof','bolon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1053,0,NULL,'bog','bamako sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1054,0,NULL,'boh','boma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1055,0,NULL,'boi','barbareño','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1056,0,NULL,'boj','anjam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1057,0,NULL,'bok','bonjo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1058,0,NULL,'bol','bole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1059,0,NULL,'bom','berom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1060,0,NULL,'bon','bine','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1061,0,NULL,'boo','tiemacèwè bozo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1062,0,NULL,'bop','bonkiman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1063,0,NULL,'boq','bogaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1064,0,NULL,'bor','borôro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1065,0,NULL,'bot','bongo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1066,0,NULL,'bou','bondei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1067,0,NULL,'bov','tuwuli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1068,0,NULL,'bow','rema','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1069,0,NULL,'box','buamu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1070,0,NULL,'boy','bodo (central african republic)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1071,0,NULL,'boz','tiéyaxo bozo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1072,0,NULL,'bpa','dakaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1073,0,NULL,'bpb','barbacoas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1074,0,NULL,'bpd','banda-banda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1075,0,NULL,'bpg','bonggo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1076,0,NULL,'bph','botlikh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1077,0,NULL,'bpi','bagupi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1078,0,NULL,'bpj','binji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1079,0,NULL,'bpk','orowe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1080,0,NULL,'bpl','broome pearling lugger pidgin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1081,0,NULL,'bpm','biyom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1082,0,NULL,'bpn','dzao min','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1083,0,NULL,'bpo','anasi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1084,0,NULL,'bpp','kaure','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1085,0,NULL,'bpq','banda malay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1086,0,NULL,'bpr','koronadal blaan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1087,0,NULL,'bps','sarangani blaan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1088,0,NULL,'bpt','barrow point','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1089,0,NULL,'bpu','bongu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1090,0,NULL,'bpv','bian marind','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1091,0,NULL,'bpw','bo (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1092,0,NULL,'bpx','palya bareli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1093,0,NULL,'bpy','bishnupriya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1094,0,NULL,'bpz','bilba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1095,0,NULL,'bqa','tchumbuli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1096,0,NULL,'bqb','bagusa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1097,0,NULL,'bqc','boko (benin)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1098,0,NULL,'bqd','bung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1099,0,NULL,'bqf','baga kaloum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1100,0,NULL,'bqg','bago-kusuntu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1101,0,NULL,'bqh','baima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1102,0,NULL,'bqi','bakhtiari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1103,0,NULL,'bqj','bandial','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1104,0,NULL,'bqk','banda-mbrès','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1105,0,NULL,'bql','bilakura','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1106,0,NULL,'bqm','wumboko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1107,0,NULL,'bqn','bulgarian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1108,0,NULL,'bqo','balo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1109,0,NULL,'bqp','busa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1110,0,NULL,'bqq','biritai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1111,0,NULL,'bqr','burusu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1112,0,NULL,'bqs','bosngun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1113,0,NULL,'bqt','bamukumbit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1114,0,NULL,'bqu','boguru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1115,0,NULL,'bqv','begbere-ejar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1116,0,NULL,'bqw','buru (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1117,0,NULL,'bqx','baangi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1118,0,NULL,'bqy','bengkala sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1119,0,NULL,'bqz','bakaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1120,0,NULL,'bra','braj','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1121,0,NULL,'brb','lave','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1122,0,NULL,'brc','berbice creole dutch','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1123,0,NULL,'brd','baraamu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1124,0,NULL,'brf','bera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1125,0,NULL,'brg','baure','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1126,0,NULL,'brh','brahui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1127,0,NULL,'bri','mokpwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1128,0,NULL,'brj','bieria','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1129,0,NULL,'brk','birked','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1130,0,NULL,'brl','birwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1131,0,NULL,'brm','barambu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1132,0,NULL,'brn','boruca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1133,0,NULL,'bro','brokkat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1134,0,NULL,'brp','barapasi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1135,0,NULL,'brq','breri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1136,0,NULL,'brr','birao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1137,0,NULL,'brs','baras','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1138,0,NULL,'brt','bitare','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1139,0,NULL,'bru','eastern bru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1140,0,NULL,'brv','western bru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1141,0,NULL,'brw','bellari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1142,0,NULL,'brx','bodo (india)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1143,0,NULL,'bry','burui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1144,0,NULL,'brz','bilbil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1145,0,NULL,'bsa','abinomn','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1146,0,NULL,'bsb','brunei bisaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1147,0,NULL,'bsc','bassari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1147,0,NULL,'bsc','oniyan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1148,0,NULL,'bse','wushi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1149,0,NULL,'bsf','bauchi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1150,0,NULL,'bsg','bashkardi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1151,0,NULL,'bsh','kati','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1152,0,NULL,'bsi','bassossi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1153,0,NULL,'bsj','bangwinji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1154,0,NULL,'bsk','burushaski','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1155,0,NULL,'bsl','basa-gumna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1156,0,NULL,'bsm','busami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1157,0,NULL,'bsn','barasana-eduria','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1158,0,NULL,'bso','buso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1159,0,NULL,'bsp','baga sitemu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1160,0,NULL,'bsq','bassa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1161,0,NULL,'bsr','bassa-kontagora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1162,0,NULL,'bss','akoose','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1163,0,NULL,'bst','basketo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1164,0,NULL,'bsu','bahonsuai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1165,0,NULL,'bsv','baga sobané','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1166,0,NULL,'bsw','baiso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1167,0,NULL,'bsx','yangkam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1168,0,NULL,'bsy','sabah bisaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1169,0,NULL,'bta','bata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1170,0,NULL,'btb','beti (cameroon)','1248825600',1268265600,NULL,NULL,NULL,NULL,NULL,'see beb, bum, bxp, eto, ewo, fan, mct'); +INSERT INTO "iana_records" VALUES(1171,0,NULL,'btc','bati (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1172,0,NULL,'btd','batak dairi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1173,0,NULL,'bte','gamo-ningi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1174,0,NULL,'btf','birgit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1175,0,NULL,'btg','gagnoa bété','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1176,0,NULL,'bth','biatah bidayuh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1177,0,NULL,'bti','burate','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1178,0,NULL,'btj','bacanese malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(1179,0,NULL,'btk','batak languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1180,0,NULL,'btl','bhatola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1181,0,NULL,'btm','batak mandailing','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1182,0,NULL,'btn','ratagnon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1183,0,NULL,'bto','rinconada bikol','1248825600',NULL,NULL,NULL,NULL,'bik',NULL,NULL); +INSERT INTO "iana_records" VALUES(1184,0,NULL,'btp','budibud','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1185,0,NULL,'btq','batek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1186,0,NULL,'btr','baetora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1187,0,NULL,'bts','batak simalungun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1188,0,NULL,'btt','bete-bendi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1189,0,NULL,'btu','batu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1190,0,NULL,'btv','bateri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1191,0,NULL,'btw','butuanon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1192,0,NULL,'btx','batak karo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1193,0,NULL,'bty','bobot','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1194,0,NULL,'btz','batak alas-kluet','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1195,0,NULL,'bua','buriat','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(1196,0,NULL,'bub','bua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1197,0,NULL,'buc','bushi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1198,0,NULL,'bud','ntcham','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1199,0,NULL,'bue','beothuk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1200,0,NULL,'buf','bushoong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1201,0,NULL,'bug','buginese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1202,0,NULL,'buh','younuo bunu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1203,0,NULL,'bui','bongili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1204,0,NULL,'buj','basa-gurmana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1205,0,NULL,'buk','bugawac','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1206,0,NULL,'bum','bulu (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1207,0,NULL,'bun','sherbro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1208,0,NULL,'buo','terei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1209,0,NULL,'bup','busoa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1210,0,NULL,'buq','brem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1211,0,NULL,'bus','bokobaru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1212,0,NULL,'but','bungain','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1213,0,NULL,'buu','budu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1214,0,NULL,'buv','bun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1215,0,NULL,'buw','bubi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1216,0,NULL,'bux','boghom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1217,0,NULL,'buy','bullom so','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1218,0,NULL,'buz','bukwen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1219,0,NULL,'bva','barein','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1220,0,NULL,'bvb','bube','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1221,0,NULL,'bvc','baelelea','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1222,0,NULL,'bvd','baeggu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1223,0,NULL,'bve','berau malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(1224,0,NULL,'bvf','boor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1225,0,NULL,'bvg','bonkeng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1226,0,NULL,'bvh','bure','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1227,0,NULL,'bvi','belanda viri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1228,0,NULL,'bvj','baan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1229,0,NULL,'bvk','bukat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1230,0,NULL,'bvl','bolivian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1231,0,NULL,'bvm','bamunka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1232,0,NULL,'bvn','buna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1233,0,NULL,'bvo','bolgo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1234,0,NULL,'bvq','birri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1235,0,NULL,'bvr','burarra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1236,0,NULL,'bvt','bati (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1237,0,NULL,'bvu','bukit malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(1238,0,NULL,'bvv','baniva','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1239,0,NULL,'bvw','boga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1240,0,NULL,'bvx','dibole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1241,0,NULL,'bvy','baybayanon','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1242,0,NULL,'bvz','bauzi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1243,0,NULL,'bwa','bwatoo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1244,0,NULL,'bwb','namosi-naitasiri-serua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1245,0,NULL,'bwc','bwile','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1246,0,NULL,'bwd','bwaidoka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1247,0,NULL,'bwe','bwe karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1248,0,NULL,'bwf','boselewa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1249,0,NULL,'bwg','barwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1250,0,NULL,'bwh','bishuo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1251,0,NULL,'bwi','baniwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1252,0,NULL,'bwj','láá láá bwamu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1253,0,NULL,'bwk','bauwaki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1254,0,NULL,'bwl','bwela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1255,0,NULL,'bwm','biwat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1256,0,NULL,'bwn','wunai bunu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1257,0,NULL,'bwo','borna (ethiopia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1257,0,NULL,'bwo','boro (ethiopia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1258,0,NULL,'bwp','mandobo bawah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1259,0,NULL,'bwq','southern bobo madaré','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1260,0,NULL,'bwr','bura-pabir','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1261,0,NULL,'bws','bomboma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1262,0,NULL,'bwt','bafaw-balong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1263,0,NULL,'bwu','buli (ghana)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1264,0,NULL,'bww','bwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1265,0,NULL,'bwx','bu-nao bunu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1266,0,NULL,'bwy','cwi bwamu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1267,0,NULL,'bwz','bwisi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1268,0,NULL,'bxa','bauro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1269,0,NULL,'bxb','belanda bor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1270,0,NULL,'bxc','molengue','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1271,0,NULL,'bxd','pela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1272,0,NULL,'bxe','birale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1273,0,NULL,'bxf','bilur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1274,0,NULL,'bxg','bangala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1275,0,NULL,'bxh','buhutu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1276,0,NULL,'bxi','pirlatapa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1277,0,NULL,'bxj','bayungu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1278,0,NULL,'bxk','bukusu','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(1278,0,NULL,'bxk','lubukusu','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(1279,0,NULL,'bxl','jalkunan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1280,0,NULL,'bxm','mongolia buriat','1248825600',NULL,NULL,NULL,NULL,'bua',NULL,NULL); +INSERT INTO "iana_records" VALUES(1281,0,NULL,'bxn','burduna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1282,0,NULL,'bxo','barikanchi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1283,0,NULL,'bxp','bebil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1284,0,NULL,'bxq','beele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1285,0,NULL,'bxr','russia buriat','1248825600',NULL,NULL,NULL,NULL,'bua',NULL,NULL); +INSERT INTO "iana_records" VALUES(1286,0,NULL,'bxs','busam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1287,0,NULL,'bxu','china buriat','1248825600',NULL,NULL,NULL,NULL,'bua',NULL,NULL); +INSERT INTO "iana_records" VALUES(1288,0,NULL,'bxv','berakou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1289,0,NULL,'bxw','bankagooma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1290,0,NULL,'bxx','borna (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1291,0,NULL,'bxz','binahari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1292,0,NULL,'bya','batak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1293,0,NULL,'byb','bikya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1294,0,NULL,'byc','ubaghara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1295,0,NULL,'byd','benyadu''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1296,0,NULL,'bye','pouye','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1297,0,NULL,'byf','bete','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1298,0,NULL,'byg','baygo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1299,0,NULL,'byh','bhujel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1300,0,NULL,'byi','buyu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1301,0,NULL,'byj','bina (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1302,0,NULL,'byk','biao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1303,0,NULL,'byl','bayono','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1304,0,NULL,'bym','bidyara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1305,0,NULL,'byn','bilin','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1305,0,NULL,'byn','blin','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1306,0,NULL,'byo','biyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1307,0,NULL,'byp','bumaji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1308,0,NULL,'byq','basay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1309,0,NULL,'byr','baruya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1310,0,NULL,'bys','burak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1311,0,NULL,'byt','berti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1312,0,NULL,'byv','medumba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1313,0,NULL,'byw','belhariya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1314,0,NULL,'byx','qaqet','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1315,0,NULL,'byy','buya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1316,0,NULL,'byz','banaro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1317,0,NULL,'bza','bandi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1318,0,NULL,'bzb','andio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1319,0,NULL,'bzd','bribri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1320,0,NULL,'bze','jenaama bozo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1321,0,NULL,'bzf','boikin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1322,0,NULL,'bzg','babuza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1323,0,NULL,'bzh','mapos buang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1324,0,NULL,'bzi','bisu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1325,0,NULL,'bzj','belize kriol english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1326,0,NULL,'bzk','nicaragua creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1327,0,NULL,'bzl','boano (sulawesi)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1328,0,NULL,'bzm','bolondo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1329,0,NULL,'bzn','boano (maluku)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1330,0,NULL,'bzo','bozaba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1331,0,NULL,'bzp','kemberano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1332,0,NULL,'bzq','buli (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1333,0,NULL,'bzr','biri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1334,0,NULL,'bzs','brazilian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1335,0,NULL,'bzt','brithenig','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1336,0,NULL,'bzu','burmeso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1337,0,NULL,'bzv','bebe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1338,0,NULL,'bzw','basa (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1339,0,NULL,'bzx','hainyaxo bozo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1340,0,NULL,'bzy','obanliku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1341,0,NULL,'bzz','evant','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1342,0,NULL,'caa','chortí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1343,0,NULL,'cab','garifuna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1344,0,NULL,'cac','chuj','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1345,0,NULL,'cad','caddo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1346,0,NULL,'cae','laalaa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1346,0,NULL,'cae','lehar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1347,0,NULL,'caf','southern carrier','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1348,0,NULL,'cag','nivaclé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1349,0,NULL,'cah','cahuarano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1350,0,NULL,'cai','central american indian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1351,0,NULL,'caj','chané','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1352,0,NULL,'cak','cakchiquel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1352,0,NULL,'cak','kaqchikel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1353,0,NULL,'cal','carolinian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1354,0,NULL,'cam','cemuhî','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1355,0,NULL,'can','chambri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1356,0,NULL,'cao','chácobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1357,0,NULL,'cap','chipaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1358,0,NULL,'caq','car nicobarese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1359,0,NULL,'car','galibi carib','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1360,0,NULL,'cas','tsimané','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1361,0,NULL,'cau','caucasian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1362,0,NULL,'cav','cavineña','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1363,0,NULL,'caw','callawalla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1364,0,NULL,'cax','chiquitano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1365,0,NULL,'cay','cayuga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1366,0,NULL,'caz','canichana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1367,0,NULL,'cba','chibchan languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1368,0,NULL,'cbb','cabiyarí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1369,0,NULL,'cbc','carapana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1370,0,NULL,'cbd','carijona','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1371,0,NULL,'cbe','chipiajes','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1372,0,NULL,'cbg','chimila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1373,0,NULL,'cbh','cagua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1374,0,NULL,'cbi','chachi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1375,0,NULL,'cbj','ede cabe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1376,0,NULL,'cbk','chavacano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1377,0,NULL,'cbl','bualkhaw chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1378,0,NULL,'cbn','nyahkur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1379,0,NULL,'cbo','izora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1380,0,NULL,'cbr','cashibo-cacataibo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1381,0,NULL,'cbs','cashinahua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1382,0,NULL,'cbt','chayahuita','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1383,0,NULL,'cbu','candoshi-shapra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1384,0,NULL,'cbv','cacua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1385,0,NULL,'cbw','kinabalian','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1386,0,NULL,'cby','carabayo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1387,0,NULL,'cca','cauca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1388,0,NULL,'ccc','chamicuro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1389,0,NULL,'ccd','cafundo creole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1390,0,NULL,'cce','chopi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1391,0,NULL,'ccg','samba daka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1392,0,NULL,'cch','atsam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1393,0,NULL,'ccj','kasanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1394,0,NULL,'ccl','cutchi-swahili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1395,0,NULL,'ccm','malaccan creole malay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1396,0,NULL,'ccn','north caucasian languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1397,0,NULL,'cco','comaltepec chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1398,0,NULL,'ccp','chakma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1399,0,NULL,'ccq','chaungtha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1400,0,NULL,'ccr','cacaopera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1401,0,NULL,'ccs','south caucasian languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1402,0,NULL,'cda','choni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1403,0,NULL,'cdc','chadic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1404,0,NULL,'cdd','caddoan languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1405,0,NULL,'cde','chenchu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1406,0,NULL,'cdf','chiru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1407,0,NULL,'cdg','chamari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1408,0,NULL,'cdh','chambeali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1409,0,NULL,'cdi','chodri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1410,0,NULL,'cdj','churahi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1411,0,NULL,'cdm','chepang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1412,0,NULL,'cdn','chaudangsi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1413,0,NULL,'cdo','min dong chinese','1248825600',NULL,NULL,NULL,NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(1414,0,NULL,'cdr','cinda-regi-tiyal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1415,0,NULL,'cds','chadian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1416,0,NULL,'cdy','chadong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1417,0,NULL,'cdz','koda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1418,0,NULL,'cea','lower chehalis','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1419,0,NULL,'ceb','cebuano','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1420,0,NULL,'ceg','chamacoco','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1421,0,NULL,'cel','celtic languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1422,0,NULL,'cen','cen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1423,0,NULL,'cet','centúúm','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1424,0,NULL,'cfa','dijim-bwilim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1425,0,NULL,'cfd','cara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1426,0,NULL,'cfg','como karim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1427,0,NULL,'cfm','falam chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1428,0,NULL,'cga','changriwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1429,0,NULL,'cgc','kagayanen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1430,0,NULL,'cgg','chiga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1431,0,NULL,'cgk','chocangacakha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1432,0,NULL,'chb','chibcha','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1433,0,NULL,'chc','catawba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1434,0,NULL,'chd','highland oaxaca chontal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1435,0,NULL,'chf','tabasco chontal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1436,0,NULL,'chg','chagatai','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1437,0,NULL,'chh','chinook','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1438,0,NULL,'chj','ojitlán chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1439,0,NULL,'chk','chuukese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1440,0,NULL,'chl','cahuilla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1441,0,NULL,'chm','mari (russia)','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(1442,0,NULL,'chn','chinook jargon','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1443,0,NULL,'cho','choctaw','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1444,0,NULL,'chp','chipewyan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1444,0,NULL,'chp','dene suline','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1445,0,NULL,'chq','quiotepec chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1446,0,NULL,'chr','cherokee','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1447,0,NULL,'cht','cholón','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1448,0,NULL,'chw','chuwabu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1449,0,NULL,'chx','chantyal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1450,0,NULL,'chy','cheyenne','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1451,0,NULL,'chz','ozumacín chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1452,0,NULL,'cia','cia-cia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1453,0,NULL,'cib','ci gbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1454,0,NULL,'cic','chickasaw','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1455,0,NULL,'cid','chimariko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1456,0,NULL,'cie','cineni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1457,0,NULL,'cih','chinali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1458,0,NULL,'cik','chitkuli kinnauri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1459,0,NULL,'cim','cimbrian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1460,0,NULL,'cin','cinta larga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1461,0,NULL,'cip','chiapanec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1462,0,NULL,'cir','tiri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1463,0,NULL,'ciw','chippewa','1248825600',NULL,NULL,NULL,NULL,'oj',NULL,NULL); +INSERT INTO "iana_records" VALUES(1464,0,NULL,'ciy','chaima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1465,0,NULL,'cja','western cham','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1466,0,NULL,'cje','chru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1467,0,NULL,'cjh','upper chehalis','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1468,0,NULL,'cji','chamalal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1469,0,NULL,'cjk','chokwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1470,0,NULL,'cjm','eastern cham','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1471,0,NULL,'cjn','chenapian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1472,0,NULL,'cjo','ashéninka pajonal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1473,0,NULL,'cjp','cabécar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1474,0,NULL,'cjr','chorotega','1248825600',1268265600,'mom',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1475,0,NULL,'cjs','shor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1476,0,NULL,'cjv','chuave','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1477,0,NULL,'cjy','jinyu chinese','1248825600',NULL,NULL,NULL,NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(1478,0,NULL,'cka','khumi awa chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1479,0,NULL,'ckb','central kurdish','1248825600',NULL,NULL,NULL,NULL,'ku',NULL,NULL); +INSERT INTO "iana_records" VALUES(1480,0,NULL,'ckh','chak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1481,0,NULL,'ckl','cibak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1482,0,NULL,'cko','anufo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1483,0,NULL,'ckq','kajakse','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1484,0,NULL,'ckr','kairak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1485,0,NULL,'cks','tayo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1486,0,NULL,'ckt','chukot','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1487,0,NULL,'cku','koasati','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1488,0,NULL,'ckv','kavalan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1489,0,NULL,'ckx','caka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1490,0,NULL,'cky','cakfem-mushere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1491,0,NULL,'ckz','cakchiquel-quiché mixed language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1492,0,NULL,'cla','ron','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1493,0,NULL,'clc','chilcotin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1494,0,NULL,'cld','chaldean neo-aramaic','1248825600',NULL,NULL,NULL,NULL,'syr',NULL,NULL); +INSERT INTO "iana_records" VALUES(1495,0,NULL,'cle','lealao chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1496,0,NULL,'clh','chilisso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1497,0,NULL,'cli','chakali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1498,0,NULL,'clk','idu-mishmi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1499,0,NULL,'cll','chala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1500,0,NULL,'clm','clallam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1501,0,NULL,'clo','lowland oaxaca chontal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1502,0,NULL,'clu','caluyanun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1503,0,NULL,'clw','chulym','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1504,0,NULL,'cly','eastern highland chatino','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1505,0,NULL,'cma','maa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1506,0,NULL,'cmc','chamic languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1507,0,NULL,'cme','cerma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1508,0,NULL,'cmg','classical mongolian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1509,0,NULL,'cmi','emberá-chamí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1510,0,NULL,'cmk','chimakum','1248825600',1268265600,'xch',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1511,0,NULL,'cml','campalagian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1512,0,NULL,'cmm','michigamea','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1513,0,NULL,'cmn','mandarin chinese','1248825600',NULL,NULL,NULL,NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(1514,0,NULL,'cmo','central mnong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1515,0,NULL,'cmr','mro chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1516,0,NULL,'cms','messapic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1517,0,NULL,'cmt','camtho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1518,0,NULL,'cna','changthang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1519,0,NULL,'cnb','chinbon chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1520,0,NULL,'cnc','côông','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1521,0,NULL,'cng','northern qiang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1522,0,NULL,'cnh','haka chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1523,0,NULL,'cni','asháninka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1524,0,NULL,'cnk','khumi chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1525,0,NULL,'cnl','lalana chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1526,0,NULL,'cno','con','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1527,0,NULL,'cns','central asmat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1528,0,NULL,'cnt','tepetotutla chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1529,0,NULL,'cnu','chenoua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1530,0,NULL,'cnw','ngawn chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1531,0,NULL,'cnx','middle cornish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1532,0,NULL,'coa','cocos islands malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(1533,0,NULL,'cob','chicomuceltec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1534,0,NULL,'coc','cocopa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1535,0,NULL,'cod','cocama-cocamilla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1536,0,NULL,'coe','koreguaje','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1537,0,NULL,'cof','colorado','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1538,0,NULL,'cog','chong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1539,0,NULL,'coh','chichonyi-chidzihana-chikauma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1539,0,NULL,'coh','chonyi-dzihana-kauma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1540,0,NULL,'coj','cochimi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1541,0,NULL,'cok','santa teresa cora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1542,0,NULL,'col','columbia-wenatchi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1543,0,NULL,'com','comanche','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1544,0,NULL,'con','cofán','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1545,0,NULL,'coo','comox','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1546,0,NULL,'cop','coptic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1547,0,NULL,'coq','coquille','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1548,0,NULL,'cot','caquinte','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1549,0,NULL,'cou','wamey','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1550,0,NULL,'cov','cao miao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1551,0,NULL,'cow','cowlitz','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1552,0,NULL,'cox','nanti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1553,0,NULL,'coy','coyaima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1554,0,NULL,'coz','chochotec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1555,0,NULL,'cpa','palantla chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1556,0,NULL,'cpb','ucayali-yurúa ashéninka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1557,0,NULL,'cpc','ajyíninka apurucayali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1558,0,NULL,'cpe','english-based creoles and pidgins','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1559,0,NULL,'cpf','french-based creoles and pidgins','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1560,0,NULL,'cpg','cappadocian greek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1561,0,NULL,'cpi','chinese pidgin english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1562,0,NULL,'cpn','cherepon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1563,0,NULL,'cpp','portuguese-based creoles and pidgins','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1564,0,NULL,'cps','capiznon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1565,0,NULL,'cpu','pichis ashéninka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1566,0,NULL,'cpx','pu-xian chinese','1248825600',NULL,NULL,NULL,NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(1567,0,NULL,'cpy','south ucayali ashéninka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1568,0,NULL,'cqd','chuanqiandian cluster miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(1569,0,NULL,'cqu','chilean quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(1570,0,NULL,'cra','chara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1571,0,NULL,'crb','island carib','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1572,0,NULL,'crc','lonwolwol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1573,0,NULL,'crd','coeur d''alene','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1574,0,NULL,'crf','caramanta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1575,0,NULL,'crg','michif','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1576,0,NULL,'crh','crimean tatar','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1576,0,NULL,'crh','crimean turkish','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1577,0,NULL,'cri','sãotomense','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1578,0,NULL,'crj','southern east cree','1248825600',NULL,NULL,NULL,NULL,'cr',NULL,NULL); +INSERT INTO "iana_records" VALUES(1579,0,NULL,'crk','plains cree','1248825600',NULL,NULL,NULL,NULL,'cr',NULL,NULL); +INSERT INTO "iana_records" VALUES(1580,0,NULL,'crl','northern east cree','1248825600',NULL,NULL,NULL,NULL,'cr',NULL,NULL); +INSERT INTO "iana_records" VALUES(1581,0,NULL,'crm','moose cree','1248825600',NULL,NULL,NULL,NULL,'cr',NULL,NULL); +INSERT INTO "iana_records" VALUES(1582,0,NULL,'crn','el nayar cora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1583,0,NULL,'cro','crow','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1584,0,NULL,'crp','creoles and pidgins','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1585,0,NULL,'crq','iyo''wujwa chorote','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1586,0,NULL,'crr','carolina algonquian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1587,0,NULL,'crs','seselwa creole french','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1588,0,NULL,'crt','iyojwa''ja chorote','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1589,0,NULL,'crv','chaura','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1590,0,NULL,'crw','chrau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1591,0,NULL,'crx','carrier','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1592,0,NULL,'cry','cori','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1593,0,NULL,'crz','cruzeño','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1594,0,NULL,'csa','chiltepec chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1595,0,NULL,'csb','kashubian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1596,0,NULL,'csc','catalan sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1596,0,NULL,'csc','lengua de señas catalana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1596,0,NULL,'csc','llengua de signes catalana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1597,0,NULL,'csd','chiangmai sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1598,0,NULL,'cse','czech sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1599,0,NULL,'csf','cuba sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1600,0,NULL,'csg','chilean sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1601,0,NULL,'csh','asho chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1602,0,NULL,'csi','coast miwok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1603,0,NULL,'csk','jola-kasa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1604,0,NULL,'csl','chinese sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1605,0,NULL,'csm','central sierra miwok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1606,0,NULL,'csn','colombian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1607,0,NULL,'cso','sochiapam chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1607,0,NULL,'cso','sochiapan chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1608,0,NULL,'csq','croatia sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1609,0,NULL,'csr','costa rican sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1610,0,NULL,'css','southern ohlone','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1611,0,NULL,'cst','northern ohlone','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1612,0,NULL,'csu','central sudanic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1613,0,NULL,'csw','swampy cree','1248825600',NULL,NULL,NULL,NULL,'cr',NULL,NULL); +INSERT INTO "iana_records" VALUES(1614,0,NULL,'csy','siyin chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1615,0,NULL,'csz','coos','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1616,0,NULL,'cta','tataltepec chatino','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1617,0,NULL,'ctc','chetco','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1618,0,NULL,'ctd','tedim chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1619,0,NULL,'cte','tepinapa chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1620,0,NULL,'ctg','chittagonian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1621,0,NULL,'ctl','tlacoatzintepec chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1622,0,NULL,'ctm','chitimacha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1623,0,NULL,'ctn','chhintange','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1624,0,NULL,'cto','emberá-catío','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1625,0,NULL,'ctp','western highland chatino','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1626,0,NULL,'cts','northern catanduanes bicolano','1248825600',NULL,NULL,NULL,NULL,'bik',NULL,NULL); +INSERT INTO "iana_records" VALUES(1627,0,NULL,'ctt','wayanad chetti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1628,0,NULL,'ctu','chol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1629,0,NULL,'ctz','zacatepec chatino','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1630,0,NULL,'cua','cua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1631,0,NULL,'cub','cubeo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1632,0,NULL,'cuc','usila chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1633,0,NULL,'cug','cung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1634,0,NULL,'cuh','chuka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1634,0,NULL,'cuh','gichuka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1635,0,NULL,'cui','cuiba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1636,0,NULL,'cuj','mashco piro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1637,0,NULL,'cuk','san blas kuna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1638,0,NULL,'cul','culina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1638,0,NULL,'cul','kulina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1639,0,NULL,'cum','cumeral','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1640,0,NULL,'cuo','cumanagoto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1641,0,NULL,'cup','cupeño','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1642,0,NULL,'cuq','cun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1643,0,NULL,'cur','chhulung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1644,0,NULL,'cus','cushitic languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1645,0,NULL,'cut','teutila cuicatec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1646,0,NULL,'cuu','tai ya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1647,0,NULL,'cuv','cuvok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1648,0,NULL,'cuw','chukwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1649,0,NULL,'cux','tepeuxila cuicatec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1650,0,NULL,'cvg','chug','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1651,0,NULL,'cvn','valle nacional chinantec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1652,0,NULL,'cwa','kabwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1653,0,NULL,'cwb','maindo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1654,0,NULL,'cwd','woods cree','1248825600',NULL,NULL,NULL,NULL,'cr',NULL,NULL); +INSERT INTO "iana_records" VALUES(1655,0,NULL,'cwe','kwere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1656,0,NULL,'cwg','cheq wong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1656,0,NULL,'cwg','chewong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1657,0,NULL,'cwt','kuwaataay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1658,0,NULL,'cya','nopala chatino','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1659,0,NULL,'cyb','cayubaba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1660,0,NULL,'cyo','cuyonon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1661,0,NULL,'czh','huizhou chinese','1248825600',NULL,NULL,NULL,NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(1662,0,NULL,'czk','knaanic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1663,0,NULL,'czn','zenzontepec chatino','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1664,0,NULL,'czo','min zhong chinese','1248825600',NULL,NULL,NULL,NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(1665,0,NULL,'czt','zotung chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1666,0,NULL,'daa','dangaléat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1667,0,NULL,'dac','dambi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1668,0,NULL,'dad','marik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1669,0,NULL,'dae','duupa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1670,0,NULL,'daf','dan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1671,0,NULL,'dag','dagbani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1672,0,NULL,'dah','gwahatike','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1673,0,NULL,'dai','day','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1674,0,NULL,'daj','dar fur daju','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1675,0,NULL,'dak','dakota','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1676,0,NULL,'dal','dahalo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1677,0,NULL,'dam','damakawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1678,0,NULL,'dao','daai chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1679,0,NULL,'dap','nisi (india)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1680,0,NULL,'daq','dandami maria','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1681,0,NULL,'dar','dargwa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1682,0,NULL,'das','daho-doo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1683,0,NULL,'dau','dar sila daju','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1684,0,NULL,'dav','dawida','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1684,0,NULL,'dav','taita','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1685,0,NULL,'daw','davawenyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1686,0,NULL,'dax','dayi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1687,0,NULL,'day','land dayak languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1688,0,NULL,'daz','dao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1689,0,NULL,'dba','bangi me','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1690,0,NULL,'dbb','deno','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1691,0,NULL,'dbd','dadiya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1692,0,NULL,'dbe','dabe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1693,0,NULL,'dbf','edopi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1694,0,NULL,'dbg','dogul dom dogon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1695,0,NULL,'dbi','doka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1696,0,NULL,'dbj','ida''an','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1697,0,NULL,'dbl','dyirbal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1698,0,NULL,'dbm','duguri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1699,0,NULL,'dbn','duriankere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1700,0,NULL,'dbo','dulbu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1701,0,NULL,'dbp','duwai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1702,0,NULL,'dbq','daba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1703,0,NULL,'dbr','dabarre','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1704,0,NULL,'dbu','bondum dom dogon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1705,0,NULL,'dbv','dungu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1706,0,NULL,'dby','dibiyaso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1707,0,NULL,'dcc','deccan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1708,0,NULL,'dcr','negerhollands','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1709,0,NULL,'ddd','dongotono','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1710,0,NULL,'dde','doondo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1711,0,NULL,'ddg','fataluku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1712,0,NULL,'ddi','west goodenough','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1713,0,NULL,'ddj','jaru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1714,0,NULL,'ddn','dendi (benin)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1715,0,NULL,'ddo','dido','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1716,0,NULL,'dds','donno so dogon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1717,0,NULL,'ddw','dawera-daweloor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1718,0,NULL,'dec','dagik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1719,0,NULL,'ded','dedua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1720,0,NULL,'dee','dewoin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1721,0,NULL,'def','dezfuli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1722,0,NULL,'deg','degema','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1723,0,NULL,'deh','dehwari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1724,0,NULL,'dei','demisa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1725,0,NULL,'dek','dek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1726,0,NULL,'del','delaware','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(1727,0,NULL,'dem','dem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1728,0,NULL,'den','slave (athapascan)','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(1729,0,NULL,'dep','pidgin delaware','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1730,0,NULL,'deq','dendi (central african republic)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1731,0,NULL,'der','deori','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1732,0,NULL,'des','desano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1733,0,NULL,'dev','domung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1734,0,NULL,'dez','dengese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1735,0,NULL,'dga','southern dagaare','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1736,0,NULL,'dgb','bunoge dogon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1737,0,NULL,'dgc','casiguran dumagat agta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1738,0,NULL,'dgd','dagaari dioula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1739,0,NULL,'dge','degenan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1740,0,NULL,'dgg','doga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1741,0,NULL,'dgh','dghwede','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1742,0,NULL,'dgi','northern dagara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1743,0,NULL,'dgk','dagba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1744,0,NULL,'dgn','dagoman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1745,0,NULL,'dgo','dogri (individual language)','1248825600',NULL,NULL,NULL,NULL,'doi',NULL,NULL); +INSERT INTO "iana_records" VALUES(1746,0,NULL,'dgr','dogrib','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1747,0,NULL,'dgs','dogoso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1748,0,NULL,'dgu','degaru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1749,0,NULL,'dgx','doghoro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1750,0,NULL,'dgz','daga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1751,0,NULL,'dha','dhanwar (india)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1752,0,NULL,'dhd','dhundari','1248825600',NULL,NULL,NULL,NULL,'mwr',NULL,NULL); +INSERT INTO "iana_records" VALUES(1753,0,NULL,'dhg','dhangu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1754,0,NULL,'dhi','dhimal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1755,0,NULL,'dhl','dhalandji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1756,0,NULL,'dhm','zemba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1757,0,NULL,'dhn','dhanki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1758,0,NULL,'dho','dhodia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1759,0,NULL,'dhr','dhargari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1760,0,NULL,'dhs','dhaiso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1761,0,NULL,'dhu','dhurga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1762,0,NULL,'dhv','dehu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1763,0,NULL,'dhw','dhanwar (nepal)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1764,0,NULL,'dia','dia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1765,0,NULL,'dib','south central dinka','1248825600',NULL,NULL,NULL,NULL,'din',NULL,NULL); +INSERT INTO "iana_records" VALUES(1766,0,NULL,'dic','lakota dida','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1767,0,NULL,'did','didinga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1768,0,NULL,'dif','dieri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1769,0,NULL,'dig','chidigo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1769,0,NULL,'dig','digo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1770,0,NULL,'dih','kumiai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1771,0,NULL,'dii','dimbong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1772,0,NULL,'dij','dai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1773,0,NULL,'dik','southwestern dinka','1248825600',NULL,NULL,NULL,NULL,'din',NULL,NULL); +INSERT INTO "iana_records" VALUES(1774,0,NULL,'dil','dilling','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1775,0,NULL,'dim','dime','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1776,0,NULL,'din','dinka','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(1777,0,NULL,'dio','dibo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1778,0,NULL,'dip','northeastern dinka','1248825600',NULL,NULL,NULL,NULL,'din',NULL,NULL); +INSERT INTO "iana_records" VALUES(1779,0,NULL,'diq','dimli (individual language)','1248825600',NULL,NULL,NULL,NULL,'zza',NULL,NULL); +INSERT INTO "iana_records" VALUES(1780,0,NULL,'dir','dirim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1781,0,NULL,'dis','dimasa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1782,0,NULL,'dit','dirari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1783,0,NULL,'diu','diriku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1784,0,NULL,'diw','northwestern dinka','1248825600',NULL,NULL,NULL,NULL,'din',NULL,NULL); +INSERT INTO "iana_records" VALUES(1785,0,NULL,'dix','dixon reef','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1786,0,NULL,'diy','diuwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1787,0,NULL,'diz','ding','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1788,0,NULL,'djb','djinba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1789,0,NULL,'djc','dar daju daju','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1790,0,NULL,'djd','djamindjung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1791,0,NULL,'dje','zarma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1792,0,NULL,'djf','djangun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1793,0,NULL,'dji','djinang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1794,0,NULL,'djj','djeebbana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1795,0,NULL,'djk','businenge tongo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1795,0,NULL,'djk','eastern maroon creole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1795,0,NULL,'djk','nenge','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1796,0,NULL,'djl','djiwarli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1797,0,NULL,'djm','jamsay dogon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1798,0,NULL,'djn','djauan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1799,0,NULL,'djo','jangkang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1800,0,NULL,'djr','djambarrpuyngu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1801,0,NULL,'dju','kapriman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1802,0,NULL,'djw','djawi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1803,0,NULL,'dka','dakpakha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1804,0,NULL,'dkk','dakka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1805,0,NULL,'dkl','kolum so dogon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1806,0,NULL,'dkr','kuijau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1807,0,NULL,'dks','southeastern dinka','1248825600',NULL,NULL,NULL,NULL,'din',NULL,NULL); +INSERT INTO "iana_records" VALUES(1808,0,NULL,'dkx','mazagway','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1809,0,NULL,'dlg','dolgan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1810,0,NULL,'dlm','dalmatian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1811,0,NULL,'dln','darlong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1812,0,NULL,'dma','duma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1813,0,NULL,'dmc','dimir','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1814,0,NULL,'dme','dugwor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1815,0,NULL,'dmg','upper kinabatangan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1816,0,NULL,'dmk','domaaki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1817,0,NULL,'dml','dameli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1818,0,NULL,'dmm','dama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1819,0,NULL,'dmn','mande languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1820,0,NULL,'dmo','kemezung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1821,0,NULL,'dmr','east damar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1822,0,NULL,'dms','dampelas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1823,0,NULL,'dmu','dubu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1823,0,NULL,'dmu','tebi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1824,0,NULL,'dmv','dumpas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1825,0,NULL,'dmx','dema','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1826,0,NULL,'dmy','demta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1826,0,NULL,'dmy','sowari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1827,0,NULL,'dna','upper grand valley dani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1828,0,NULL,'dnd','daonda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1829,0,NULL,'dne','ndendeule','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1830,0,NULL,'dng','dungan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1831,0,NULL,'dni','lower grand valley dani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1832,0,NULL,'dnk','dengka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1833,0,NULL,'dnn','dzùùngoo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1834,0,NULL,'dnr','danaru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1835,0,NULL,'dnt','mid grand valley dani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1836,0,NULL,'dnu','danau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1837,0,NULL,'dnw','western dani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1838,0,NULL,'dny','dení','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1839,0,NULL,'doa','dom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1840,0,NULL,'dob','dobu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1841,0,NULL,'doc','northern dong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1842,0,NULL,'doe','doe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1843,0,NULL,'dof','domu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1844,0,NULL,'doh','dong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1845,0,NULL,'doi','dogri (macrolanguage)','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(1846,0,NULL,'dok','dondo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1847,0,NULL,'dol','doso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1848,0,NULL,'don','toura (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1849,0,NULL,'doo','dongo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1850,0,NULL,'dop','lukpa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1851,0,NULL,'doq','dominican sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1852,0,NULL,'dor','dori''o','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1853,0,NULL,'dos','dogosé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1854,0,NULL,'dot','dass','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1855,0,NULL,'dov','dombe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1856,0,NULL,'dow','doyayo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1857,0,NULL,'dox','bussa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1858,0,NULL,'doy','dompo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1859,0,NULL,'doz','dorze','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1860,0,NULL,'dpp','papar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1861,0,NULL,'dra','dravidian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1862,0,NULL,'drb','dair','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1863,0,NULL,'drd','darmiya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1864,0,NULL,'dre','dolpo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1865,0,NULL,'drg','rungus','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1866,0,NULL,'drh','darkhat','1248825600',1268265600,'khk',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1867,0,NULL,'dri','c''lela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1868,0,NULL,'drl','darling','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1869,0,NULL,'drn','west damar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1870,0,NULL,'dro','daro-matu melanau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1871,0,NULL,'drq','dura','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1872,0,NULL,'drr','dororo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1873,0,NULL,'drs','gedeo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1874,0,NULL,'drt','drents','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1875,0,NULL,'dru','rukai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1876,0,NULL,'drw','darwazi','1248825600',1268265600,'prs',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1877,0,NULL,'dry','darai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1878,0,NULL,'dsb','lower sorbian','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1879,0,NULL,'dse','dutch sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1880,0,NULL,'dsh','daasanach','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1881,0,NULL,'dsi','disa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1882,0,NULL,'dsl','danish sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1883,0,NULL,'dsn','dusner','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1884,0,NULL,'dso','desiya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1885,0,NULL,'dsq','tadaksahak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1886,0,NULL,'dta','daur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1887,0,NULL,'dtb','labuk-kinabatangan kadazan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1888,0,NULL,'dti','ana tinga dogon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1889,0,NULL,'dtk','tene kan dogon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1890,0,NULL,'dtm','tomo kan dogon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1891,0,NULL,'dtp','central dusun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1892,0,NULL,'dtr','lotud','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1893,0,NULL,'dts','toro so dogon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1894,0,NULL,'dtt','toro tegu dogon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1895,0,NULL,'dtu','tebul ure dogon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1896,0,NULL,'dua','duala','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1897,0,NULL,'dub','dubli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1898,0,NULL,'duc','duna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1899,0,NULL,'dud','hun-saare','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1900,0,NULL,'due','umiray dumaget agta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1901,0,NULL,'duf','dumbea','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1902,0,NULL,'dug','chiduruma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1902,0,NULL,'dug','duruma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1903,0,NULL,'duh','dungra bhil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1904,0,NULL,'dui','dumun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1905,0,NULL,'duj','dhuwal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1906,0,NULL,'duk','duduela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1907,0,NULL,'dul','alabat island agta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1908,0,NULL,'dum','middle dutch (ca. 1050-1350)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1909,0,NULL,'dun','dusun deyah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1910,0,NULL,'duo','dupaninan agta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1911,0,NULL,'dup','duano','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(1912,0,NULL,'duq','dusun malang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1913,0,NULL,'dur','dii','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1914,0,NULL,'dus','dumi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1915,0,NULL,'duu','drung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1916,0,NULL,'duv','duvle','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1917,0,NULL,'duw','dusun witu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1918,0,NULL,'dux','duungooma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1919,0,NULL,'duy','dicamay agta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1920,0,NULL,'duz','duli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1921,0,NULL,'dva','duau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1922,0,NULL,'dwa','diri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1923,0,NULL,'dwl','walo kumbe dogon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1924,0,NULL,'dwr','dawro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1925,0,NULL,'dws','dutton world speedwords','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1926,0,NULL,'dww','dawawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1927,0,NULL,'dya','dyan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1928,0,NULL,'dyb','dyaberdyaber','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1929,0,NULL,'dyd','dyugun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1930,0,NULL,'dyg','villa viciosa agta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1931,0,NULL,'dyi','djimini senoufo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1932,0,NULL,'dym','yanda dom dogon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1933,0,NULL,'dyn','dyangadi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1934,0,NULL,'dyo','jola-fonyi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1935,0,NULL,'dyu','dyula','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1936,0,NULL,'dyy','dyaabugay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1937,0,NULL,'dza','tunzu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1938,0,NULL,'dzd','daza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1939,0,NULL,'dzg','dazaga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1940,0,NULL,'dzl','dzalakha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1941,0,NULL,'dzn','dzando','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1942,0,NULL,'ebg','ebughu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1943,0,NULL,'ebk','eastern bontok','1268265600',NULL,NULL,NULL,NULL,'bnc',NULL,NULL); +INSERT INTO "iana_records" VALUES(1944,0,NULL,'ebo','teke-ebo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1945,0,NULL,'ebr','ebrié','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1946,0,NULL,'ebu','embu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1946,0,NULL,'ebu','kiembu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1947,0,NULL,'ecr','eteocretan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1948,0,NULL,'ecs','ecuadorian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1949,0,NULL,'ecy','eteocypriot','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1950,0,NULL,'eee','e','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1951,0,NULL,'efa','efai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1952,0,NULL,'efe','efe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1953,0,NULL,'efi','efik','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1954,0,NULL,'ega','ega','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1955,0,NULL,'egl','emilian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1956,0,NULL,'ego','eggon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1957,0,NULL,'egx','egyptian languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(1958,0,NULL,'egy','egyptian (ancient)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1959,0,NULL,'ehu','ehueun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1960,0,NULL,'eip','eipomek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1961,0,NULL,'eit','eitiep','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1962,0,NULL,'eiv','askopan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1963,0,NULL,'eja','ejamat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1964,0,NULL,'eka','ekajuk','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1965,0,NULL,'eke','ekit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1966,0,NULL,'ekg','ekari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1967,0,NULL,'eki','eki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1968,0,NULL,'ekk','standard estonian','1248825600',NULL,NULL,NULL,NULL,'et',NULL,NULL); +INSERT INTO "iana_records" VALUES(1969,0,NULL,'ekl','kol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1970,0,NULL,'ekm','elip','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1971,0,NULL,'eko','koti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1972,0,NULL,'ekp','ekpeye','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1973,0,NULL,'ekr','yace','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1974,0,NULL,'eky','eastern kayah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1975,0,NULL,'ele','elepi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1976,0,NULL,'elh','el hugeirat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1977,0,NULL,'eli','nding','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1978,0,NULL,'elk','elkei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1979,0,NULL,'elm','eleme','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1980,0,NULL,'elo','el molo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1981,0,NULL,'elp','elpaputih','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1982,0,NULL,'elu','elu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1983,0,NULL,'elx','elamite','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1984,0,NULL,'ema','emai-iuleha-ora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1985,0,NULL,'emb','embaloh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1986,0,NULL,'eme','emerillon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1987,0,NULL,'emg','eastern meohang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1988,0,NULL,'emi','mussau-emira','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1989,0,NULL,'emk','eastern maninkakan','1248825600',NULL,NULL,NULL,NULL,'man',NULL,NULL); +INSERT INTO "iana_records" VALUES(1990,0,NULL,'emm','mamulique','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1991,0,NULL,'emn','eman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1992,0,NULL,'emo','emok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1993,0,NULL,'emp','northern emberá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1994,0,NULL,'ems','pacific gulf yupik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1995,0,NULL,'emu','eastern muria','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1996,0,NULL,'emw','emplawas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1997,0,NULL,'emx','erromintxela','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1998,0,NULL,'emy','epigraphic mayan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(1999,0,NULL,'ena','apali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2000,0,NULL,'enb','markweeta','1248825600',NULL,NULL,NULL,NULL,'kln',NULL,NULL); +INSERT INTO "iana_records" VALUES(2001,0,NULL,'enc','en','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2002,0,NULL,'end','ende','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2003,0,NULL,'enf','forest enets','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2004,0,NULL,'enh','tundra enets','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2005,0,NULL,'enm','middle english (1100-1500)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2006,0,NULL,'enn','engenni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2007,0,NULL,'eno','enggano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2008,0,NULL,'enq','enga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2009,0,NULL,'enr','emem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2009,0,NULL,'enr','emumu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2010,0,NULL,'enu','enu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2011,0,NULL,'env','enwan (edu state)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2012,0,NULL,'enw','enwan (akwa ibom state)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2013,0,NULL,'eot','beti (côte d''ivoire)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2014,0,NULL,'epi','epie','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2015,0,NULL,'era','eravallan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2016,0,NULL,'erg','sie','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2017,0,NULL,'erh','eruwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2018,0,NULL,'eri','ogea','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2019,0,NULL,'erk','south efate','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2020,0,NULL,'ero','horpa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2021,0,NULL,'err','erre','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2022,0,NULL,'ers','ersu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2023,0,NULL,'ert','eritai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2024,0,NULL,'erw','erokwanas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2025,0,NULL,'ese','ese ejja','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2026,0,NULL,'esh','eshtehardi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2027,0,NULL,'esi','north alaskan inupiatun','1248825600',NULL,NULL,NULL,NULL,'ik',NULL,NULL); +INSERT INTO "iana_records" VALUES(2028,0,NULL,'esk','northwest alaska inupiatun','1248825600',NULL,NULL,NULL,NULL,'ik',NULL,NULL); +INSERT INTO "iana_records" VALUES(2029,0,NULL,'esl','egypt sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2030,0,NULL,'esm','esuma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2031,0,NULL,'esn','salvadoran sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2032,0,NULL,'eso','estonian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2033,0,NULL,'esq','esselen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2034,0,NULL,'ess','central siberian yupik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2035,0,NULL,'esu','central yupik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2036,0,NULL,'esx','eskimo-aleut languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2037,0,NULL,'etb','etebi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2038,0,NULL,'etc','etchemin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2039,0,NULL,'eth','ethiopian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2040,0,NULL,'etn','eton (vanuatu)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2041,0,NULL,'eto','eton (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2042,0,NULL,'etr','edolo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2043,0,NULL,'ets','yekhee','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2044,0,NULL,'ett','etruscan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2045,0,NULL,'etu','ejagham','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2046,0,NULL,'etx','eten','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2047,0,NULL,'etz','semimi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2048,0,NULL,'euq','basque (family)','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2049,0,NULL,'eve','even','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2050,0,NULL,'evh','uvbie','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2051,0,NULL,'evn','evenki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2052,0,NULL,'ewo','ewondo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2053,0,NULL,'ext','extremaduran','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2054,0,NULL,'eya','eyak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2055,0,NULL,'eyo','keiyo','1248825600',NULL,NULL,NULL,NULL,'kln',NULL,NULL); +INSERT INTO "iana_records" VALUES(2056,0,NULL,'eze','uzekwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2057,0,NULL,'faa','fasu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2058,0,NULL,'fab','fa d''ambu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2059,0,NULL,'fad','wagi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2060,0,NULL,'faf','fagani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2061,0,NULL,'fag','finongan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2062,0,NULL,'fah','baissa fali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2063,0,NULL,'fai','faiwol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2064,0,NULL,'faj','faita','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2065,0,NULL,'fak','fang (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2066,0,NULL,'fal','south fali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2067,0,NULL,'fam','fam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2068,0,NULL,'fan','fang (equatorial guinea)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2069,0,NULL,'fap','palor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2070,0,NULL,'far','fataleka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2071,0,NULL,'fat','fanti','1129420800',NULL,NULL,NULL,NULL,'ak',NULL,NULL); +INSERT INTO "iana_records" VALUES(2072,0,NULL,'fau','fayu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2073,0,NULL,'fax','fala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2074,0,NULL,'fay','southwestern fars','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2075,0,NULL,'faz','northwestern fars','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2076,0,NULL,'fbl','west albay bikol','1268265600',NULL,NULL,NULL,NULL,'bik',NULL,NULL); +INSERT INTO "iana_records" VALUES(2077,0,NULL,'fcs','quebec sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2078,0,NULL,'fer','feroge','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2079,0,NULL,'ffi','foia foia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2080,0,NULL,'ffm','maasina fulfulde','1248825600',NULL,NULL,NULL,NULL,'ff',NULL,NULL); +INSERT INTO "iana_records" VALUES(2081,0,NULL,'fgr','fongoro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2082,0,NULL,'fia','nobiin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2083,0,NULL,'fie','fyer','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2084,0,NULL,'fil','filipino','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2084,0,NULL,'fil','pilipino','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2085,0,NULL,'fip','fipa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2086,0,NULL,'fir','firan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2087,0,NULL,'fit','tornedalen finnish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2088,0,NULL,'fiu','finno-ugrian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2089,0,NULL,'fiw','fiwaga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2090,0,NULL,'fkv','kven finnish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2091,0,NULL,'fla','kalispel-pend d''oreille','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2092,0,NULL,'flh','foau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2093,0,NULL,'fli','fali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2094,0,NULL,'fll','north fali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2095,0,NULL,'fln','flinders island','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2096,0,NULL,'flr','fuliiru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2097,0,NULL,'fly','tsotsitaal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2098,0,NULL,'fmp','fe''fe''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2099,0,NULL,'fmu','far western muria','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2100,0,NULL,'fng','fanagalo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2101,0,NULL,'fni','fania','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2102,0,NULL,'fod','foodo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2103,0,NULL,'foi','foi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2104,0,NULL,'fom','foma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2105,0,NULL,'fon','fon','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2106,0,NULL,'for','fore','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2107,0,NULL,'fos','siraya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2108,0,NULL,'fox','formosan languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2109,0,NULL,'fpe','fernando po creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2110,0,NULL,'fqs','fas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2111,0,NULL,'frc','cajun french','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2112,0,NULL,'frd','fordata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2113,0,NULL,'frk','frankish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2114,0,NULL,'frm','middle french (ca. 1400-1600)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2115,0,NULL,'fro','old french (842-ca. 1400)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2116,0,NULL,'frp','arpitan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2116,0,NULL,'frp','francoprovençal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2117,0,NULL,'frq','forak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2118,0,NULL,'frr','northern frisian','1141776000',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2119,0,NULL,'frs','eastern frisian','1141776000',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2120,0,NULL,'frt','fortsenal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2121,0,NULL,'fse','finnish sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2122,0,NULL,'fsl','french sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2123,0,NULL,'fss','finland-swedish sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2123,0,NULL,'fss','finlandssvenskt teckensprÃ¥k','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2123,0,NULL,'fss','suomenruotsalainen viittomakieli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2124,0,NULL,'fub','adamawa fulfulde','1248825600',NULL,NULL,NULL,NULL,'ff',NULL,NULL); +INSERT INTO "iana_records" VALUES(2125,0,NULL,'fuc','pulaar','1248825600',NULL,NULL,NULL,NULL,'ff',NULL,NULL); +INSERT INTO "iana_records" VALUES(2126,0,NULL,'fud','east futuna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2127,0,NULL,'fue','borgu fulfulde','1248825600',NULL,NULL,NULL,NULL,'ff',NULL,NULL); +INSERT INTO "iana_records" VALUES(2128,0,NULL,'fuf','pular','1248825600',NULL,NULL,NULL,NULL,'ff',NULL,NULL); +INSERT INTO "iana_records" VALUES(2129,0,NULL,'fuh','western niger fulfulde','1248825600',NULL,NULL,NULL,NULL,'ff',NULL,NULL); +INSERT INTO "iana_records" VALUES(2130,0,NULL,'fui','bagirmi fulfulde','1248825600',NULL,NULL,NULL,NULL,'ff',NULL,NULL); +INSERT INTO "iana_records" VALUES(2131,0,NULL,'fuj','ko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2132,0,NULL,'fum','fum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2133,0,NULL,'fun','fulniô','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2134,0,NULL,'fuq','central-eastern niger fulfulde','1248825600',NULL,NULL,NULL,NULL,'ff',NULL,NULL); +INSERT INTO "iana_records" VALUES(2135,0,NULL,'fur','friulian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2136,0,NULL,'fut','futuna-aniwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2137,0,NULL,'fuu','furu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2138,0,NULL,'fuv','nigerian fulfulde','1248825600',NULL,NULL,NULL,NULL,'ff',NULL,NULL); +INSERT INTO "iana_records" VALUES(2139,0,NULL,'fuy','fuyug','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2140,0,NULL,'fvr','fur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2141,0,NULL,'fwa','fwâi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2142,0,NULL,'fwe','fwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2143,0,NULL,'gaa','ga','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2144,0,NULL,'gab','gabri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2145,0,NULL,'gac','mixed great andamanese','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2146,0,NULL,'gad','gaddang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2147,0,NULL,'gae','guarequena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2148,0,NULL,'gaf','gende','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2149,0,NULL,'gag','gagauz','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2150,0,NULL,'gah','alekano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2151,0,NULL,'gai','borei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2152,0,NULL,'gaj','gadsup','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2153,0,NULL,'gak','gamkonora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2154,0,NULL,'gal','galoli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2155,0,NULL,'gam','kandawo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2156,0,NULL,'gan','gan chinese','1248825600',NULL,NULL,NULL,NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(2157,0,NULL,'gao','gants','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2158,0,NULL,'gap','gal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2159,0,NULL,'gaq','gata''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2160,0,NULL,'gar','galeya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2161,0,NULL,'gas','adiwasi garasia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2162,0,NULL,'gat','kenati','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2163,0,NULL,'gau','mudhili gadaba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2164,0,NULL,'gav','gabutamon','1248825600',1268265600,'dev',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2165,0,NULL,'gaw','nobonob','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2166,0,NULL,'gax','borana-arsi-guji oromo','1248825600',NULL,NULL,NULL,NULL,'om',NULL,NULL); +INSERT INTO "iana_records" VALUES(2167,0,NULL,'gay','gayo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2168,0,NULL,'gaz','west central oromo','1248825600',NULL,NULL,NULL,NULL,'om',NULL,NULL); +INSERT INTO "iana_records" VALUES(2169,0,NULL,'gba','gbaya (central african republic)','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(2170,0,NULL,'gbb','kaytetye','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2171,0,NULL,'gbc','garawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2172,0,NULL,'gbd','karadjeri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2173,0,NULL,'gbe','niksek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2174,0,NULL,'gbf','gaikundi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2175,0,NULL,'gbg','gbanziri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2176,0,NULL,'gbh','defi gbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2177,0,NULL,'gbi','galela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2178,0,NULL,'gbj','bodo gadaba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2179,0,NULL,'gbk','gaddi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2180,0,NULL,'gbl','gamit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2181,0,NULL,'gbm','garhwali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2182,0,NULL,'gbn','mo''da','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2183,0,NULL,'gbo','northern grebo','1248825600',NULL,NULL,NULL,NULL,'grb',NULL,NULL); +INSERT INTO "iana_records" VALUES(2184,0,NULL,'gbp','gbaya-bossangoa','1248825600',NULL,NULL,NULL,NULL,'gba',NULL,NULL); +INSERT INTO "iana_records" VALUES(2185,0,NULL,'gbq','gbaya-bozoum','1248825600',NULL,NULL,NULL,NULL,'gba',NULL,NULL); +INSERT INTO "iana_records" VALUES(2186,0,NULL,'gbr','gbagyi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2187,0,NULL,'gbs','gbesi gbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2188,0,NULL,'gbu','gagadu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2189,0,NULL,'gbv','gbanu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2190,0,NULL,'gbx','eastern xwla gbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2191,0,NULL,'gby','gbari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2192,0,NULL,'gbz','zoroastrian dari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2193,0,NULL,'gcc','mali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2194,0,NULL,'gcd','ganggalida','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2195,0,NULL,'gce','galice','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2196,0,NULL,'gcf','guadeloupean creole french','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2197,0,NULL,'gcl','grenadian creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2198,0,NULL,'gcn','gaina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2199,0,NULL,'gcr','guianese creole french','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2200,0,NULL,'gct','colonia tovar german','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2201,0,NULL,'gda','gade lohar','1248825600',NULL,NULL,NULL,NULL,'raj',NULL,NULL); +INSERT INTO "iana_records" VALUES(2202,0,NULL,'gdb','pottangi ollar gadaba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2203,0,NULL,'gdc','gugu badhun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2204,0,NULL,'gdd','gedaged','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2205,0,NULL,'gde','gude','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2206,0,NULL,'gdf','guduf-gava','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2207,0,NULL,'gdg','ga''dang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2208,0,NULL,'gdh','gadjerawang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2209,0,NULL,'gdi','gundi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2210,0,NULL,'gdj','gurdjar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2211,0,NULL,'gdk','gadang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2212,0,NULL,'gdl','dirasha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2213,0,NULL,'gdm','laal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2214,0,NULL,'gdn','umanakaina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2215,0,NULL,'gdo','ghodoberi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2216,0,NULL,'gdq','mehri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2217,0,NULL,'gdr','wipi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2218,0,NULL,'gdu','gudu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2219,0,NULL,'gdx','godwari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2220,0,NULL,'gea','geruma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2221,0,NULL,'geb','kire','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2222,0,NULL,'gec','gboloo grebo','1248825600',NULL,NULL,NULL,NULL,'grb',NULL,NULL); +INSERT INTO "iana_records" VALUES(2223,0,NULL,'ged','gade','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2224,0,NULL,'geg','gengle','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2225,0,NULL,'geh','hutterisch','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2225,0,NULL,'geh','hutterite german','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2226,0,NULL,'gei','gebe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2227,0,NULL,'gej','gen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2228,0,NULL,'gek','yiwom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2229,0,NULL,'gel','kag-fer-jiir-koor-ror-us-zuksun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2230,0,NULL,'gem','germanic languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2231,0,NULL,'geq','geme','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2232,0,NULL,'ges','geser-gorom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2233,0,NULL,'gew','gera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2234,0,NULL,'gex','garre','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2235,0,NULL,'gey','enya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2236,0,NULL,'gez','geez','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2237,0,NULL,'gfk','patpatar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2238,0,NULL,'gft','gafat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2239,0,NULL,'gga','gao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2240,0,NULL,'ggb','gbii','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2241,0,NULL,'ggd','gugadj','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2242,0,NULL,'gge','guragone','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2243,0,NULL,'ggg','gurgula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2244,0,NULL,'ggk','kungarakany','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2245,0,NULL,'ggl','ganglau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2246,0,NULL,'ggn','eastern gurung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2247,0,NULL,'ggo','southern gondi','1248825600',NULL,NULL,NULL,NULL,'gon',NULL,NULL); +INSERT INTO "iana_records" VALUES(2248,0,NULL,'ggr','aghu tharnggalu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2249,0,NULL,'ggt','gitua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2250,0,NULL,'ggu','gagu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2251,0,NULL,'ggw','gogodala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2252,0,NULL,'gha','ghadamès','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2253,0,NULL,'ghc','hiberno-scottish gaelic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2254,0,NULL,'ghe','southern ghale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2255,0,NULL,'ghh','northern ghale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2256,0,NULL,'ghk','geko karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2257,0,NULL,'ghl','ghulfan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2258,0,NULL,'ghn','ghanongga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2259,0,NULL,'gho','ghomara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2260,0,NULL,'ghr','ghera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2261,0,NULL,'ghs','guhu-samane','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2262,0,NULL,'ght','kutang ghale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2263,0,NULL,'gia','kitja','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2264,0,NULL,'gib','gibanawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2265,0,NULL,'gic','gail','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2266,0,NULL,'gid','gidar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2267,0,NULL,'gig','goaria','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2268,0,NULL,'gil','gilbertese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2269,0,NULL,'gim','gimi (eastern highlands)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2270,0,NULL,'gin','hinukh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2271,0,NULL,'gio','gelao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2272,0,NULL,'gip','gimi (west new britain)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2273,0,NULL,'giq','green gelao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2274,0,NULL,'gir','red gelao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2275,0,NULL,'gis','north giziga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2276,0,NULL,'git','gitxsan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2277,0,NULL,'giw','white gelao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2278,0,NULL,'gix','gilima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2279,0,NULL,'giy','giyug','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2280,0,NULL,'giz','south giziga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2281,0,NULL,'gji','geji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2282,0,NULL,'gjk','kachi koli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2283,0,NULL,'gjn','gonja','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2284,0,NULL,'gju','gujari','1248825600',NULL,NULL,NULL,NULL,'raj',NULL,NULL); +INSERT INTO "iana_records" VALUES(2285,0,NULL,'gka','guya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2286,0,NULL,'gke','ndai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2287,0,NULL,'gkn','gokana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2288,0,NULL,'gkp','guinea kpelle','1248825600',NULL,NULL,NULL,NULL,'kpe',NULL,NULL); +INSERT INTO "iana_records" VALUES(2289,0,NULL,'glc','bon gula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2290,0,NULL,'gld','nanai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2291,0,NULL,'glh','northwest pashayi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2292,0,NULL,'gli','guliguli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2293,0,NULL,'glj','gula iro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2294,0,NULL,'glk','gilaki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2295,0,NULL,'glo','galambu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2296,0,NULL,'glr','glaro-twabo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2297,0,NULL,'glu','gula (chad)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2298,0,NULL,'glw','glavda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2299,0,NULL,'gly','gule','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2300,0,NULL,'gma','gambera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2301,0,NULL,'gmb','gula''alaa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2302,0,NULL,'gmd','mághdì','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2303,0,NULL,'gme','east germanic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2304,0,NULL,'gmh','middle high german (ca. 1050-1500)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2305,0,NULL,'gml','middle low german','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2306,0,NULL,'gmm','gbaya-mbodomo','1248825600',NULL,NULL,NULL,NULL,'gba',NULL,NULL); +INSERT INTO "iana_records" VALUES(2307,0,NULL,'gmn','gimnime','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2308,0,NULL,'gmq','north germanic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2309,0,NULL,'gmu','gumalu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2310,0,NULL,'gmv','gamo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2311,0,NULL,'gmw','west germanic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2312,0,NULL,'gmx','magoma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2313,0,NULL,'gmy','mycenaean greek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2314,0,NULL,'gna','kaansa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2315,0,NULL,'gnb','gangte','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2316,0,NULL,'gnc','guanche','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2317,0,NULL,'gnd','zulgo-gemzek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2318,0,NULL,'gne','ganang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2319,0,NULL,'gng','ngangam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2320,0,NULL,'gnh','lere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2321,0,NULL,'gni','gooniyandi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2322,0,NULL,'gnk','//gana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2323,0,NULL,'gnl','gangulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2324,0,NULL,'gnm','ginuman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2325,0,NULL,'gnn','gumatj','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2326,0,NULL,'gno','northern gondi','1248825600',NULL,NULL,NULL,NULL,'gon',NULL,NULL); +INSERT INTO "iana_records" VALUES(2327,0,NULL,'gnq','gana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2328,0,NULL,'gnr','gureng gureng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2329,0,NULL,'gnt','guntai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2330,0,NULL,'gnu','gnau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2331,0,NULL,'gnw','western bolivian guaraní','1248825600',NULL,NULL,NULL,NULL,'gn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2332,0,NULL,'gnz','ganzi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2333,0,NULL,'goa','guro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2334,0,NULL,'gob','playero','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2335,0,NULL,'goc','gorakor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2336,0,NULL,'god','godié','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2337,0,NULL,'goe','gongduk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2338,0,NULL,'gof','gofa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2339,0,NULL,'gog','gogo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2340,0,NULL,'goh','old high german (ca. 750-1050)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2341,0,NULL,'goi','gobasi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2342,0,NULL,'goj','gowlan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2343,0,NULL,'gok','gowli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2344,0,NULL,'gol','gola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2345,0,NULL,'gom','goan konkani','1248825600',NULL,NULL,NULL,NULL,'kok',NULL,NULL); +INSERT INTO "iana_records" VALUES(2346,0,NULL,'gon','gondi','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(2347,0,NULL,'goo','gone dau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2348,0,NULL,'gop','yeretuar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2349,0,NULL,'goq','gorap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2350,0,NULL,'gor','gorontalo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2351,0,NULL,'gos','gronings','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2352,0,NULL,'got','gothic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2353,0,NULL,'gou','gavar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2354,0,NULL,'gow','gorowa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2355,0,NULL,'gox','gobu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2356,0,NULL,'goy','goundo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2357,0,NULL,'goz','gozarkhani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2358,0,NULL,'gpa','gupa-abawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2359,0,NULL,'gpn','taiap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2360,0,NULL,'gqa','ga''anda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2361,0,NULL,'gqi','guiqiong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2362,0,NULL,'gqn','guana (brazil)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2363,0,NULL,'gqr','gor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2364,0,NULL,'gra','rajput garasia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2365,0,NULL,'grb','grebo','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(2366,0,NULL,'grc','ancient greek (to 1453)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2367,0,NULL,'grd','guruntum-mbaaru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2368,0,NULL,'grg','madi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2369,0,NULL,'grh','gbiri-niragu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2370,0,NULL,'gri','ghari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2371,0,NULL,'grj','southern grebo','1248825600',NULL,NULL,NULL,NULL,'grb',NULL,NULL); +INSERT INTO "iana_records" VALUES(2372,0,NULL,'grk','greek languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2373,0,NULL,'grm','kota marudu talantang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2374,0,NULL,'gro','groma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2375,0,NULL,'grq','gorovu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2376,0,NULL,'grr','taznatit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2377,0,NULL,'grs','gresi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2378,0,NULL,'grt','garo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2379,0,NULL,'gru','kistane','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2380,0,NULL,'grv','central grebo','1248825600',NULL,NULL,NULL,NULL,'grb',NULL,NULL); +INSERT INTO "iana_records" VALUES(2381,0,NULL,'grw','gweda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2382,0,NULL,'grx','guriaso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2383,0,NULL,'gry','barclayville grebo','1248825600',NULL,NULL,NULL,NULL,'grb',NULL,NULL); +INSERT INTO "iana_records" VALUES(2384,0,NULL,'grz','guramalum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2385,0,NULL,'gse','ghanaian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2386,0,NULL,'gsg','german sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2387,0,NULL,'gsl','gusilay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2388,0,NULL,'gsm','guatemalan sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2389,0,NULL,'gsn','gusan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2390,0,NULL,'gso','southwest gbaya','1248825600',NULL,NULL,NULL,NULL,'gba',NULL,NULL); +INSERT INTO "iana_records" VALUES(2391,0,NULL,'gsp','wasembo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2392,0,NULL,'gss','greek sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2393,0,NULL,'gsw','alemannic','1141776000',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2393,0,NULL,'gsw','alsatian','1141776000',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2393,0,NULL,'gsw','swiss german','1141776000',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2394,0,NULL,'gta','guató','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2395,0,NULL,'gti','gbati-ri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2396,0,NULL,'gua','shiki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2397,0,NULL,'gub','guajajára','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2398,0,NULL,'guc','wayuu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2399,0,NULL,'gud','yocoboué dida','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2400,0,NULL,'gue','gurinji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2401,0,NULL,'guf','gupapuyngu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2402,0,NULL,'gug','paraguayan guaraní','1248825600',NULL,NULL,NULL,NULL,'gn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2403,0,NULL,'guh','guahibo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2404,0,NULL,'gui','eastern bolivian guaraní','1248825600',NULL,NULL,NULL,NULL,'gn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2405,0,NULL,'guk','gumuz','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2406,0,NULL,'gul','sea island creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2407,0,NULL,'gum','guambiano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2408,0,NULL,'gun','mbyá guaraní','1248825600',NULL,NULL,NULL,NULL,'gn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2409,0,NULL,'guo','guayabero','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2410,0,NULL,'gup','gunwinggu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2411,0,NULL,'guq','aché','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2412,0,NULL,'gur','farefare','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2413,0,NULL,'gus','guinean sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2414,0,NULL,'gut','maléku jaíka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2415,0,NULL,'guu','yanomamö','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2416,0,NULL,'guv','gey','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2417,0,NULL,'guw','gun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2418,0,NULL,'gux','gourmanchéma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2419,0,NULL,'guz','ekegusii','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2419,0,NULL,'guz','gusii','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2420,0,NULL,'gva','guana (paraguay)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2421,0,NULL,'gvc','guanano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2422,0,NULL,'gve','duwet','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2423,0,NULL,'gvf','golin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2424,0,NULL,'gvj','guajá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2425,0,NULL,'gvl','gulay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2426,0,NULL,'gvm','gurmana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2427,0,NULL,'gvn','kuku-yalanji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2428,0,NULL,'gvo','gavião do jiparaná','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2429,0,NULL,'gvp','pará gavião','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2430,0,NULL,'gvr','western gurung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2431,0,NULL,'gvs','gumawana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2432,0,NULL,'gvy','guyani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2433,0,NULL,'gwa','mbato','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2434,0,NULL,'gwb','gwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2435,0,NULL,'gwc','kalami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2436,0,NULL,'gwd','gawwada','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2437,0,NULL,'gwe','gweno','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2438,0,NULL,'gwf','gowro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2439,0,NULL,'gwg','moo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2440,0,NULL,'gwi','gwichʼin','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2441,0,NULL,'gwj','/gwi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2442,0,NULL,'gwn','gwandara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2443,0,NULL,'gwr','gwere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2444,0,NULL,'gwt','gawar-bati','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2445,0,NULL,'gwu','guwamu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2446,0,NULL,'gww','kwini','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2447,0,NULL,'gwx','gua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2448,0,NULL,'gxx','wè southern','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2449,0,NULL,'gya','northwest gbaya','1248825600',NULL,NULL,NULL,NULL,'gba',NULL,NULL); +INSERT INTO "iana_records" VALUES(2450,0,NULL,'gyb','garus','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2451,0,NULL,'gyd','kayardild','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2452,0,NULL,'gye','gyem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2453,0,NULL,'gyf','gungabula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2454,0,NULL,'gyg','gbayi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2455,0,NULL,'gyi','gyele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2456,0,NULL,'gyl','gayil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2457,0,NULL,'gym','ngäbere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2458,0,NULL,'gyn','guyanese creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2459,0,NULL,'gyr','guarayu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2460,0,NULL,'gyy','gunya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2461,0,NULL,'gza','ganza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2462,0,NULL,'gzi','gazi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2463,0,NULL,'gzn','gane','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2464,0,NULL,'haa','han','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2465,0,NULL,'hab','hanoi sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2466,0,NULL,'hac','gurani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2467,0,NULL,'had','hatam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2468,0,NULL,'hae','eastern oromo','1248825600',NULL,NULL,NULL,NULL,'om',NULL,NULL); +INSERT INTO "iana_records" VALUES(2469,0,NULL,'haf','haiphong sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2470,0,NULL,'hag','hanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2471,0,NULL,'hah','hahon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2472,0,NULL,'hai','haida','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(2473,0,NULL,'haj','hajong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2474,0,NULL,'hak','hakka chinese','1248825600',NULL,NULL,NULL,NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(2475,0,NULL,'hal','halang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2476,0,NULL,'ham','hewa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2477,0,NULL,'han','hangaza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2478,0,NULL,'hao','hakö','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2479,0,NULL,'hap','hupla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2480,0,NULL,'haq','ha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2481,0,NULL,'har','harari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2482,0,NULL,'has','haisla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2483,0,NULL,'hav','havu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2484,0,NULL,'haw','hawaiian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2485,0,NULL,'hax','southern haida','1248825600',NULL,NULL,NULL,NULL,'hai',NULL,NULL); +INSERT INTO "iana_records" VALUES(2486,0,NULL,'hay','haya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2487,0,NULL,'haz','hazaragi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2488,0,NULL,'hba','hamba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2489,0,NULL,'hbb','huba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2490,0,NULL,'hbn','heiban','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2491,0,NULL,'hbo','ancient hebrew','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2492,0,NULL,'hbu','habu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2493,0,NULL,'hca','andaman creole hindi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2494,0,NULL,'hch','huichol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2495,0,NULL,'hdn','northern haida','1248825600',NULL,NULL,NULL,NULL,'hai',NULL,NULL); +INSERT INTO "iana_records" VALUES(2496,0,NULL,'hds','honduras sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2497,0,NULL,'hdy','hadiyya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2498,0,NULL,'hea','northern qiandong miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2499,0,NULL,'hed','herdé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2500,0,NULL,'heg','helong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2501,0,NULL,'heh','hehe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2502,0,NULL,'hei','heiltsuk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2503,0,NULL,'hem','hemba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2504,0,NULL,'hgm','hai//om','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2505,0,NULL,'hgw','haigwai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2506,0,NULL,'hhi','hoia hoia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2507,0,NULL,'hhr','kerak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2508,0,NULL,'hhy','hoyahoya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2509,0,NULL,'hia','lamang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2510,0,NULL,'hib','hibito','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2511,0,NULL,'hid','hidatsa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2512,0,NULL,'hif','fiji hindi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2513,0,NULL,'hig','kamwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2514,0,NULL,'hih','pamosu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2515,0,NULL,'hii','hinduri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2516,0,NULL,'hij','hijuk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2517,0,NULL,'hik','seit-kaitetu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2518,0,NULL,'hil','hiligaynon','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2519,0,NULL,'him','himachali languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2519,0,NULL,'him','western pahari languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2520,0,NULL,'hio','tsoa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2521,0,NULL,'hir','himarimã','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2522,0,NULL,'hit','hittite','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2523,0,NULL,'hiw','hiw','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2524,0,NULL,'hix','hixkaryána','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2525,0,NULL,'hji','haji','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(2526,0,NULL,'hka','kahe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2527,0,NULL,'hke','hunde','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2528,0,NULL,'hkk','hunjara-kaina ke','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2529,0,NULL,'hks','heung kong sau yue','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2529,0,NULL,'hks','hong kong sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2530,0,NULL,'hla','halia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2531,0,NULL,'hlb','halbi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2532,0,NULL,'hld','halang doan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2533,0,NULL,'hle','hlersu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2534,0,NULL,'hlt','nga la','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2535,0,NULL,'hlu','hieroglyphic luwian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2536,0,NULL,'hma','southern mashan hmong','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2536,0,NULL,'hma','southern mashan miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2537,0,NULL,'hmb','humburi senni songhay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2538,0,NULL,'hmc','central huishui hmong','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2538,0,NULL,'hmc','central huishui miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2539,0,NULL,'hmd','a-hmaos','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2539,0,NULL,'hmd','da-hua miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2539,0,NULL,'hmd','large flowery miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2540,0,NULL,'hme','eastern huishui hmong','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2540,0,NULL,'hme','eastern huishui miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2541,0,NULL,'hmf','hmong don','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2542,0,NULL,'hmg','southwestern guiyang hmong','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2543,0,NULL,'hmh','southwestern huishui hmong','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2543,0,NULL,'hmh','southwestern huishui miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2544,0,NULL,'hmi','northern huishui hmong','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2544,0,NULL,'hmi','northern huishui miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2545,0,NULL,'hmj','ge','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2545,0,NULL,'hmj','gejia','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2546,0,NULL,'hmk','maek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2547,0,NULL,'hml','luopohe hmong','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2547,0,NULL,'hml','luopohe miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2548,0,NULL,'hmm','central mashan hmong','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2548,0,NULL,'hmm','central mashan miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2549,0,NULL,'hmn','hmong','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(2549,0,NULL,'hmn','mong','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(2550,0,NULL,'hmp','northern mashan hmong','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2550,0,NULL,'hmp','northern mashan miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2551,0,NULL,'hmq','eastern qiandong miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2552,0,NULL,'hmr','hmar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2553,0,NULL,'hms','southern qiandong miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2554,0,NULL,'hmt','hamtai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2555,0,NULL,'hmu','hamap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2556,0,NULL,'hmv','hmong dô','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2557,0,NULL,'hmw','western mashan hmong','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2557,0,NULL,'hmw','western mashan miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2558,0,NULL,'hmx','hmong-mien languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2559,0,NULL,'hmy','southern guiyang hmong','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2559,0,NULL,'hmy','southern guiyang miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2560,0,NULL,'hmz','hmong shua','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2560,0,NULL,'hmz','sinicized miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2561,0,NULL,'hna','mina (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2562,0,NULL,'hnd','southern hindko','1248825600',NULL,NULL,NULL,NULL,'lah',NULL,NULL); +INSERT INTO "iana_records" VALUES(2563,0,NULL,'hne','chhattisgarhi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2564,0,NULL,'hnh','//ani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2565,0,NULL,'hni','hani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2566,0,NULL,'hnj','hmong njua','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2566,0,NULL,'hnj','mong leng','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2566,0,NULL,'hnj','mong njua','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2567,0,NULL,'hnn','hanunoo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2568,0,NULL,'hno','northern hindko','1248825600',NULL,NULL,NULL,NULL,'lah',NULL,NULL); +INSERT INTO "iana_records" VALUES(2569,0,NULL,'hns','caribbean hindustani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2570,0,NULL,'hnu','hung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2571,0,NULL,'hoa','hoava','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2572,0,NULL,'hob','mari (madang province)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2573,0,NULL,'hoc','ho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2574,0,NULL,'hod','holma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2575,0,NULL,'hoe','horom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2576,0,NULL,'hoh','hobyót','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2577,0,NULL,'hoi','holikachuk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2578,0,NULL,'hoj','hadothi','1248825600',NULL,NULL,NULL,NULL,'raj',NULL,NULL); +INSERT INTO "iana_records" VALUES(2579,0,NULL,'hok','hokan languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2580,0,NULL,'hol','holu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2581,0,NULL,'hom','homa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2582,0,NULL,'hoo','holoholo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2583,0,NULL,'hop','hopi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2584,0,NULL,'hor','horo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2585,0,NULL,'hos','ho chi minh city sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2586,0,NULL,'hot','hote','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2586,0,NULL,'hot','malê','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2587,0,NULL,'hov','hovongan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2588,0,NULL,'how','honi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2589,0,NULL,'hoy','holiya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2590,0,NULL,'hoz','hozo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2591,0,NULL,'hpo','hpon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2592,0,NULL,'hps','hawai''i pidgin sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2593,0,NULL,'hra','hrangkhol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2594,0,NULL,'hre','hre','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2595,0,NULL,'hrk','haruku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2596,0,NULL,'hrm','horned miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2597,0,NULL,'hro','haroi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2598,0,NULL,'hrr','horuru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2599,0,NULL,'hrt','hértevin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2600,0,NULL,'hru','hruso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2601,0,NULL,'hrx','hunsrik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2602,0,NULL,'hrz','harzani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2603,0,NULL,'hsb','upper sorbian','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2604,0,NULL,'hsh','hungarian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2605,0,NULL,'hsl','hausa sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2606,0,NULL,'hsn','xiang chinese','1248825600',NULL,NULL,NULL,NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(2607,0,NULL,'hss','harsusi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2608,0,NULL,'hti','hoti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2609,0,NULL,'hto','minica huitoto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2610,0,NULL,'hts','hadza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2611,0,NULL,'htu','hitu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2612,0,NULL,'htx','middle hittite','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2613,0,NULL,'hub','huambisa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2614,0,NULL,'huc','=/hua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2615,0,NULL,'hud','huaulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2616,0,NULL,'hue','san francisco del mar huave','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2617,0,NULL,'huf','humene','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2618,0,NULL,'hug','huachipaeri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2619,0,NULL,'huh','huilliche','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2620,0,NULL,'hui','huli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2621,0,NULL,'huj','northern guiyang hmong','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2621,0,NULL,'huj','northern guiyang miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(2622,0,NULL,'huk','hulung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2623,0,NULL,'hul','hula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2624,0,NULL,'hum','hungana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2625,0,NULL,'huo','hu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2626,0,NULL,'hup','hupa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2627,0,NULL,'huq','tsat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2628,0,NULL,'hur','halkomelem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2629,0,NULL,'hus','huastec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2630,0,NULL,'hut','humla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2631,0,NULL,'huu','murui huitoto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2632,0,NULL,'huv','san mateo del mar huave','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2633,0,NULL,'huw','hukumina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2634,0,NULL,'hux','nüpode huitoto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2635,0,NULL,'huy','hulaulá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2636,0,NULL,'huz','hunzib','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2637,0,NULL,'hvc','haitian vodoun culture language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2638,0,NULL,'hve','san dionisio del mar huave','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2639,0,NULL,'hvk','haveke','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2640,0,NULL,'hvn','sabu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2641,0,NULL,'hvv','santa maría del mar huave','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2642,0,NULL,'hwa','wané','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2643,0,NULL,'hwc','hawai''i creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2644,0,NULL,'hwo','hwana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2645,0,NULL,'hya','hya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2646,0,NULL,'hyx','armenian (family)','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2647,0,NULL,'iai','iaai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2648,0,NULL,'ian','iatmul','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2649,0,NULL,'iap','iapama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2650,0,NULL,'iar','purari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2651,0,NULL,'iba','iban','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2652,0,NULL,'ibb','ibibio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2653,0,NULL,'ibd','iwaidja','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2654,0,NULL,'ibe','akpes','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2655,0,NULL,'ibg','ibanag','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2656,0,NULL,'ibi','ibilo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2657,0,NULL,'ibl','ibaloi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2658,0,NULL,'ibm','agoi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2659,0,NULL,'ibn','ibino','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2660,0,NULL,'ibr','ibuoro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2661,0,NULL,'ibu','ibu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2662,0,NULL,'iby','ibani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2663,0,NULL,'ica','ede ica','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2664,0,NULL,'ich','etkywan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2665,0,NULL,'icl','icelandic sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2666,0,NULL,'icr','islander creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2667,0,NULL,'ida','idakho-isukha-tiriki','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(2667,0,NULL,'ida','luidakho-luisukha-lutirichi','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(2668,0,NULL,'idb','indo-portuguese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2669,0,NULL,'idc','idon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2670,0,NULL,'idd','ede idaca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2671,0,NULL,'ide','idere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2672,0,NULL,'idi','idi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2673,0,NULL,'idr','indri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2674,0,NULL,'ids','idesa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2675,0,NULL,'idt','idaté','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2676,0,NULL,'idu','idoma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2677,0,NULL,'ifa','amganad ifugao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2678,0,NULL,'ifb','ayangan ifugao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2678,0,NULL,'ifb','batad ifugao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2679,0,NULL,'ife','ifè','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2680,0,NULL,'iff','ifo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2681,0,NULL,'ifk','tuwali ifugao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2682,0,NULL,'ifm','teke-fuumu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2683,0,NULL,'ifu','mayoyao ifugao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2684,0,NULL,'ify','keley-i kallahan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2685,0,NULL,'igb','ebira','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2686,0,NULL,'ige','igede','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2687,0,NULL,'igg','igana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2688,0,NULL,'igl','igala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2689,0,NULL,'igm','kanggape','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2690,0,NULL,'ign','ignaciano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2691,0,NULL,'igo','isebe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2692,0,NULL,'igs','interglossa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2693,0,NULL,'igw','igwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2694,0,NULL,'ihb','iha based pidgin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2695,0,NULL,'ihi','ihievbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2696,0,NULL,'ihp','iha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2697,0,NULL,'iir','indo-iranian languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2698,0,NULL,'ijc','izon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2699,0,NULL,'ije','biseni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2700,0,NULL,'ijj','ede ije','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2701,0,NULL,'ijn','kalabari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2702,0,NULL,'ijo','ijo languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2703,0,NULL,'ijs','southeast ijo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2704,0,NULL,'ike','eastern canadian inuktitut','1248825600',NULL,NULL,NULL,NULL,'iu',NULL,NULL); +INSERT INTO "iana_records" VALUES(2705,0,NULL,'iki','iko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2706,0,NULL,'ikk','ika','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2707,0,NULL,'ikl','ikulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2708,0,NULL,'iko','olulumo-ikom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2709,0,NULL,'ikp','ikpeshi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2710,0,NULL,'ikt','western canadian inuktitut','1248825600',NULL,NULL,NULL,NULL,'iu',NULL,NULL); +INSERT INTO "iana_records" VALUES(2711,0,NULL,'ikv','iku-gora-ankwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2712,0,NULL,'ikw','ikwere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2713,0,NULL,'ikx','ik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2714,0,NULL,'ikz','ikizu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2715,0,NULL,'ila','ile ape','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2716,0,NULL,'ilb','ila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2717,0,NULL,'ilg','garig-ilgar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2718,0,NULL,'ili','ili turki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2719,0,NULL,'ilk','ilongot','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2720,0,NULL,'ill','iranun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2721,0,NULL,'ilo','iloko','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2722,0,NULL,'ils','international sign','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2723,0,NULL,'ilu','ili''uun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2724,0,NULL,'ilv','ilue','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2725,0,NULL,'ilw','talur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2726,0,NULL,'ima','mala malasar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2727,0,NULL,'ime','imeraguen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2728,0,NULL,'imi','anamgura','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2729,0,NULL,'iml','miluk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2730,0,NULL,'imn','imonda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2731,0,NULL,'imo','imbongu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2732,0,NULL,'imr','imroing','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2733,0,NULL,'ims','marsian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2734,0,NULL,'imy','milyan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2735,0,NULL,'inb','inga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2736,0,NULL,'inc','indic languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2737,0,NULL,'ine','indo-european languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2738,0,NULL,'ing','degexit''an','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2739,0,NULL,'inh','ingush','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2740,0,NULL,'inj','jungle inga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2741,0,NULL,'inl','indonesian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2742,0,NULL,'inm','minaean','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2743,0,NULL,'inn','isinai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2744,0,NULL,'ino','inoke-yate','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2745,0,NULL,'inp','iñapari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2746,0,NULL,'ins','indian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2747,0,NULL,'int','intha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2748,0,NULL,'inz','ineseño','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2749,0,NULL,'ior','inor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2750,0,NULL,'iou','tuma-irumu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2751,0,NULL,'iow','iowa-oto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2752,0,NULL,'ipi','ipili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2753,0,NULL,'ipo','ipiko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2754,0,NULL,'iqu','iquito','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2755,0,NULL,'ira','iranian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2756,0,NULL,'ire','iresim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2757,0,NULL,'irh','irarutu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2758,0,NULL,'iri','irigwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2759,0,NULL,'irk','iraqw','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2760,0,NULL,'irn','irántxe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2761,0,NULL,'iro','iroquoian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2762,0,NULL,'irr','ir','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2763,0,NULL,'iru','irula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2764,0,NULL,'irx','kamberau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2765,0,NULL,'iry','iraya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2766,0,NULL,'isa','isabi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2767,0,NULL,'isc','isconahua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2768,0,NULL,'isd','isnag','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2769,0,NULL,'ise','italian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2770,0,NULL,'isg','irish sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2771,0,NULL,'ish','esan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2772,0,NULL,'isi','nkem-nkum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2773,0,NULL,'isk','ishkashimi','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2774,0,NULL,'ism','masimasi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2775,0,NULL,'isn','isanzu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2776,0,NULL,'iso','isoko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2777,0,NULL,'isr','israeli sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2778,0,NULL,'ist','istriot','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2779,0,NULL,'isu','isu (menchum division)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2780,0,NULL,'itb','binongan itneg','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2781,0,NULL,'itc','italic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2782,0,NULL,'ite','itene','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2783,0,NULL,'iti','inlaod itneg','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2784,0,NULL,'itk','judeo-italian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2785,0,NULL,'itl','itelmen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2786,0,NULL,'itm','itu mbon uzo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2787,0,NULL,'ito','itonama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2788,0,NULL,'itr','iteri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2789,0,NULL,'its','isekiri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2790,0,NULL,'itt','maeng itneg','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2791,0,NULL,'itv','itawit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2792,0,NULL,'itw','ito','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2793,0,NULL,'itx','itik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2794,0,NULL,'ity','moyadan itneg','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2795,0,NULL,'itz','itzá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2796,0,NULL,'ium','iu mien','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2797,0,NULL,'ivb','ibatan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2798,0,NULL,'ivv','ivatan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2799,0,NULL,'iwk','i-wak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2800,0,NULL,'iwm','iwam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2801,0,NULL,'iwo','iwur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2802,0,NULL,'iws','sepik iwam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2803,0,NULL,'ixc','ixcatec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2804,0,NULL,'ixl','ixil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2805,0,NULL,'iya','iyayu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2806,0,NULL,'iyo','mesaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2807,0,NULL,'iyx','yaka (congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2808,0,NULL,'izh','ingrian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2809,0,NULL,'izi','izi-ezaa-ikwo-mgbo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2810,0,NULL,'izr','izere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2811,0,NULL,'jaa','jamamadí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2812,0,NULL,'jab','hyam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2813,0,NULL,'jac','jakalteko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2813,0,NULL,'jac','popti''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2814,0,NULL,'jad','jahanka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2815,0,NULL,'jae','yabem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2816,0,NULL,'jaf','jara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2817,0,NULL,'jah','jah hut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2818,0,NULL,'jaj','zazao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2819,0,NULL,'jak','jakun','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(2820,0,NULL,'jal','yalahatan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2821,0,NULL,'jam','jamaican creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2822,0,NULL,'jao','yanyuwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2823,0,NULL,'jaq','yaqay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2824,0,NULL,'jar','jarawa (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2825,0,NULL,'jas','new caledonian javanese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2826,0,NULL,'jat','jakati','1248825600',NULL,NULL,NULL,NULL,'lah',NULL,NULL); +INSERT INTO "iana_records" VALUES(2827,0,NULL,'jau','yaur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2828,0,NULL,'jax','jambi malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(2829,0,NULL,'jay','yan-nhangu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2830,0,NULL,'jaz','jawe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2831,0,NULL,'jbe','judeo-berber','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2832,0,NULL,'jbj','arandai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2833,0,NULL,'jbn','nafusi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2834,0,NULL,'jbo','lojban','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2835,0,NULL,'jbr','jofotek-bromnya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2836,0,NULL,'jbt','jabutí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2837,0,NULL,'jbu','jukun takum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2838,0,NULL,'jcs','jamaican country sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2839,0,NULL,'jct','krymchak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2840,0,NULL,'jda','jad','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2841,0,NULL,'jdg','jadgali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2842,0,NULL,'jdt','judeo-tat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2843,0,NULL,'jeb','jebero','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2844,0,NULL,'jee','jerung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2845,0,NULL,'jeg','jeng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2846,0,NULL,'jeh','jeh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2847,0,NULL,'jei','yei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2848,0,NULL,'jek','jeri kuo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2849,0,NULL,'jel','yelmek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2850,0,NULL,'jen','dza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2851,0,NULL,'jer','jere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2852,0,NULL,'jet','manem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2853,0,NULL,'jeu','jonkor bourmataguil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2854,0,NULL,'jgb','ngbee','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2855,0,NULL,'jge','judeo-georgian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2856,0,NULL,'jgo','ngomba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2857,0,NULL,'jhi','jehai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2858,0,NULL,'jhs','jhankot sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2859,0,NULL,'jia','jina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2860,0,NULL,'jib','jibu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2861,0,NULL,'jic','tol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2862,0,NULL,'jid','bu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2863,0,NULL,'jie','jilbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2864,0,NULL,'jig','djingili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2865,0,NULL,'jih','shangzhai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2866,0,NULL,'jii','jiiddu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2867,0,NULL,'jil','jilim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2868,0,NULL,'jim','jimi (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2869,0,NULL,'jio','jiamao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2870,0,NULL,'jiq','guanyinqiao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2871,0,NULL,'jit','jita','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2872,0,NULL,'jiu','youle jinuo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2873,0,NULL,'jiv','shuar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2874,0,NULL,'jiy','buyuan jinuo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2875,0,NULL,'jko','kubo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2876,0,NULL,'jku','labir','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2877,0,NULL,'jle','ngile','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2878,0,NULL,'jls','jamaican sign language','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2879,0,NULL,'jma','dima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2880,0,NULL,'jmb','zumbun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2881,0,NULL,'jmc','machame','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2882,0,NULL,'jmd','yamdena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2883,0,NULL,'jmi','jimi (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2884,0,NULL,'jml','jumli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2885,0,NULL,'jmn','makuri naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2886,0,NULL,'jmr','kamara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2887,0,NULL,'jms','mashi (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2888,0,NULL,'jmx','western juxtlahuaca mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2889,0,NULL,'jna','jangshung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2890,0,NULL,'jnd','jandavra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2891,0,NULL,'jng','yangman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2892,0,NULL,'jni','janji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2893,0,NULL,'jnj','yemsa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2894,0,NULL,'jnl','rawat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2895,0,NULL,'jns','jaunsari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2896,0,NULL,'job','joba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2897,0,NULL,'jod','wojenaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2898,0,NULL,'jor','jorá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2899,0,NULL,'jos','jordanian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2900,0,NULL,'jow','jowulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2901,0,NULL,'jpa','jewish palestinian aramaic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2902,0,NULL,'jpr','judeo-persian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2903,0,NULL,'jpx','japanese (family)','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2904,0,NULL,'jqr','jaqaru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2905,0,NULL,'jra','jarai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2906,0,NULL,'jrb','judeo-arabic','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(2907,0,NULL,'jrr','jiru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2908,0,NULL,'jrt','jorto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2909,0,NULL,'jru','japrería','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2910,0,NULL,'jsl','japanese sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2911,0,NULL,'jua','júma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2912,0,NULL,'jub','wannu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2913,0,NULL,'juc','jurchen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2914,0,NULL,'jud','worodougou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2915,0,NULL,'juh','hõne','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2916,0,NULL,'juk','wapan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2917,0,NULL,'jul','jirel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2918,0,NULL,'jum','jumjum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2919,0,NULL,'jun','juang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2920,0,NULL,'juo','jiba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2921,0,NULL,'jup','hupdë','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2922,0,NULL,'jur','jurúna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2923,0,NULL,'jus','jumla sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2924,0,NULL,'jut','jutish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2925,0,NULL,'juu','ju','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2926,0,NULL,'juw','wãpha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2927,0,NULL,'juy','juray','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2928,0,NULL,'jvd','javindo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2929,0,NULL,'jvn','caribbean javanese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2930,0,NULL,'jwi','jwira-pepesa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2931,0,NULL,'jya','jiarong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2932,0,NULL,'jye','judeo-yemeni arabic','1248825600',NULL,NULL,NULL,NULL,'jrb',NULL,NULL); +INSERT INTO "iana_records" VALUES(2933,0,NULL,'jyy','jaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2934,0,NULL,'kaa','kara-kalpak','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2935,0,NULL,'kab','kabyle','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2936,0,NULL,'kac','jingpho','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2936,0,NULL,'kac','kachin','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2937,0,NULL,'kad','kadara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2938,0,NULL,'kae','ketangalan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2939,0,NULL,'kaf','katso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2940,0,NULL,'kag','kajaman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2941,0,NULL,'kah','kara (central african republic)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2942,0,NULL,'kai','karekare','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2943,0,NULL,'kaj','jju','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2944,0,NULL,'kak','kayapa kallahan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2945,0,NULL,'kam','kamba (kenya)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2946,0,NULL,'kao','xaasongaxango','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2947,0,NULL,'kap','bezhta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2948,0,NULL,'kaq','capanahua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2949,0,NULL,'kar','karen languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(2950,0,NULL,'kav','katukína','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2951,0,NULL,'kaw','kawi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2952,0,NULL,'kax','kao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2953,0,NULL,'kay','kamayurá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2954,0,NULL,'kba','kalarko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2955,0,NULL,'kbb','kaxuiâna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2956,0,NULL,'kbc','kadiwéu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2957,0,NULL,'kbd','kabardian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2958,0,NULL,'kbe','kanju','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2959,0,NULL,'kbf','kakauhua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2960,0,NULL,'kbg','khamba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2961,0,NULL,'kbh','camsá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2962,0,NULL,'kbi','kaptiau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2963,0,NULL,'kbj','kari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2964,0,NULL,'kbk','grass koiari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2965,0,NULL,'kbl','kanembu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2966,0,NULL,'kbm','iwal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2967,0,NULL,'kbn','kare (central african republic)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2968,0,NULL,'kbo','keliko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2969,0,NULL,'kbp','kabiyè','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2970,0,NULL,'kbq','kamano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2971,0,NULL,'kbr','kafa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2972,0,NULL,'kbs','kande','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2973,0,NULL,'kbt','abadi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2974,0,NULL,'kbu','kabutra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2975,0,NULL,'kbv','dera (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2976,0,NULL,'kbw','kaiep','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2977,0,NULL,'kbx','ap ma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2978,0,NULL,'kby','manga kanuri','1248825600',NULL,NULL,NULL,NULL,'kr',NULL,NULL); +INSERT INTO "iana_records" VALUES(2979,0,NULL,'kbz','duhwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2980,0,NULL,'kca','khanty','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2981,0,NULL,'kcb','kawacha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2982,0,NULL,'kcc','lubila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2983,0,NULL,'kcd','ngkâlmpw kanum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2984,0,NULL,'kce','kaivi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2985,0,NULL,'kcf','ukaan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2986,0,NULL,'kcg','tyap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2987,0,NULL,'kch','vono','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2988,0,NULL,'kci','kamantan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2989,0,NULL,'kcj','kobiana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2990,0,NULL,'kck','kalanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2991,0,NULL,'kcl','kela (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2992,0,NULL,'kcm','gula (central african republic)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2993,0,NULL,'kcn','nubi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2994,0,NULL,'kco','kinalakna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2995,0,NULL,'kcp','kanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2996,0,NULL,'kcq','kamo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2997,0,NULL,'kcr','katla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2998,0,NULL,'kcs','koenoem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(2999,0,NULL,'kct','kaian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3000,0,NULL,'kcu','kami (tanzania)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3001,0,NULL,'kcv','kete','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3002,0,NULL,'kcw','kabwari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3003,0,NULL,'kcx','kachama-ganjule','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3004,0,NULL,'kcy','korandje','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3005,0,NULL,'kcz','konongo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3006,0,NULL,'kda','worimi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3007,0,NULL,'kdc','kutu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3008,0,NULL,'kdd','yankunytjatjara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3009,0,NULL,'kde','makonde','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3010,0,NULL,'kdf','mamusi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3011,0,NULL,'kdg','seba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3012,0,NULL,'kdh','tem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3013,0,NULL,'kdi','kumam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3014,0,NULL,'kdj','karamojong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3015,0,NULL,'kdk','numee','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3016,0,NULL,'kdl','tsikimba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3017,0,NULL,'kdm','kagoma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3018,0,NULL,'kdn','kunda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3019,0,NULL,'kdo','kordofanian languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(3020,0,NULL,'kdp','kaningdon-nindem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3021,0,NULL,'kdq','koch','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3022,0,NULL,'kdr','karaim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3023,0,NULL,'kdt','kuy','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3024,0,NULL,'kdu','kadaru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3025,0,NULL,'kdv','kado','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3026,0,NULL,'kdw','koneraw','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3027,0,NULL,'kdx','kam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3028,0,NULL,'kdy','keder','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3028,0,NULL,'kdy','keijar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3029,0,NULL,'kdz','kwaja','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3030,0,NULL,'kea','kabuverdianu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3031,0,NULL,'keb','kélé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3032,0,NULL,'kec','keiga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3033,0,NULL,'ked','kerewe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3034,0,NULL,'kee','eastern keres','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3035,0,NULL,'kef','kpessi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3036,0,NULL,'keg','tese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3037,0,NULL,'keh','keak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3038,0,NULL,'kei','kei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3039,0,NULL,'kej','kadar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3040,0,NULL,'kek','kekchí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3041,0,NULL,'kel','kela (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3042,0,NULL,'kem','kemak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3043,0,NULL,'ken','kenyang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3044,0,NULL,'keo','kakwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3045,0,NULL,'kep','kaikadi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3046,0,NULL,'keq','kamar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3047,0,NULL,'ker','kera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3048,0,NULL,'kes','kugbo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3049,0,NULL,'ket','ket','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3050,0,NULL,'keu','akebu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3051,0,NULL,'kev','kanikkaran','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3052,0,NULL,'kew','west kewa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3053,0,NULL,'kex','kukna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3054,0,NULL,'key','kupia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3055,0,NULL,'kez','kukele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3056,0,NULL,'kfa','kodava','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3057,0,NULL,'kfb','northwestern kolami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3058,0,NULL,'kfc','konda-dora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3059,0,NULL,'kfd','korra koraga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3060,0,NULL,'kfe','kota (india)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3061,0,NULL,'kff','koya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3062,0,NULL,'kfg','kudiya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3063,0,NULL,'kfh','kurichiya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3064,0,NULL,'kfi','kannada kurumba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3065,0,NULL,'kfj','kemiehua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3066,0,NULL,'kfk','kinnauri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3067,0,NULL,'kfl','kung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3068,0,NULL,'kfm','khunsari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3069,0,NULL,'kfn','kuk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3070,0,NULL,'kfo','koro (côte d''ivoire)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3071,0,NULL,'kfp','korwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3072,0,NULL,'kfq','korku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3073,0,NULL,'kfr','kachchi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3074,0,NULL,'kfs','bilaspuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3075,0,NULL,'kft','kanjari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3076,0,NULL,'kfu','katkari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3077,0,NULL,'kfv','kurmukar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3078,0,NULL,'kfw','kharam naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3079,0,NULL,'kfx','kullu pahari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3080,0,NULL,'kfy','kumaoni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3081,0,NULL,'kfz','koromfé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3082,0,NULL,'kga','koyaga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3083,0,NULL,'kgb','kawe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3084,0,NULL,'kgc','kasseng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3085,0,NULL,'kgd','kataang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3086,0,NULL,'kge','komering','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3087,0,NULL,'kgf','kube','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3088,0,NULL,'kgg','kusunda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3089,0,NULL,'kgh','upper tanudan kalinga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3090,0,NULL,'kgi','selangor sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3091,0,NULL,'kgj','gamale kham','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3092,0,NULL,'kgk','kaiwá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3093,0,NULL,'kgl','kunggari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3094,0,NULL,'kgm','karipúna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3095,0,NULL,'kgn','karingani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3096,0,NULL,'kgo','krongo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3097,0,NULL,'kgp','kaingang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3098,0,NULL,'kgq','kamoro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3099,0,NULL,'kgr','abun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3100,0,NULL,'kgs','kumbainggar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3101,0,NULL,'kgt','somyev','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3102,0,NULL,'kgu','kobol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3103,0,NULL,'kgv','karas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3104,0,NULL,'kgw','karon dori','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3105,0,NULL,'kgx','kamaru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3106,0,NULL,'kgy','kyerung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3107,0,NULL,'kha','khasi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,'as of 2008-04-21 this subtag does not include lyngngam; see lyg'); +INSERT INTO "iana_records" VALUES(3108,0,NULL,'khb','lü','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3109,0,NULL,'khc','tukang besi north','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3110,0,NULL,'khd','bädi kanum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3111,0,NULL,'khe','korowai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3112,0,NULL,'khf','khuen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3113,0,NULL,'khg','khams tibetan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3114,0,NULL,'khh','kehu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3115,0,NULL,'khi','khoisan languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(3116,0,NULL,'khj','kuturmi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3117,0,NULL,'khk','halh mongolian','1248825600',NULL,NULL,NULL,NULL,'mn',NULL,NULL); +INSERT INTO "iana_records" VALUES(3118,0,NULL,'khl','lusi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3119,0,NULL,'khn','khandesi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3120,0,NULL,'kho','khotanese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3120,0,NULL,'kho','sakan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3121,0,NULL,'khp','kapauri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3121,0,NULL,'khp','kapori','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3122,0,NULL,'khq','koyra chiini songhay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3123,0,NULL,'khr','kharia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3124,0,NULL,'khs','kasua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3125,0,NULL,'kht','khamti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3126,0,NULL,'khu','nkhumbi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3127,0,NULL,'khv','khvarshi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3128,0,NULL,'khw','khowar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3129,0,NULL,'khx','kanu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3130,0,NULL,'khy','kele (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3131,0,NULL,'khz','keapara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3132,0,NULL,'kia','kim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3133,0,NULL,'kib','koalib','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3134,0,NULL,'kic','kickapoo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3135,0,NULL,'kid','koshin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3136,0,NULL,'kie','kibet','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3137,0,NULL,'kif','eastern parbate kham','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3138,0,NULL,'kig','kimaama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3138,0,NULL,'kig','kimaghima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3139,0,NULL,'kih','kilmeri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3140,0,NULL,'kii','kitsai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3141,0,NULL,'kij','kilivila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3142,0,NULL,'kil','kariya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3143,0,NULL,'kim','karagas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3144,0,NULL,'kio','kiowa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3145,0,NULL,'kip','sheshi kham','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3146,0,NULL,'kiq','kosadle','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3146,0,NULL,'kiq','kosare','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3147,0,NULL,'kis','kis','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3148,0,NULL,'kit','agob','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3149,0,NULL,'kiu','kirmanjki (individual language)','1248825600',NULL,NULL,NULL,NULL,'zza',NULL,NULL); +INSERT INTO "iana_records" VALUES(3150,0,NULL,'kiv','kimbu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3151,0,NULL,'kiw','northeast kiwai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3152,0,NULL,'kix','khiamniungan naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3153,0,NULL,'kiy','kirikiri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3154,0,NULL,'kiz','kisi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3155,0,NULL,'kja','mlap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3156,0,NULL,'kjb','kanjobal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3156,0,NULL,'kjb','q''anjob''al','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3157,0,NULL,'kjc','coastal konjo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3158,0,NULL,'kjd','southern kiwai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3159,0,NULL,'kje','kisar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3160,0,NULL,'kjf','khalaj','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3161,0,NULL,'kjg','khmu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3162,0,NULL,'kjh','khakas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3163,0,NULL,'kji','zabana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3164,0,NULL,'kjj','khinalugh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3165,0,NULL,'kjk','highland konjo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3166,0,NULL,'kjl','western parbate kham','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3167,0,NULL,'kjm','kháng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3168,0,NULL,'kjn','kunjen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3169,0,NULL,'kjo','harijan kinnauri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3170,0,NULL,'kjp','pwo eastern karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3171,0,NULL,'kjq','western keres','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3172,0,NULL,'kjr','kurudu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3173,0,NULL,'kjs','east kewa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3174,0,NULL,'kjt','phrae pwo karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3175,0,NULL,'kju','kashaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3176,0,NULL,'kjx','ramopa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3177,0,NULL,'kjy','erave','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3178,0,NULL,'kjz','bumthangkha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3179,0,NULL,'kka','kakanda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3180,0,NULL,'kkb','kwerisa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3181,0,NULL,'kkc','odoodee','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3182,0,NULL,'kkd','kinuku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3183,0,NULL,'kke','kakabe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3184,0,NULL,'kkf','kalaktang monpa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3185,0,NULL,'kkg','mabaka valley kalinga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3186,0,NULL,'kkh','khün','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3187,0,NULL,'kki','kagulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3188,0,NULL,'kkj','kako','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3189,0,NULL,'kkk','kokota','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3190,0,NULL,'kkl','kosarek yale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3191,0,NULL,'kkm','kiong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3192,0,NULL,'kkn','kon keu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3193,0,NULL,'kko','karko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3194,0,NULL,'kkp','gugubera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3195,0,NULL,'kkq','kaiku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3196,0,NULL,'kkr','kir-balar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3197,0,NULL,'kks','giiwo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3198,0,NULL,'kkt','koi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3199,0,NULL,'kku','tumi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3200,0,NULL,'kkv','kangean','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3201,0,NULL,'kkw','teke-kukuya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3202,0,NULL,'kkx','kohin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3203,0,NULL,'kky','guguyimidjir','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3204,0,NULL,'kkz','kaska','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3205,0,NULL,'kla','klamath-modoc','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3206,0,NULL,'klb','kiliwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3207,0,NULL,'klc','kolbila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3208,0,NULL,'kld','gamilaraay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3209,0,NULL,'kle','kulung (nepal)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3210,0,NULL,'klf','kendeje','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3211,0,NULL,'klg','tagakaulo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3212,0,NULL,'klh','weliki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3213,0,NULL,'kli','kalumpang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3214,0,NULL,'klj','turkic khalaj','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3215,0,NULL,'klk','kono (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3216,0,NULL,'kll','kagan kalagan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3217,0,NULL,'klm','migum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3218,0,NULL,'kln','kalenjin','1248825600',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(3219,0,NULL,'klo','kapya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3220,0,NULL,'klp','kamasa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3221,0,NULL,'klq','rumu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3222,0,NULL,'klr','khaling','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3223,0,NULL,'kls','kalasha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3224,0,NULL,'klt','nukna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3225,0,NULL,'klu','klao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3226,0,NULL,'klv','maskelynes','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3227,0,NULL,'klw','lindu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3228,0,NULL,'klx','koluwawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3229,0,NULL,'kly','kalao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3230,0,NULL,'klz','kabola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3231,0,NULL,'kma','konni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3232,0,NULL,'kmb','kimbundu','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3233,0,NULL,'kmc','southern dong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3234,0,NULL,'kmd','majukayang kalinga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3235,0,NULL,'kme','bakole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3236,0,NULL,'kmf','kare (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3237,0,NULL,'kmg','kâte','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3238,0,NULL,'kmh','kalam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3239,0,NULL,'kmi','kami (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3240,0,NULL,'kmj','kumarbhag paharia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3241,0,NULL,'kmk','limos kalinga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3242,0,NULL,'kml','lower tanudan kalinga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3243,0,NULL,'kmm','kom (india)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3244,0,NULL,'kmn','awtuw','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3245,0,NULL,'kmo','kwoma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3246,0,NULL,'kmp','gimme','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3247,0,NULL,'kmq','kwama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3248,0,NULL,'kmr','northern kurdish','1248825600',NULL,NULL,NULL,NULL,'ku',NULL,NULL); +INSERT INTO "iana_records" VALUES(3249,0,NULL,'kms','kamasau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3250,0,NULL,'kmt','kemtuik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3251,0,NULL,'kmu','kanite','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3252,0,NULL,'kmv','karipúna creole french','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3253,0,NULL,'kmw','komo (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3254,0,NULL,'kmx','waboda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3255,0,NULL,'kmy','koma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3256,0,NULL,'kmz','khorasani turkish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3257,0,NULL,'kna','dera (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3258,0,NULL,'knb','lubuagan kalinga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3259,0,NULL,'knc','central kanuri','1248825600',NULL,NULL,NULL,NULL,'kr',NULL,NULL); +INSERT INTO "iana_records" VALUES(3260,0,NULL,'knd','konda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3261,0,NULL,'kne','kankanaey','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3262,0,NULL,'knf','mankanya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3263,0,NULL,'kng','koongo','1248825600',NULL,NULL,NULL,NULL,'kg',NULL,NULL); +INSERT INTO "iana_records" VALUES(3264,0,NULL,'kni','kanufi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3265,0,NULL,'knj','western kanjobal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3266,0,NULL,'knk','kuranko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3267,0,NULL,'knl','keninjal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3268,0,NULL,'knm','kanamarí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3269,0,NULL,'knn','konkani (individual language)','1248825600',NULL,NULL,NULL,NULL,'kok',NULL,NULL); +INSERT INTO "iana_records" VALUES(3270,0,NULL,'kno','kono (sierra leone)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3271,0,NULL,'knp','kwanja','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3272,0,NULL,'knq','kintaq','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3273,0,NULL,'knr','kaningra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3274,0,NULL,'kns','kensiu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3275,0,NULL,'knt','panoan katukína','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3276,0,NULL,'knu','kono (guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3277,0,NULL,'knv','tabo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3278,0,NULL,'knw','kung-ekoka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3279,0,NULL,'knx','kendayan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3279,0,NULL,'knx','salako','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3280,0,NULL,'kny','kanyok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3281,0,NULL,'knz','kalamsé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3282,0,NULL,'koa','konomala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3283,0,NULL,'koc','kpati','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3284,0,NULL,'kod','kodi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3285,0,NULL,'koe','kacipo-balesi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3286,0,NULL,'kof','kubi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3287,0,NULL,'kog','cogui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3287,0,NULL,'kog','kogi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3288,0,NULL,'koh','koyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3289,0,NULL,'koi','komi-permyak','1248825600',NULL,NULL,NULL,NULL,'kv',NULL,NULL); +INSERT INTO "iana_records" VALUES(3290,0,NULL,'koj','sara dunjo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3291,0,NULL,'kok','konkani (macrolanguage)','1129420800',NULL,NULL,NULL,'deva',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(3292,0,NULL,'kol','kol (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3293,0,NULL,'koo','konzo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3294,0,NULL,'kop','kwato','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3295,0,NULL,'koq','kota (gabon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3296,0,NULL,'kos','kosraean','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3297,0,NULL,'kot','lagwan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3298,0,NULL,'kou','koke','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3299,0,NULL,'kov','kudu-camo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3300,0,NULL,'kow','kugama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3301,0,NULL,'kox','coxima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3302,0,NULL,'koy','koyukon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3303,0,NULL,'koz','korak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3304,0,NULL,'kpa','kutto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3305,0,NULL,'kpb','mullu kurumba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3306,0,NULL,'kpc','curripaco','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3307,0,NULL,'kpd','koba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3308,0,NULL,'kpe','kpelle','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(3309,0,NULL,'kpf','komba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3310,0,NULL,'kpg','kapingamarangi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3311,0,NULL,'kph','kplang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3312,0,NULL,'kpi','kofei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3313,0,NULL,'kpj','karajá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3314,0,NULL,'kpk','kpan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3315,0,NULL,'kpl','kpala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3316,0,NULL,'kpm','koho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3317,0,NULL,'kpn','kepkiriwát','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3318,0,NULL,'kpo','ikposo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3319,0,NULL,'kpp','paku karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3320,0,NULL,'kpq','korupun-sela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3321,0,NULL,'kpr','korafe-yegha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3322,0,NULL,'kps','tehit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3323,0,NULL,'kpt','karata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3324,0,NULL,'kpu','kafoa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3325,0,NULL,'kpv','komi-zyrian','1248825600',NULL,NULL,NULL,NULL,'kv',NULL,NULL); +INSERT INTO "iana_records" VALUES(3326,0,NULL,'kpw','kobon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3327,0,NULL,'kpx','mountain koiali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3328,0,NULL,'kpy','koryak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3329,0,NULL,'kpz','kupsabiny','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3330,0,NULL,'kqa','mum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3331,0,NULL,'kqb','kovai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3332,0,NULL,'kqc','doromu-koki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3333,0,NULL,'kqd','koy sanjaq surat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3334,0,NULL,'kqe','kalagan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3335,0,NULL,'kqf','kakabai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3336,0,NULL,'kqg','khe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3337,0,NULL,'kqh','kisankasa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3338,0,NULL,'kqi','koitabu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3339,0,NULL,'kqj','koromira','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3340,0,NULL,'kqk','kotafon gbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3341,0,NULL,'kql','kyenele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3342,0,NULL,'kqm','khisa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3343,0,NULL,'kqn','kaonde','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3344,0,NULL,'kqo','eastern krahn','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3345,0,NULL,'kqp','kimré','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3346,0,NULL,'kqq','krenak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3347,0,NULL,'kqr','kimaragang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3348,0,NULL,'kqs','northern kissi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3349,0,NULL,'kqt','klias river kadazan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3350,0,NULL,'kqu','seroa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3351,0,NULL,'kqv','okolod','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3352,0,NULL,'kqw','kandas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3353,0,NULL,'kqx','mser','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3354,0,NULL,'kqy','koorete','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3355,0,NULL,'kqz','korana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3356,0,NULL,'kra','kumhali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3357,0,NULL,'krb','karkin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3358,0,NULL,'krc','karachay-balkar','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3359,0,NULL,'krd','kairui-midiki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3360,0,NULL,'kre','panará','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3361,0,NULL,'krf','koro (vanuatu)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3362,0,NULL,'krh','kurama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3363,0,NULL,'kri','krio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3364,0,NULL,'krj','kinaray-a','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3365,0,NULL,'krk','kerek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3366,0,NULL,'krl','karelian','1141776000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3367,0,NULL,'krm','krim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3368,0,NULL,'krn','sapo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3369,0,NULL,'kro','kru languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(3370,0,NULL,'krp','korop','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3371,0,NULL,'krr','kru''ng 2','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3372,0,NULL,'krs','gbaya (sudan)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3373,0,NULL,'krt','tumari kanuri','1248825600',NULL,NULL,NULL,NULL,'kr',NULL,NULL); +INSERT INTO "iana_records" VALUES(3374,0,NULL,'kru','kurukh','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3375,0,NULL,'krv','kavet','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3376,0,NULL,'krw','western krahn','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3377,0,NULL,'krx','karon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3378,0,NULL,'kry','kryts','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3379,0,NULL,'krz','sota kanum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3380,0,NULL,'ksa','shuwa-zamani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3381,0,NULL,'ksb','shambala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3382,0,NULL,'ksc','southern kalinga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3383,0,NULL,'ksd','kuanua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3384,0,NULL,'kse','kuni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3385,0,NULL,'ksf','bafia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3386,0,NULL,'ksg','kusaghe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3387,0,NULL,'ksh','kölsch','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3388,0,NULL,'ksi','i''saka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3388,0,NULL,'ksi','krisa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3389,0,NULL,'ksj','uare','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3390,0,NULL,'ksk','kansa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3391,0,NULL,'ksl','kumalu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3392,0,NULL,'ksm','kumba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3393,0,NULL,'ksn','kasiguranin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3394,0,NULL,'kso','kofa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3395,0,NULL,'ksp','kaba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3396,0,NULL,'ksq','kwaami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3397,0,NULL,'ksr','borong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3398,0,NULL,'kss','southern kisi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3399,0,NULL,'kst','winyé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3400,0,NULL,'ksu','khamyang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3401,0,NULL,'ksv','kusu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3402,0,NULL,'ksw','s''gaw karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3403,0,NULL,'ksx','kedang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3404,0,NULL,'ksy','kharia thar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3405,0,NULL,'ksz','kodaku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3406,0,NULL,'kta','katua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3407,0,NULL,'ktb','kambaata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3408,0,NULL,'ktc','kholok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3409,0,NULL,'ktd','kokata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3410,0,NULL,'kte','nubri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3411,0,NULL,'ktf','kwami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3412,0,NULL,'ktg','kalkutung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3413,0,NULL,'kth','karanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3414,0,NULL,'kti','north muyu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3415,0,NULL,'ktj','plapo krumen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3416,0,NULL,'ktk','kaniet','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3417,0,NULL,'ktl','koroshi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3418,0,NULL,'ktm','kurti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3419,0,NULL,'ktn','karitiâna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3420,0,NULL,'kto','kuot','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3421,0,NULL,'ktp','kaduo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3422,0,NULL,'ktq','katabaga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3423,0,NULL,'ktr','kota marudu tinagas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3424,0,NULL,'kts','south muyu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3425,0,NULL,'ktt','ketum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3426,0,NULL,'ktu','kituba (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3427,0,NULL,'ktv','eastern katu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3428,0,NULL,'ktw','kato','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3429,0,NULL,'ktx','kaxararí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3430,0,NULL,'kty','kango (bas-uélé district)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3431,0,NULL,'ktz','ju/''hoan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3432,0,NULL,'kub','kutep','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3433,0,NULL,'kuc','kwinsu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3434,0,NULL,'kud','''auhelawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3435,0,NULL,'kue','kuman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3436,0,NULL,'kuf','western katu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3437,0,NULL,'kug','kupa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3438,0,NULL,'kuh','kushi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3439,0,NULL,'kui','kuikúro-kalapálo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3440,0,NULL,'kuj','kuria','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3441,0,NULL,'kuk','kepo''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3442,0,NULL,'kul','kulere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3443,0,NULL,'kum','kumyk','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3444,0,NULL,'kun','kunama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3445,0,NULL,'kuo','kumukio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3446,0,NULL,'kup','kunimaipa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3447,0,NULL,'kuq','karipuna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3448,0,NULL,'kus','kusaal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3449,0,NULL,'kut','kutenai','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3450,0,NULL,'kuu','upper kuskokwim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3451,0,NULL,'kuv','kur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3452,0,NULL,'kuw','kpagua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3453,0,NULL,'kux','kukatja','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3454,0,NULL,'kuy','kuuku-ya''u','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3455,0,NULL,'kuz','kunza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3456,0,NULL,'kva','bagvalal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3457,0,NULL,'kvb','kubu','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(3458,0,NULL,'kvc','kove','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3459,0,NULL,'kvd','kui (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3460,0,NULL,'kve','kalabakan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3461,0,NULL,'kvf','kabalai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3462,0,NULL,'kvg','kuni-boazi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3463,0,NULL,'kvh','komodo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3464,0,NULL,'kvi','kwang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3465,0,NULL,'kvj','psikye','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3466,0,NULL,'kvk','korean sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3467,0,NULL,'kvl','brek karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3468,0,NULL,'kvm','kendem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3469,0,NULL,'kvn','border kuna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3470,0,NULL,'kvo','dobel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3471,0,NULL,'kvp','kompane','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3472,0,NULL,'kvq','geba karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3473,0,NULL,'kvr','kerinci','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(3474,0,NULL,'kvs','kunggara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3475,0,NULL,'kvt','lahta karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3476,0,NULL,'kvu','yinbaw karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3477,0,NULL,'kvv','kola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3478,0,NULL,'kvw','wersing','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3479,0,NULL,'kvx','parkari koli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3480,0,NULL,'kvy','yintale karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3481,0,NULL,'kvz','tsakwambo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3481,0,NULL,'kvz','tsaukambo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3482,0,NULL,'kwa','dâw','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3483,0,NULL,'kwb','kwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3484,0,NULL,'kwc','likwala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3485,0,NULL,'kwd','kwaio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3486,0,NULL,'kwe','kwerba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3487,0,NULL,'kwf','kwara''ae','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3488,0,NULL,'kwg','sara kaba deme','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3489,0,NULL,'kwh','kowiai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3490,0,NULL,'kwi','awa-cuaiquer','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3491,0,NULL,'kwj','kwanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3492,0,NULL,'kwk','kwakiutl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3493,0,NULL,'kwl','kofyar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3494,0,NULL,'kwm','kwambi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3495,0,NULL,'kwn','kwangali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3496,0,NULL,'kwo','kwomtari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3497,0,NULL,'kwp','kodia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3498,0,NULL,'kwq','kwak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3499,0,NULL,'kwr','kwer','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3500,0,NULL,'kws','kwese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3501,0,NULL,'kwt','kwesten','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3502,0,NULL,'kwu','kwakum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3503,0,NULL,'kwv','sara kaba náà','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3504,0,NULL,'kww','kwinti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3505,0,NULL,'kwx','khirwar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3506,0,NULL,'kwy','san salvador kongo','1248825600',NULL,NULL,NULL,NULL,'kg',NULL,NULL); +INSERT INTO "iana_records" VALUES(3507,0,NULL,'kwz','kwadi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3508,0,NULL,'kxa','kairiru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3509,0,NULL,'kxb','krobu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3510,0,NULL,'kxc','khonso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3510,0,NULL,'kxc','konso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3511,0,NULL,'kxd','brunei','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(3512,0,NULL,'kxe','kakihum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3513,0,NULL,'kxf','manumanaw karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3514,0,NULL,'kxh','karo (ethiopia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3515,0,NULL,'kxi','keningau murut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3516,0,NULL,'kxj','kulfa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3517,0,NULL,'kxk','zayein karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3518,0,NULL,'kxl','nepali kurux','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3519,0,NULL,'kxm','northern khmer','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3520,0,NULL,'kxn','kanowit-tanjong melanau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3521,0,NULL,'kxo','kanoé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3522,0,NULL,'kxp','wadiyara koli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3523,0,NULL,'kxq','smärky kanum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3524,0,NULL,'kxr','koro (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3525,0,NULL,'kxs','kangjia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3526,0,NULL,'kxt','koiwat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3527,0,NULL,'kxu','kui (india)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3528,0,NULL,'kxv','kuvi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3529,0,NULL,'kxw','konai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3530,0,NULL,'kxx','likuba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3531,0,NULL,'kxy','kayong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3532,0,NULL,'kxz','kerewo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3533,0,NULL,'kya','kwaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3534,0,NULL,'kyb','butbut kalinga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3535,0,NULL,'kyc','kyaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3536,0,NULL,'kyd','karey','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3537,0,NULL,'kye','krache','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3538,0,NULL,'kyf','kouya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3539,0,NULL,'kyg','keyagana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3540,0,NULL,'kyh','karok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3541,0,NULL,'kyi','kiput','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3542,0,NULL,'kyj','karao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3543,0,NULL,'kyk','kamayo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3544,0,NULL,'kyl','kalapuya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3545,0,NULL,'kym','kpatili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3546,0,NULL,'kyn','northern binukidnon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3547,0,NULL,'kyo','kelon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3548,0,NULL,'kyp','kang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3549,0,NULL,'kyq','kenga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3550,0,NULL,'kyr','kuruáya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3551,0,NULL,'kys','baram kayan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3552,0,NULL,'kyt','kayagar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3553,0,NULL,'kyu','western kayah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3554,0,NULL,'kyv','kayort','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3555,0,NULL,'kyw','kudmali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3556,0,NULL,'kyx','rapoisi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3557,0,NULL,'kyy','kambaira','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3558,0,NULL,'kyz','kayabí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3559,0,NULL,'kza','western karaboro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3560,0,NULL,'kzb','kaibobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3561,0,NULL,'kzc','bondoukou kulango','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3562,0,NULL,'kzd','kadai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3563,0,NULL,'kze','kosena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3564,0,NULL,'kzf','da''a kaili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3565,0,NULL,'kzg','kikai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3566,0,NULL,'kzh','kenuzi-dongola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3567,0,NULL,'kzi','kelabit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3568,0,NULL,'kzj','coastal kadazan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3569,0,NULL,'kzk','kazukuru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3570,0,NULL,'kzl','kayeli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3571,0,NULL,'kzm','kais','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3572,0,NULL,'kzn','kokola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3573,0,NULL,'kzo','kaningi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3574,0,NULL,'kzp','kaidipang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3575,0,NULL,'kzq','kaike','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3576,0,NULL,'kzr','karang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3577,0,NULL,'kzs','sugut dusun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3578,0,NULL,'kzt','tambunan dusun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3579,0,NULL,'kzu','kayupulau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3580,0,NULL,'kzv','komyandaret','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3581,0,NULL,'kzw','karirí-xocó','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3582,0,NULL,'kzx','kamarian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3583,0,NULL,'kzy','kango (tshopo district)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3584,0,NULL,'kzz','kalabra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3585,0,NULL,'laa','southern subanen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3586,0,NULL,'lab','linear a','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3587,0,NULL,'lac','lacandon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3588,0,NULL,'lad','ladino','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3589,0,NULL,'lae','pattani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3590,0,NULL,'laf','lafofa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3591,0,NULL,'lag','langi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3592,0,NULL,'lah','lahnda','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(3593,0,NULL,'lai','lambya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3594,0,NULL,'laj','lango (uganda)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3595,0,NULL,'lak','laka (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3596,0,NULL,'lal','lalia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3597,0,NULL,'lam','lamba','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3598,0,NULL,'lan','laru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3599,0,NULL,'lap','laka (chad)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3600,0,NULL,'laq','qabiao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3601,0,NULL,'lar','larteh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3602,0,NULL,'las','lama (togo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3603,0,NULL,'lau','laba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3604,0,NULL,'law','lauje','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3605,0,NULL,'lax','tiwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3606,0,NULL,'lay','lama (myanmar)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3607,0,NULL,'laz','aribwatsa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3608,0,NULL,'lba','lui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3609,0,NULL,'lbb','label','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3610,0,NULL,'lbc','lakkia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3611,0,NULL,'lbe','lak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3612,0,NULL,'lbf','tinani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3613,0,NULL,'lbg','laopang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3614,0,NULL,'lbi','la''bi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3615,0,NULL,'lbj','ladakhi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3616,0,NULL,'lbk','central bontok','1268265600',NULL,NULL,NULL,NULL,'bnc',NULL,NULL); +INSERT INTO "iana_records" VALUES(3617,0,NULL,'lbl','libon bikol','1268265600',NULL,NULL,NULL,NULL,'bik',NULL,NULL); +INSERT INTO "iana_records" VALUES(3618,0,NULL,'lbm','lodhi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3619,0,NULL,'lbn','lamet','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3620,0,NULL,'lbo','laven','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3621,0,NULL,'lbq','wampar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3622,0,NULL,'lbr','northern lorung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3623,0,NULL,'lbs','libyan sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3624,0,NULL,'lbt','lachi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3625,0,NULL,'lbu','labu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3626,0,NULL,'lbv','lavatbura-lamusong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3627,0,NULL,'lbw','tolaki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3628,0,NULL,'lbx','lawangan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3629,0,NULL,'lby','lamu-lamu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3630,0,NULL,'lbz','lardil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3631,0,NULL,'lcc','legenyem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3632,0,NULL,'lcd','lola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3633,0,NULL,'lce','loncong','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(3634,0,NULL,'lcf','lubu','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(3635,0,NULL,'lch','luchazi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3636,0,NULL,'lcl','lisela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3637,0,NULL,'lcm','tungag','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3638,0,NULL,'lcp','western lawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3639,0,NULL,'lcq','luhu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3640,0,NULL,'lcs','lisabata-nuniali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3641,0,NULL,'ldb','idun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3642,0,NULL,'ldd','luri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3643,0,NULL,'ldg','lenyima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3644,0,NULL,'ldh','lamja-dengsa-tola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3645,0,NULL,'ldi','laari','1248825600',NULL,NULL,NULL,NULL,'kg',NULL,NULL); +INSERT INTO "iana_records" VALUES(3646,0,NULL,'ldj','lemoro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3647,0,NULL,'ldk','leelau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3648,0,NULL,'ldl','kaan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3649,0,NULL,'ldm','landoma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3650,0,NULL,'ldn','láadan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3651,0,NULL,'ldo','loo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3652,0,NULL,'ldp','tso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3653,0,NULL,'ldq','lufu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3654,0,NULL,'lea','lega-shabunda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3655,0,NULL,'leb','lala-bisa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3656,0,NULL,'lec','leco','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3657,0,NULL,'led','lendu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3658,0,NULL,'lee','lyélé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3659,0,NULL,'lef','lelemi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3660,0,NULL,'leg','lengua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3661,0,NULL,'leh','lenje','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3662,0,NULL,'lei','lemio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3663,0,NULL,'lej','lengola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3664,0,NULL,'lek','leipon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3665,0,NULL,'lel','lele (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3666,0,NULL,'lem','nomaande','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3667,0,NULL,'len','lenca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3668,0,NULL,'leo','leti (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3669,0,NULL,'lep','lepcha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3670,0,NULL,'leq','lembena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3671,0,NULL,'ler','lenkau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3672,0,NULL,'les','lese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3673,0,NULL,'let','lesing-gelimi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3674,0,NULL,'leu','kara (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3675,0,NULL,'lev','lamma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3676,0,NULL,'lew','ledo kaili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3677,0,NULL,'lex','luang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3678,0,NULL,'ley','lemolang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3679,0,NULL,'lez','lezghian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3680,0,NULL,'lfa','lefa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3681,0,NULL,'lfn','lingua franca nova','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3682,0,NULL,'lga','lungga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3683,0,NULL,'lgb','laghu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3684,0,NULL,'lgg','lugbara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3685,0,NULL,'lgh','laghuu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3686,0,NULL,'lgi','lengilu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3687,0,NULL,'lgk','lingarak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3687,0,NULL,'lgk','neverver','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3688,0,NULL,'lgl','wala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3689,0,NULL,'lgm','lega-mwenga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3690,0,NULL,'lgn','opuuo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3691,0,NULL,'lgq','logba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3692,0,NULL,'lgr','lengo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3693,0,NULL,'lgt','pahi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3694,0,NULL,'lgu','longgu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3695,0,NULL,'lgz','ligenza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3696,0,NULL,'lha','laha (viet nam)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3697,0,NULL,'lhh','laha (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3698,0,NULL,'lhi','lahu shi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3699,0,NULL,'lhl','lahul lohar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3700,0,NULL,'lhm','lhomi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3701,0,NULL,'lhn','lahanan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3702,0,NULL,'lhp','lhokpu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3703,0,NULL,'lhs','mlahsö','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3704,0,NULL,'lht','lo-toga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3705,0,NULL,'lhu','lahu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3706,0,NULL,'lia','west-central limba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3707,0,NULL,'lib','likum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3708,0,NULL,'lic','hlai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3709,0,NULL,'lid','nyindrou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3710,0,NULL,'lie','likila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3711,0,NULL,'lif','limbu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3712,0,NULL,'lig','ligbi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3713,0,NULL,'lih','lihir','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3714,0,NULL,'lii','lingkhim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3715,0,NULL,'lij','ligurian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3716,0,NULL,'lik','lika','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3717,0,NULL,'lil','lillooet','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3718,0,NULL,'lio','liki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3719,0,NULL,'lip','sekpele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3720,0,NULL,'liq','libido','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3721,0,NULL,'lir','liberian english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3722,0,NULL,'lis','lisu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3723,0,NULL,'liu','logorik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3724,0,NULL,'liv','liv','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3725,0,NULL,'liw','col','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(3726,0,NULL,'lix','liabuku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3727,0,NULL,'liy','banda-bambari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3728,0,NULL,'liz','libinza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3729,0,NULL,'lje','rampi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3730,0,NULL,'lji','laiyolo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3731,0,NULL,'ljl','li''o','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3732,0,NULL,'ljp','lampung api','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3733,0,NULL,'lka','lakalei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3734,0,NULL,'lkb','kabras','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3734,0,NULL,'lkb','lukabaras','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3735,0,NULL,'lkc','kucong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3736,0,NULL,'lkd','lakondê','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3737,0,NULL,'lke','kenyi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3738,0,NULL,'lkh','lakha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3739,0,NULL,'lki','laki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3740,0,NULL,'lkj','remun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3741,0,NULL,'lkl','laeko-libuat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3742,0,NULL,'lkn','lakon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3742,0,NULL,'lkn','vure','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3743,0,NULL,'lko','khayo','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3743,0,NULL,'lko','olukhayo','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3744,0,NULL,'lkr','päri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3745,0,NULL,'lks','kisa','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3745,0,NULL,'lks','olushisa','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3746,0,NULL,'lkt','lakota','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3747,0,NULL,'lky','lokoya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3748,0,NULL,'lla','lala-roba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3749,0,NULL,'llb','lolo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3750,0,NULL,'llc','lele (guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3751,0,NULL,'lld','ladin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3752,0,NULL,'lle','lele (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3753,0,NULL,'llf','hermit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3754,0,NULL,'llg','lole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3755,0,NULL,'llh','lamu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3756,0,NULL,'lli','teke-laali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3757,0,NULL,'llk','lelak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3758,0,NULL,'lll','lilau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3759,0,NULL,'llm','lasalimu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3760,0,NULL,'lln','lele (chad)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3761,0,NULL,'llo','khlor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3762,0,NULL,'llp','north efate','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3763,0,NULL,'llq','lolak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3764,0,NULL,'lls','lithuanian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3765,0,NULL,'llu','lau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3766,0,NULL,'llx','lauan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3767,0,NULL,'lma','east limba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3768,0,NULL,'lmb','merei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3769,0,NULL,'lmc','limilngan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3770,0,NULL,'lmd','lumun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3771,0,NULL,'lme','pévé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3772,0,NULL,'lmf','south lembata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3773,0,NULL,'lmg','lamogai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3774,0,NULL,'lmh','lambichhong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3775,0,NULL,'lmi','lombi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3776,0,NULL,'lmj','west lembata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3777,0,NULL,'lmk','lamkang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3778,0,NULL,'lml','hano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3779,0,NULL,'lmm','lamam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3780,0,NULL,'lmn','lambadi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3781,0,NULL,'lmo','lombard','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3782,0,NULL,'lmp','limbum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3783,0,NULL,'lmq','lamatuka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3784,0,NULL,'lmr','lamalera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3785,0,NULL,'lmu','lamenu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3786,0,NULL,'lmv','lomaiviti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3787,0,NULL,'lmw','lake miwok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3788,0,NULL,'lmx','laimbue','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3789,0,NULL,'lmy','lamboya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3790,0,NULL,'lmz','lumbee','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3791,0,NULL,'lna','langbashe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3792,0,NULL,'lnb','mbalanhu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3793,0,NULL,'lnd','lun bawang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3793,0,NULL,'lnd','lundayeh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3794,0,NULL,'lng','langobardic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3795,0,NULL,'lnh','lanoh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3796,0,NULL,'lni','daantanai''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3797,0,NULL,'lnj','leningitij','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3798,0,NULL,'lnl','south central banda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3799,0,NULL,'lnm','langam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3800,0,NULL,'lnn','lorediakarkar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3801,0,NULL,'lno','lango (sudan)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3802,0,NULL,'lns','lamnso''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3803,0,NULL,'lnu','longuda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3804,0,NULL,'lnz','lonzo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3805,0,NULL,'loa','loloda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3806,0,NULL,'lob','lobi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3807,0,NULL,'loc','inonhan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3808,0,NULL,'loe','saluan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3809,0,NULL,'lof','logol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3810,0,NULL,'log','logo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3811,0,NULL,'loh','narim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3812,0,NULL,'loi','loma (côte d''ivoire)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3813,0,NULL,'loj','lou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3814,0,NULL,'lok','loko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3815,0,NULL,'lol','mongo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3816,0,NULL,'lom','loma (liberia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3817,0,NULL,'lon','malawi lomwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3818,0,NULL,'loo','lombo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3819,0,NULL,'lop','lopa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3820,0,NULL,'loq','lobala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3821,0,NULL,'lor','téén','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3822,0,NULL,'los','loniu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3823,0,NULL,'lot','otuho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3824,0,NULL,'lou','louisiana creole french','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3825,0,NULL,'lov','lopi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3826,0,NULL,'low','tampias lobu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3827,0,NULL,'lox','loun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3828,0,NULL,'loy','lowa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3829,0,NULL,'loz','lozi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3830,0,NULL,'lpa','lelepa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3831,0,NULL,'lpe','lepki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3832,0,NULL,'lpn','long phuri naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3833,0,NULL,'lpo','lipo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3834,0,NULL,'lpx','lopit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3835,0,NULL,'lra','rara bakati''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3836,0,NULL,'lrc','northern luri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3837,0,NULL,'lre','laurentian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3838,0,NULL,'lrg','laragia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3839,0,NULL,'lri','marachi','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3839,0,NULL,'lri','olumarachi','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3840,0,NULL,'lrk','loarki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3841,0,NULL,'lrl','lari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3842,0,NULL,'lrm','marama','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3842,0,NULL,'lrm','olumarama','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3843,0,NULL,'lrn','lorang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3844,0,NULL,'lro','laro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3845,0,NULL,'lrr','southern lorung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3846,0,NULL,'lrt','larantuka malay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3847,0,NULL,'lrv','larevat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3848,0,NULL,'lrz','lemerig','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3849,0,NULL,'lsa','lasgerdi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3850,0,NULL,'lsd','lishana deni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3851,0,NULL,'lse','lusengo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3852,0,NULL,'lsg','lyons sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3853,0,NULL,'lsh','lish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3854,0,NULL,'lsi','lashi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3855,0,NULL,'lsl','latvian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3856,0,NULL,'lsm','olusamia','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3856,0,NULL,'lsm','saamia','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3857,0,NULL,'lso','laos sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3858,0,NULL,'lsp','lengua de señas panameñas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3858,0,NULL,'lsp','panamanian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3859,0,NULL,'lsr','aruop','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3860,0,NULL,'lss','lasi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3861,0,NULL,'lst','trinidad and tobago sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3862,0,NULL,'lsy','mauritian sign language','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3863,0,NULL,'ltc','late middle chinese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3864,0,NULL,'ltg','latgalian','1268265600',NULL,NULL,NULL,NULL,'lv',NULL,NULL); +INSERT INTO "iana_records" VALUES(3865,0,NULL,'lti','leti (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3866,0,NULL,'ltn','latundê','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3867,0,NULL,'lto','olutsotso','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3867,0,NULL,'lto','tsotso','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3868,0,NULL,'lts','lutachoni','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3868,0,NULL,'lts','tachoni','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3869,0,NULL,'ltu','latu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3870,0,NULL,'lua','luba-lulua','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3871,0,NULL,'luc','aringa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3872,0,NULL,'lud','ludian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3873,0,NULL,'lue','luvale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3874,0,NULL,'luf','laua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3875,0,NULL,'lui','luiseno','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3876,0,NULL,'luj','luna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3877,0,NULL,'luk','lunanakha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3878,0,NULL,'lul','olu''bo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3879,0,NULL,'lum','luimbi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3880,0,NULL,'lun','lunda','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3881,0,NULL,'luo','dholuo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3881,0,NULL,'luo','luo (kenya and tanzania)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3882,0,NULL,'lup','lumbu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3883,0,NULL,'luq','lucumi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3884,0,NULL,'lur','laura','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3885,0,NULL,'lus','lushai','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3886,0,NULL,'lut','lushootseed','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3887,0,NULL,'luu','lumba-yakkha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3888,0,NULL,'luv','luwati','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3889,0,NULL,'luw','luo (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3890,0,NULL,'luy','luyia','1248825600',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(3890,0,NULL,'luy','oluluyia','1248825600',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(3891,0,NULL,'luz','southern luri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3892,0,NULL,'lva','maku''a','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3893,0,NULL,'lvk','lavukaleve','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3894,0,NULL,'lvs','standard latvian','1268265600',NULL,NULL,NULL,NULL,'lv',NULL,NULL); +INSERT INTO "iana_records" VALUES(3895,0,NULL,'lvu','levuka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3896,0,NULL,'lwa','lwalu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3897,0,NULL,'lwe','lewo eleng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3898,0,NULL,'lwg','oluwanga','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3898,0,NULL,'lwg','wanga','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(3899,0,NULL,'lwh','white lachi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3900,0,NULL,'lwl','eastern lawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3901,0,NULL,'lwm','laomian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3902,0,NULL,'lwo','luwo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3903,0,NULL,'lwt','lewotobi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3904,0,NULL,'lww','lewo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3905,0,NULL,'lya','layakha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3906,0,NULL,'lyg','lyngngam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3907,0,NULL,'lyn','luyana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3908,0,NULL,'lzh','literary chinese','1248825600',NULL,NULL,NULL,NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(3909,0,NULL,'lzl','litzlitz','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3910,0,NULL,'lzn','leinong naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3911,0,NULL,'lzz','laz','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3912,0,NULL,'maa','san jerónimo tecóatl mazatec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3913,0,NULL,'mab','yutanduchi mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3914,0,NULL,'mad','madurese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3915,0,NULL,'mae','bo-rukul','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3916,0,NULL,'maf','mafa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3917,0,NULL,'mag','magahi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3918,0,NULL,'mai','maithili','1129420800',NULL,NULL,NULL,'deva',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3919,0,NULL,'maj','jalapa de díaz mazatec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3920,0,NULL,'mak','makasar','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3921,0,NULL,'mam','mam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3922,0,NULL,'man','mandingo','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(3923,0,NULL,'map','austronesian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(3924,0,NULL,'maq','chiquihuitlán mazatec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3925,0,NULL,'mas','masai','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3926,0,NULL,'mat','san francisco matlatzinca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3927,0,NULL,'mau','huautla mazatec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3928,0,NULL,'mav','sateré-mawé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3929,0,NULL,'maw','mampruli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3930,0,NULL,'max','north moluccan malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(3931,0,NULL,'maz','central mazahua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3932,0,NULL,'mba','higaonon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3933,0,NULL,'mbb','western bukidnon manobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3934,0,NULL,'mbc','macushi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3935,0,NULL,'mbd','dibabawon manobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3936,0,NULL,'mbe','molale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3937,0,NULL,'mbf','baba malay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3938,0,NULL,'mbh','mangseng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3939,0,NULL,'mbi','ilianen manobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3940,0,NULL,'mbj','nadëb','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3941,0,NULL,'mbk','malol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3942,0,NULL,'mbl','maxakalí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3943,0,NULL,'mbm','ombamba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3944,0,NULL,'mbn','macaguán','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3945,0,NULL,'mbo','mbo (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3946,0,NULL,'mbp','malayo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3947,0,NULL,'mbq','maisin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3948,0,NULL,'mbr','nukak makú','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3949,0,NULL,'mbs','sarangani manobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3950,0,NULL,'mbt','matigsalug manobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3951,0,NULL,'mbu','mbula-bwazza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3952,0,NULL,'mbv','mbulungish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3953,0,NULL,'mbw','maring','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3954,0,NULL,'mbx','mari (east sepik province)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3955,0,NULL,'mby','memoni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3956,0,NULL,'mbz','amoltepec mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3957,0,NULL,'mca','maca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3958,0,NULL,'mcb','machiguenga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3959,0,NULL,'mcc','bitur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3960,0,NULL,'mcd','sharanahua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3961,0,NULL,'mce','itundujia mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3962,0,NULL,'mcf','matsés','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3963,0,NULL,'mcg','mapoyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3964,0,NULL,'mch','maquiritari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3965,0,NULL,'mci','mese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3966,0,NULL,'mcj','mvanip','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3967,0,NULL,'mck','mbunda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3968,0,NULL,'mcl','macaguaje','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3969,0,NULL,'mcm','malaccan creole portuguese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3970,0,NULL,'mcn','masana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3971,0,NULL,'mco','coatlán mixe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3972,0,NULL,'mcp','makaa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3973,0,NULL,'mcq','ese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3974,0,NULL,'mcr','menya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3975,0,NULL,'mcs','mambai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3976,0,NULL,'mct','mengisa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3977,0,NULL,'mcu','cameroon mambila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3978,0,NULL,'mcv','minanibai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3979,0,NULL,'mcw','mawa (chad)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3980,0,NULL,'mcx','mpiemo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3981,0,NULL,'mcy','south watut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3982,0,NULL,'mcz','mawan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3983,0,NULL,'mda','mada (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3984,0,NULL,'mdb','morigi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3985,0,NULL,'mdc','male (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3986,0,NULL,'mdd','mbum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3987,0,NULL,'mde','maba (chad)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3988,0,NULL,'mdf','moksha','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3989,0,NULL,'mdg','massalat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3990,0,NULL,'mdh','maguindanaon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3991,0,NULL,'mdi','mamvu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3992,0,NULL,'mdj','mangbetu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3993,0,NULL,'mdk','mangbutu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3994,0,NULL,'mdl','maltese sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3995,0,NULL,'mdm','mayogo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3996,0,NULL,'mdn','mbati','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3997,0,NULL,'mdp','mbala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3998,0,NULL,'mdq','mbole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(3999,0,NULL,'mdr','mandar','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4000,0,NULL,'mds','maria (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4001,0,NULL,'mdt','mbere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4002,0,NULL,'mdu','mboko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4003,0,NULL,'mdv','santa lucía monteverde mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4004,0,NULL,'mdw','mbosi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4005,0,NULL,'mdx','dizin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4006,0,NULL,'mdy','male (ethiopia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4007,0,NULL,'mdz','suruí do pará','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4008,0,NULL,'mea','menka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4009,0,NULL,'meb','ikobi-mena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4010,0,NULL,'mec','mara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4011,0,NULL,'med','melpa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4012,0,NULL,'mee','mengen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4013,0,NULL,'mef','megam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4014,0,NULL,'meg','mea','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4015,0,NULL,'meh','southwestern tlaxiaco mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4016,0,NULL,'mei','midob','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4017,0,NULL,'mej','meyah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4018,0,NULL,'mek','mekeo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4019,0,NULL,'mel','central melanau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4020,0,NULL,'mem','mangala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4021,0,NULL,'men','mende (sierra leone)','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4022,0,NULL,'meo','kedah malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(4023,0,NULL,'mep','miriwung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4024,0,NULL,'meq','merey','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4025,0,NULL,'mer','meru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4026,0,NULL,'mes','masmaje','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4027,0,NULL,'met','mato','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4028,0,NULL,'meu','motu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4029,0,NULL,'mev','mann','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4030,0,NULL,'mew','maaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4031,0,NULL,'mey','hassaniyya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4032,0,NULL,'mez','menominee','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4033,0,NULL,'mfa','pattani malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(4034,0,NULL,'mfb','bangka','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(4035,0,NULL,'mfc','mba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4036,0,NULL,'mfd','mendankwe-nkwen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4037,0,NULL,'mfe','morisyen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4038,0,NULL,'mff','naki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4039,0,NULL,'mfg','mixifore','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4040,0,NULL,'mfh','matal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4041,0,NULL,'mfi','wandala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4042,0,NULL,'mfj','mefele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4043,0,NULL,'mfk','north mofu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4044,0,NULL,'mfl','putai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4045,0,NULL,'mfm','marghi south','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4046,0,NULL,'mfn','cross river mbembe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4047,0,NULL,'mfo','mbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4048,0,NULL,'mfp','makassar malay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4049,0,NULL,'mfq','moba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4050,0,NULL,'mfr','marithiel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4051,0,NULL,'mfs','mexican sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4052,0,NULL,'mft','mokerang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4053,0,NULL,'mfu','mbwela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4054,0,NULL,'mfv','mandjak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4055,0,NULL,'mfw','mulaha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4056,0,NULL,'mfx','melo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4057,0,NULL,'mfy','mayo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4058,0,NULL,'mfz','mabaan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4059,0,NULL,'mga','middle irish (900-1200)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4060,0,NULL,'mgb','mararit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4061,0,NULL,'mgc','morokodo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4062,0,NULL,'mgd','moru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4063,0,NULL,'mge','mango','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4064,0,NULL,'mgf','maklew','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4065,0,NULL,'mgg','mpongmpong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4066,0,NULL,'mgh','makhuwa-meetto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4067,0,NULL,'mgi','lijili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4068,0,NULL,'mgj','abureni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4069,0,NULL,'mgk','mawes','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4070,0,NULL,'mgl','maleu-kilenge','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4071,0,NULL,'mgm','mambae','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4072,0,NULL,'mgn','mbangi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4073,0,NULL,'mgo','meta''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4074,0,NULL,'mgp','eastern magar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4075,0,NULL,'mgq','malila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4076,0,NULL,'mgr','mambwe-lungu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4077,0,NULL,'mgs','manda (tanzania)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4078,0,NULL,'mgt','mongol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4079,0,NULL,'mgu','mailu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4080,0,NULL,'mgv','matengo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4081,0,NULL,'mgw','matumbi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4082,0,NULL,'mgx','omati','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4083,0,NULL,'mgy','mbunga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4084,0,NULL,'mgz','mbugwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4085,0,NULL,'mha','manda (india)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4086,0,NULL,'mhb','mahongwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4087,0,NULL,'mhc','mocho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4088,0,NULL,'mhd','mbugu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4089,0,NULL,'mhe','besisi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4089,0,NULL,'mhe','mah meri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4090,0,NULL,'mhf','mamaa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4091,0,NULL,'mhg','margu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4092,0,NULL,'mhh','maskoy pidgin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4093,0,NULL,'mhi','ma''di','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4094,0,NULL,'mhj','mogholi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4095,0,NULL,'mhk','mungaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4096,0,NULL,'mhl','mauwake','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4097,0,NULL,'mhm','makhuwa-moniga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4098,0,NULL,'mhn','mócheno','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4099,0,NULL,'mho','mashi (zambia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4100,0,NULL,'mhp','balinese malay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4101,0,NULL,'mhq','mandan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4102,0,NULL,'mhr','eastern mari','1248825600',NULL,NULL,NULL,NULL,'chm',NULL,NULL); +INSERT INTO "iana_records" VALUES(4103,0,NULL,'mhs','buru (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4104,0,NULL,'mht','mandahuaca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4105,0,NULL,'mhu','darang deng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4105,0,NULL,'mhu','digaro-mishmi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4106,0,NULL,'mhw','mbukushu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4107,0,NULL,'mhx','lhaovo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4107,0,NULL,'mhx','maru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4108,0,NULL,'mhy','ma''anyan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4109,0,NULL,'mhz','mor (mor islands)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4110,0,NULL,'mia','miami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4111,0,NULL,'mib','atatláhuca mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4112,0,NULL,'mic','mi''kmaq','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4112,0,NULL,'mic','micmac','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4113,0,NULL,'mid','mandaic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4114,0,NULL,'mie','ocotepec mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4115,0,NULL,'mif','mofu-gudur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4116,0,NULL,'mig','san miguel el grande mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4117,0,NULL,'mih','chayuco mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4118,0,NULL,'mii','chigmecatitlán mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4119,0,NULL,'mij','abar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4120,0,NULL,'mik','mikasuki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4121,0,NULL,'mil','peñoles mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4122,0,NULL,'mim','alacatlatzala mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4123,0,NULL,'min','minangkabau','1129420800',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(4124,0,NULL,'mio','pinotepa nacional mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4125,0,NULL,'mip','apasco-apoala mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4126,0,NULL,'miq','mískito','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4127,0,NULL,'mir','isthmus mixe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4128,0,NULL,'mis','uncoded languages','1129420800',NULL,NULL,NULL,NULL,NULL,'special',NULL); +INSERT INTO "iana_records" VALUES(4129,0,NULL,'mit','southern puebla mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4130,0,NULL,'miu','cacaloxtepec mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4131,0,NULL,'miw','akoye','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4132,0,NULL,'mix','mixtepec mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4133,0,NULL,'miy','ayutla mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4134,0,NULL,'miz','coatzospan mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4135,0,NULL,'mja','mahei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4136,0,NULL,'mjc','san juan colorado mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4137,0,NULL,'mjd','northwest maidu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4138,0,NULL,'mje','muskum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4139,0,NULL,'mjg','tu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4140,0,NULL,'mjh','mwera (nyasa)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4141,0,NULL,'mji','kim mun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4142,0,NULL,'mjj','mawak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4143,0,NULL,'mjk','matukar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4144,0,NULL,'mjl','mandeali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4145,0,NULL,'mjm','medebur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4146,0,NULL,'mjn','ma (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4147,0,NULL,'mjo','malankuravan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4148,0,NULL,'mjp','malapandaram','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4149,0,NULL,'mjq','malaryan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4150,0,NULL,'mjr','malavedan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4151,0,NULL,'mjs','miship','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4152,0,NULL,'mjt','sauria paharia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4153,0,NULL,'mju','manna-dora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4154,0,NULL,'mjv','mannan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4155,0,NULL,'mjw','karbi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4156,0,NULL,'mjx','mahali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4157,0,NULL,'mjy','mahican','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4158,0,NULL,'mjz','majhi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4159,0,NULL,'mka','mbre','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4160,0,NULL,'mkb','mal paharia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4161,0,NULL,'mkc','siliput','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4162,0,NULL,'mke','mawchi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4163,0,NULL,'mkf','miya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4164,0,NULL,'mkg','mak (china)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4165,0,NULL,'mkh','mon-khmer languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(4166,0,NULL,'mki','dhatki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4167,0,NULL,'mkj','mokilese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4168,0,NULL,'mkk','byep','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4169,0,NULL,'mkl','mokole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4170,0,NULL,'mkm','moklen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4171,0,NULL,'mkn','kupang malay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4172,0,NULL,'mko','mingang doso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4173,0,NULL,'mkp','moikodi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4174,0,NULL,'mkq','bay miwok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4175,0,NULL,'mkr','malas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4176,0,NULL,'mks','silacayoapan mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4177,0,NULL,'mkt','vamale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4178,0,NULL,'mku','konyanka maninka','1248825600',NULL,NULL,NULL,NULL,'man',NULL,NULL); +INSERT INTO "iana_records" VALUES(4179,0,NULL,'mkv','mafea','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4180,0,NULL,'mkw','kituba (congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4181,0,NULL,'mkx','kinamiging manobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4182,0,NULL,'mky','east makian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4183,0,NULL,'mkz','makasae','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4184,0,NULL,'mla','malo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4185,0,NULL,'mlb','mbule','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4186,0,NULL,'mlc','cao lan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4187,0,NULL,'mld','malakhel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4188,0,NULL,'mle','manambu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4189,0,NULL,'mlf','mal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4190,0,NULL,'mlh','mape','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4191,0,NULL,'mli','malimpung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4192,0,NULL,'mlj','miltu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4193,0,NULL,'mlk','ilwana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4193,0,NULL,'mlk','kiwilwana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4194,0,NULL,'mll','malua bay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4195,0,NULL,'mlm','mulam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4196,0,NULL,'mln','malango','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4197,0,NULL,'mlo','mlomp','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4198,0,NULL,'mlp','bargam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4199,0,NULL,'mlq','western maninkakan','1248825600',NULL,NULL,NULL,NULL,'man',NULL,NULL); +INSERT INTO "iana_records" VALUES(4200,0,NULL,'mlr','vame','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4201,0,NULL,'mls','masalit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4202,0,NULL,'mlu','to''abaita','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4203,0,NULL,'mlv','motlav','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4203,0,NULL,'mlv','mwotlap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4204,0,NULL,'mlw','moloko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4205,0,NULL,'mlx','malfaxal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4205,0,NULL,'mlx','naha''ai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4206,0,NULL,'mlz','malaynon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4207,0,NULL,'mma','mama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4208,0,NULL,'mmb','momina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4209,0,NULL,'mmc','michoacán mazahua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4210,0,NULL,'mmd','maonan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4211,0,NULL,'mme','mae','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4212,0,NULL,'mmf','mundat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4213,0,NULL,'mmg','north ambrym','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4214,0,NULL,'mmh','mehináku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4215,0,NULL,'mmi','musar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4216,0,NULL,'mmj','majhwar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4217,0,NULL,'mmk','mukha-dora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4218,0,NULL,'mml','man met','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4219,0,NULL,'mmm','maii','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4220,0,NULL,'mmn','mamanwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4221,0,NULL,'mmo','mangga buang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4222,0,NULL,'mmp','siawi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4223,0,NULL,'mmq','musak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4224,0,NULL,'mmr','western xiangxi miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(4225,0,NULL,'mmt','malalamai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4226,0,NULL,'mmu','mmaala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4227,0,NULL,'mmv','miriti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4228,0,NULL,'mmw','emae','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4229,0,NULL,'mmx','madak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4230,0,NULL,'mmy','migaama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4231,0,NULL,'mmz','mabaale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4232,0,NULL,'mna','mbula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4233,0,NULL,'mnb','muna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4234,0,NULL,'mnc','manchu','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4235,0,NULL,'mnd','mondé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4236,0,NULL,'mne','naba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4237,0,NULL,'mnf','mundani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4238,0,NULL,'mng','eastern mnong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4239,0,NULL,'mnh','mono (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4240,0,NULL,'mni','manipuri','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4241,0,NULL,'mnj','munji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4242,0,NULL,'mnk','mandinka','1248825600',NULL,NULL,NULL,NULL,'man',NULL,NULL); +INSERT INTO "iana_records" VALUES(4243,0,NULL,'mnl','tiale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4244,0,NULL,'mnm','mapena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4245,0,NULL,'mnn','southern mnong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4246,0,NULL,'mno','manobo languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(4247,0,NULL,'mnp','min bei chinese','1248825600',NULL,NULL,NULL,NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(4248,0,NULL,'mnq','minriq','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4249,0,NULL,'mnr','mono (usa)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4250,0,NULL,'mns','mansi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4251,0,NULL,'mnt','maykulan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4252,0,NULL,'mnu','mer','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4253,0,NULL,'mnv','rennell-bellona','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4254,0,NULL,'mnw','mon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4255,0,NULL,'mnx','manikion','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4256,0,NULL,'mny','manyawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4257,0,NULL,'mnz','moni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4258,0,NULL,'moa','mwan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4259,0,NULL,'moc','mocoví','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4260,0,NULL,'mod','mobilian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4261,0,NULL,'moe','montagnais','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4262,0,NULL,'mof','mohegan-montauk-narragansett','1248825600',1268265600,NULL,NULL,NULL,NULL,NULL,'see xnt, xpq'); +INSERT INTO "iana_records" VALUES(4263,0,NULL,'mog','mongondow','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4264,0,NULL,'moh','mohawk','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4265,0,NULL,'moi','mboi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4266,0,NULL,'moj','monzombo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4267,0,NULL,'mok','morori','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4268,0,NULL,'mom','mangue','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4269,0,NULL,'moo','monom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4270,0,NULL,'mop','mopán maya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4271,0,NULL,'moq','mor (bomberai peninsula)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4272,0,NULL,'mor','moro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4273,0,NULL,'mos','mossi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4274,0,NULL,'mot','barí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4275,0,NULL,'mou','mogum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4276,0,NULL,'mov','mohave','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4277,0,NULL,'mow','moi (congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4278,0,NULL,'mox','molima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4279,0,NULL,'moy','shekkacho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4280,0,NULL,'moz','mukulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4281,0,NULL,'mpa','mpoto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4282,0,NULL,'mpb','mullukmulluk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4283,0,NULL,'mpc','mangarayi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4284,0,NULL,'mpd','machinere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4285,0,NULL,'mpe','majang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4286,0,NULL,'mpg','marba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4287,0,NULL,'mph','maung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4288,0,NULL,'mpi','mpade','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4289,0,NULL,'mpj','martu wangka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4290,0,NULL,'mpk','mbara (chad)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4291,0,NULL,'mpl','middle watut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4292,0,NULL,'mpm','yosondúa mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4293,0,NULL,'mpn','mindiri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4294,0,NULL,'mpo','miu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4295,0,NULL,'mpp','migabac','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4296,0,NULL,'mpq','matís','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4297,0,NULL,'mpr','vangunu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4298,0,NULL,'mps','dadibi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4299,0,NULL,'mpt','mian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4300,0,NULL,'mpu','makuráp','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4301,0,NULL,'mpv','mungkip','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4302,0,NULL,'mpw','mapidian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4303,0,NULL,'mpx','misima-paneati','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4304,0,NULL,'mpy','mapia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4305,0,NULL,'mpz','mpi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4306,0,NULL,'mqa','maba (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4307,0,NULL,'mqb','mbuko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4308,0,NULL,'mqc','mangole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4309,0,NULL,'mqe','matepi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4310,0,NULL,'mqf','momuna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4311,0,NULL,'mqg','kota bangun kutai malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(4312,0,NULL,'mqh','tlazoyaltepec mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4313,0,NULL,'mqi','mariri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4314,0,NULL,'mqj','mamasa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4315,0,NULL,'mqk','rajah kabunsuwan manobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4316,0,NULL,'mql','mbelime','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4317,0,NULL,'mqm','south marquesan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4318,0,NULL,'mqn','moronene','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4319,0,NULL,'mqo','modole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4320,0,NULL,'mqp','manipa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4321,0,NULL,'mqq','minokok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4322,0,NULL,'mqr','mander','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4323,0,NULL,'mqs','west makian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4324,0,NULL,'mqt','mok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4325,0,NULL,'mqu','mandari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4326,0,NULL,'mqv','mosimo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4327,0,NULL,'mqw','murupi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4328,0,NULL,'mqx','mamuju','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4329,0,NULL,'mqy','manggarai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4330,0,NULL,'mqz','malasanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4331,0,NULL,'mra','mlabri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4332,0,NULL,'mrb','marino','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4333,0,NULL,'mrc','maricopa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4334,0,NULL,'mrd','western magar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4335,0,NULL,'mre','martha''s vineyard sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4336,0,NULL,'mrf','elseng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4337,0,NULL,'mrg','miri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4338,0,NULL,'mrh','mara chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4339,0,NULL,'mrj','western mari','1248825600',NULL,NULL,NULL,NULL,'chm',NULL,NULL); +INSERT INTO "iana_records" VALUES(4340,0,NULL,'mrk','hmwaveke','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4341,0,NULL,'mrl','mortlockese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4342,0,NULL,'mrm','merlav','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4342,0,NULL,'mrm','mwerlap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4343,0,NULL,'mrn','cheke holo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4344,0,NULL,'mro','mru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4345,0,NULL,'mrp','morouas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4346,0,NULL,'mrq','north marquesan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4347,0,NULL,'mrr','maria (india)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4348,0,NULL,'mrs','maragus','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4349,0,NULL,'mrt','marghi central','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4350,0,NULL,'mru','mono (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4351,0,NULL,'mrv','mangareva','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4352,0,NULL,'mrw','maranao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4353,0,NULL,'mrx','dineor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4353,0,NULL,'mrx','maremgi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4354,0,NULL,'mry','mandaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4355,0,NULL,'mrz','marind','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4356,0,NULL,'msb','masbatenyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4357,0,NULL,'msc','sankaran maninka','1248825600',NULL,NULL,NULL,NULL,'man',NULL,NULL); +INSERT INTO "iana_records" VALUES(4358,0,NULL,'msd','yucatec maya sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4359,0,NULL,'mse','musey','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4360,0,NULL,'msf','mekwei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4361,0,NULL,'msg','moraid','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4362,0,NULL,'msh','masikoro malagasy','1248825600',NULL,NULL,NULL,NULL,'mg',NULL,NULL); +INSERT INTO "iana_records" VALUES(4363,0,NULL,'msi','sabah malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(4364,0,NULL,'msj','ma (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4365,0,NULL,'msk','mansaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4366,0,NULL,'msl','molof','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4366,0,NULL,'msl','poule','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4367,0,NULL,'msm','agusan manobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4368,0,NULL,'msn','vurës','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4369,0,NULL,'mso','mombum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4370,0,NULL,'msp','maritsauá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4371,0,NULL,'msq','caac','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4372,0,NULL,'msr','mongolian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4373,0,NULL,'mss','west masela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4374,0,NULL,'mst','cataelano mandaya','1248825600',1268265600,'mry',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4375,0,NULL,'msu','musom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4376,0,NULL,'msv','maslam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4377,0,NULL,'msw','mansoanka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4378,0,NULL,'msx','moresada','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4379,0,NULL,'msy','aruamu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4380,0,NULL,'msz','momare','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4381,0,NULL,'mta','cotabato manobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4382,0,NULL,'mtb','anyin morofo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4383,0,NULL,'mtc','munit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4384,0,NULL,'mtd','mualang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4385,0,NULL,'mte','mono (solomon islands)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4386,0,NULL,'mtf','murik (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4387,0,NULL,'mtg','una','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4388,0,NULL,'mth','munggui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4389,0,NULL,'mti','maiwa (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4390,0,NULL,'mtj','moskona','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4391,0,NULL,'mtk','mbe''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4392,0,NULL,'mtl','montol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4393,0,NULL,'mtm','mator','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4394,0,NULL,'mtn','matagalpa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4395,0,NULL,'mto','totontepec mixe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4396,0,NULL,'mtp','wichí lhamtés nocten','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4397,0,NULL,'mtq','muong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4398,0,NULL,'mtr','mewari','1248825600',NULL,NULL,NULL,NULL,'mwr',NULL,NULL); +INSERT INTO "iana_records" VALUES(4399,0,NULL,'mts','yora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4400,0,NULL,'mtt','mota','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4401,0,NULL,'mtu','tututepec mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4402,0,NULL,'mtv','asaro''o','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4403,0,NULL,'mtw','southern binukidnon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4404,0,NULL,'mtx','tidaá mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4405,0,NULL,'mty','nabi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4406,0,NULL,'mua','mundang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4407,0,NULL,'mub','mubi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4408,0,NULL,'muc','mbu''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4409,0,NULL,'mud','mednyj aleut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4410,0,NULL,'mue','media lengua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4411,0,NULL,'mug','musgu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4412,0,NULL,'muh','mündü','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4413,0,NULL,'mui','musi','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(4414,0,NULL,'muj','mabire','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4415,0,NULL,'muk','mugom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4416,0,NULL,'mul','multiple languages','1129420800',NULL,NULL,NULL,NULL,NULL,'special',NULL); +INSERT INTO "iana_records" VALUES(4417,0,NULL,'mum','maiwala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4418,0,NULL,'mun','munda languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(4419,0,NULL,'muo','nyong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4420,0,NULL,'mup','malvi','1248825600',NULL,NULL,NULL,NULL,'raj',NULL,NULL); +INSERT INTO "iana_records" VALUES(4421,0,NULL,'muq','eastern xiangxi miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(4422,0,NULL,'mur','murle','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4423,0,NULL,'mus','creek','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4424,0,NULL,'mut','western muria','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4425,0,NULL,'muu','yaaku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4426,0,NULL,'muv','muthuvan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4427,0,NULL,'mux','bo-ung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4428,0,NULL,'muy','muyang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4429,0,NULL,'muz','mursi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4430,0,NULL,'mva','manam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4431,0,NULL,'mvb','mattole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4432,0,NULL,'mvd','mamboru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4433,0,NULL,'mve','marwari (pakistan)','1248825600',NULL,NULL,NULL,NULL,'mwr',NULL,NULL); +INSERT INTO "iana_records" VALUES(4434,0,NULL,'mvf','peripheral mongolian','1248825600',NULL,NULL,NULL,NULL,'mn',NULL,NULL); +INSERT INTO "iana_records" VALUES(4435,0,NULL,'mvg','yucuañe mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4436,0,NULL,'mvh','mire','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4437,0,NULL,'mvi','miyako','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4438,0,NULL,'mvk','mekmek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4439,0,NULL,'mvl','mbara (australia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4440,0,NULL,'mvm','muya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4441,0,NULL,'mvn','minaveha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4442,0,NULL,'mvo','marovo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4443,0,NULL,'mvp','duri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4444,0,NULL,'mvq','moere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4445,0,NULL,'mvr','marau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4446,0,NULL,'mvs','massep','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4447,0,NULL,'mvt','mpotovoro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4448,0,NULL,'mvu','marfa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4449,0,NULL,'mvv','tagal murut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4450,0,NULL,'mvw','machinga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4451,0,NULL,'mvx','meoswar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4452,0,NULL,'mvy','indus kohistani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4453,0,NULL,'mvz','mesqan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4454,0,NULL,'mwa','mwatebu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4455,0,NULL,'mwb','juwal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4456,0,NULL,'mwc','are','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4457,0,NULL,'mwd','mudbura','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4458,0,NULL,'mwe','mwera (chimwera)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4459,0,NULL,'mwf','murrinh-patha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4460,0,NULL,'mwg','aiklep','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4461,0,NULL,'mwh','mouk-aria','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4462,0,NULL,'mwi','labo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4462,0,NULL,'mwi','ninde','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4463,0,NULL,'mwj','maligo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4464,0,NULL,'mwk','kita maninkakan','1248825600',NULL,NULL,NULL,NULL,'man',NULL,NULL); +INSERT INTO "iana_records" VALUES(4465,0,NULL,'mwl','mirandese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4466,0,NULL,'mwm','sar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4467,0,NULL,'mwn','nyamwanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4468,0,NULL,'mwo','central maewo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4469,0,NULL,'mwp','kala lagaw ya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4470,0,NULL,'mwq','mün chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4471,0,NULL,'mwr','marwari','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(4472,0,NULL,'mws','mwimbi-muthambi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4473,0,NULL,'mwt','moken','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4474,0,NULL,'mwu','mittu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4475,0,NULL,'mwv','mentawai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4476,0,NULL,'mww','hmong daw','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(4477,0,NULL,'mwx','mediak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4478,0,NULL,'mwy','mosiro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4479,0,NULL,'mwz','moingi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4480,0,NULL,'mxa','northwest oaxaca mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4481,0,NULL,'mxb','tezoatlán mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4482,0,NULL,'mxc','manyika','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4483,0,NULL,'mxd','modang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4484,0,NULL,'mxe','mele-fila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4485,0,NULL,'mxf','malgbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4486,0,NULL,'mxg','mbangala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4487,0,NULL,'mxh','mvuba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4488,0,NULL,'mxi','mozarabic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4489,0,NULL,'mxj','geman deng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4489,0,NULL,'mxj','miju-mishmi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4490,0,NULL,'mxk','monumbo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4491,0,NULL,'mxl','maxi gbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4492,0,NULL,'mxm','meramera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4493,0,NULL,'mxn','moi (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4494,0,NULL,'mxo','mbowe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4495,0,NULL,'mxp','tlahuitoltepec mixe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4496,0,NULL,'mxq','juquila mixe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4497,0,NULL,'mxr','murik (malaysia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4498,0,NULL,'mxs','huitepec mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4499,0,NULL,'mxt','jamiltepec mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4500,0,NULL,'mxu','mada (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4501,0,NULL,'mxv','metlatónoc mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4502,0,NULL,'mxw','namo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4503,0,NULL,'mxx','mahou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4503,0,NULL,'mxx','mawukakan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4504,0,NULL,'mxy','southeastern nochixtlán mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4505,0,NULL,'mxz','central masela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4506,0,NULL,'myb','mbay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4507,0,NULL,'myc','mayeka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4508,0,NULL,'myd','maramba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4509,0,NULL,'mye','myene','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4510,0,NULL,'myf','bambassi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4511,0,NULL,'myg','manta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4512,0,NULL,'myh','makah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4513,0,NULL,'myi','mina (india)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4514,0,NULL,'myj','mangayat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4515,0,NULL,'myk','mamara senoufo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4516,0,NULL,'myl','moma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4517,0,NULL,'mym','me''en','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4518,0,NULL,'myn','mayan languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(4519,0,NULL,'myo','anfillo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4520,0,NULL,'myp','pirahã','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4521,0,NULL,'myq','forest maninka','1248825600',NULL,NULL,NULL,NULL,'man',NULL,NULL); +INSERT INTO "iana_records" VALUES(4522,0,NULL,'myr','muniche','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4523,0,NULL,'mys','mesmes','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4524,0,NULL,'myt','sangab mandaya','1248825600',1268265600,'mry',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4525,0,NULL,'myu','mundurukú','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4526,0,NULL,'myv','erzya','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4527,0,NULL,'myw','muyuw','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4528,0,NULL,'myx','masaaba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4529,0,NULL,'myy','macuna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4530,0,NULL,'myz','classical mandaic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4531,0,NULL,'mza','santa maría zacatepec mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4532,0,NULL,'mzb','tumzabt','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4533,0,NULL,'mzc','madagascar sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4534,0,NULL,'mzd','malimba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4535,0,NULL,'mze','morawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4536,0,NULL,'mzg','monastic sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4537,0,NULL,'mzh','wichí lhamtés güisnay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4538,0,NULL,'mzi','ixcatlán mazatec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4539,0,NULL,'mzj','manya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4540,0,NULL,'mzk','nigeria mambila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4541,0,NULL,'mzl','mazatlán mixe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4542,0,NULL,'mzm','mumuye','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4543,0,NULL,'mzn','mazanderani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4544,0,NULL,'mzo','matipuhy','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4545,0,NULL,'mzp','movima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4546,0,NULL,'mzq','mori atas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4547,0,NULL,'mzr','marúbo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4548,0,NULL,'mzs','macanese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4549,0,NULL,'mzt','mintil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4550,0,NULL,'mzu','inapang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4551,0,NULL,'mzv','manza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4552,0,NULL,'mzw','deg','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4553,0,NULL,'mzx','mawayana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4554,0,NULL,'mzy','mozambican sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4555,0,NULL,'mzz','maiadomu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4556,0,NULL,'naa','namla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4557,0,NULL,'nab','southern nambikuára','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4558,0,NULL,'nac','narak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4559,0,NULL,'nad','nijadali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4560,0,NULL,'nae','naka''ela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4561,0,NULL,'naf','nabak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4562,0,NULL,'nag','naga pidgin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4563,0,NULL,'nah','nahuatl languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(4564,0,NULL,'nai','north american indian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(4565,0,NULL,'naj','nalu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4566,0,NULL,'nak','nakanai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4567,0,NULL,'nal','nalik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4568,0,NULL,'nam','nangikurrunggurr','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4569,0,NULL,'nan','min nan chinese','1248825600',NULL,NULL,NULL,NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(4570,0,NULL,'nao','naaba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4571,0,NULL,'nap','neapolitan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4572,0,NULL,'naq','nama (namibia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4573,0,NULL,'nar','iguta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4574,0,NULL,'nas','naasioi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4575,0,NULL,'nat','hungworo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4576,0,NULL,'naw','nawuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4577,0,NULL,'nax','nakwi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4578,0,NULL,'nay','narrinyeri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4579,0,NULL,'naz','coatepec nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4580,0,NULL,'nba','nyemba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4581,0,NULL,'nbb','ndoe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4582,0,NULL,'nbc','chang naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4583,0,NULL,'nbd','ngbinda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4584,0,NULL,'nbe','konyak naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4585,0,NULL,'nbf','naxi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4586,0,NULL,'nbg','nagarchal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4587,0,NULL,'nbh','ngamo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4588,0,NULL,'nbi','mao naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4589,0,NULL,'nbj','ngarinman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4590,0,NULL,'nbk','nake','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4591,0,NULL,'nbm','ngbaka ma''bo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4592,0,NULL,'nbn','kuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4593,0,NULL,'nbo','nkukoli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4594,0,NULL,'nbp','nnam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4595,0,NULL,'nbq','nggem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4596,0,NULL,'nbr','numana-nunku-gbantu-numbu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4597,0,NULL,'nbs','namibian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4598,0,NULL,'nbt','na','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4599,0,NULL,'nbu','rongmei naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4600,0,NULL,'nbv','ngamambo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4601,0,NULL,'nbw','southern ngbandi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4602,0,NULL,'nbx','ngura','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4603,0,NULL,'nby','ningera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4604,0,NULL,'nca','iyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4605,0,NULL,'ncb','central nicobarese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4606,0,NULL,'ncc','ponam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4607,0,NULL,'ncd','nachering','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4608,0,NULL,'nce','yale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4609,0,NULL,'ncf','notsi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4610,0,NULL,'ncg','nisga''a','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4611,0,NULL,'nch','central huasteca nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4612,0,NULL,'nci','classical nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4613,0,NULL,'ncj','northern puebla nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4614,0,NULL,'nck','nakara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4615,0,NULL,'ncl','michoacán nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4616,0,NULL,'ncm','nambo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4617,0,NULL,'ncn','nauna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4618,0,NULL,'nco','sibe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4619,0,NULL,'ncp','ndaktup','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4620,0,NULL,'ncr','ncane','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4621,0,NULL,'ncs','nicaraguan sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4622,0,NULL,'nct','chothe naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4623,0,NULL,'ncu','chumburung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4624,0,NULL,'ncx','central puebla nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4625,0,NULL,'ncz','natchez','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4626,0,NULL,'nda','ndasa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4627,0,NULL,'ndb','kenswei nsei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4628,0,NULL,'ndc','ndau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4629,0,NULL,'ndd','nde-nsele-nta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4630,0,NULL,'ndf','nadruvian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4631,0,NULL,'ndg','ndengereko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4632,0,NULL,'ndh','ndali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4633,0,NULL,'ndi','samba leko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4634,0,NULL,'ndj','ndamba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4635,0,NULL,'ndk','ndaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4636,0,NULL,'ndl','ndolo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4637,0,NULL,'ndm','ndam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4638,0,NULL,'ndn','ngundi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4639,0,NULL,'ndp','ndo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4640,0,NULL,'ndq','ndombe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4641,0,NULL,'ndr','ndoola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4642,0,NULL,'nds','low german','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4642,0,NULL,'nds','low saxon','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4643,0,NULL,'ndt','ndunga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4644,0,NULL,'ndu','dugun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4645,0,NULL,'ndv','ndut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4646,0,NULL,'ndw','ndobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4647,0,NULL,'ndx','nduga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4648,0,NULL,'ndy','lutos','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4649,0,NULL,'ndz','ndogo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4650,0,NULL,'nea','eastern ngad''a','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4651,0,NULL,'neb','toura (côte d''ivoire)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4652,0,NULL,'nec','nedebang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4653,0,NULL,'ned','nde-gbite','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4654,0,NULL,'nee','kumak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4655,0,NULL,'nef','nefamese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4656,0,NULL,'neg','negidal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4657,0,NULL,'neh','nyenkha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4658,0,NULL,'nei','neo-hittite','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4659,0,NULL,'nej','neko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4660,0,NULL,'nek','neku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4661,0,NULL,'nem','nemi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4662,0,NULL,'nen','nengone','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4663,0,NULL,'neo','ná-meo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4664,0,NULL,'neq','north central mixe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4665,0,NULL,'ner','yahadian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4666,0,NULL,'nes','bhoti kinnauri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4667,0,NULL,'net','nete','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4668,0,NULL,'nev','nyaheun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4669,0,NULL,'new','nepal bhasa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4669,0,NULL,'new','newari','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4670,0,NULL,'nex','neme','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4671,0,NULL,'ney','neyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4672,0,NULL,'nez','nez perce','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4673,0,NULL,'nfa','dhao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4674,0,NULL,'nfd','ahwai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4675,0,NULL,'nfl','ayiwo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4675,0,NULL,'nfl','Äiwoo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4676,0,NULL,'nfr','nafaanra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4677,0,NULL,'nfu','mfumte','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4678,0,NULL,'nga','ngbaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4679,0,NULL,'ngb','northern ngbandi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4680,0,NULL,'ngc','ngombe (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4681,0,NULL,'ngd','ngando (central african republic)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4682,0,NULL,'nge','ngemba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4683,0,NULL,'ngf','trans-new guinea languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(4684,0,NULL,'ngg','ngbaka manza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4685,0,NULL,'ngh','n/u','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4686,0,NULL,'ngi','ngizim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4687,0,NULL,'ngj','ngie','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4688,0,NULL,'ngk','ngalkbun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4689,0,NULL,'ngl','lomwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4690,0,NULL,'ngm','ngatik men''s creole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4691,0,NULL,'ngn','ngwo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4692,0,NULL,'ngo','ngoni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4693,0,NULL,'ngp','ngulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4694,0,NULL,'ngq','ngoreme','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4694,0,NULL,'ngq','ngurimi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4695,0,NULL,'ngr','nagu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4695,0,NULL,'ngr','nanggu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4696,0,NULL,'ngs','gvoko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4697,0,NULL,'ngt','ngeq','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4698,0,NULL,'ngu','guerrero nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4699,0,NULL,'ngv','nagumi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4700,0,NULL,'ngw','ngwaba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4701,0,NULL,'ngx','nggwahyi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4702,0,NULL,'ngy','tibea','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4703,0,NULL,'ngz','ngungwel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4704,0,NULL,'nha','nhanda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4705,0,NULL,'nhb','beng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4706,0,NULL,'nhc','tabasco nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4707,0,NULL,'nhd','ava guaraní','1248825600',NULL,NULL,NULL,NULL,'gn',NULL,NULL); +INSERT INTO "iana_records" VALUES(4707,0,NULL,'nhd','chiripá','1248825600',NULL,NULL,NULL,NULL,'gn',NULL,NULL); +INSERT INTO "iana_records" VALUES(4708,0,NULL,'nhe','eastern huasteca nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4709,0,NULL,'nhf','nhuwala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4710,0,NULL,'nhg','tetelcingo nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4711,0,NULL,'nhh','nahari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4712,0,NULL,'nhi','zacatlán-ahuacatlán-tepetzintla nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4713,0,NULL,'nhk','isthmus-cosoleacaque nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4714,0,NULL,'nhm','morelos nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4715,0,NULL,'nhn','central nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4716,0,NULL,'nho','takuu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4717,0,NULL,'nhp','isthmus-pajapan nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4718,0,NULL,'nhq','huaxcaleca nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4719,0,NULL,'nhr','naro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4720,0,NULL,'nht','ometepec nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4721,0,NULL,'nhu','noone','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4722,0,NULL,'nhv','temascaltepec nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4723,0,NULL,'nhw','western huasteca nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4724,0,NULL,'nhx','isthmus-mecayapan nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4725,0,NULL,'nhy','northern oaxaca nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4726,0,NULL,'nhz','santa maría la alta nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4727,0,NULL,'nia','nias','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4728,0,NULL,'nib','nakama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4729,0,NULL,'nic','niger-kordofanian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(4730,0,NULL,'nid','ngandi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4731,0,NULL,'nie','niellim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4732,0,NULL,'nif','nek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4733,0,NULL,'nig','ngalakan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4734,0,NULL,'nih','nyiha (tanzania)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4735,0,NULL,'nii','nii','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4736,0,NULL,'nij','ngaju','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4737,0,NULL,'nik','southern nicobarese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4738,0,NULL,'nil','nila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4739,0,NULL,'nim','nilamba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4740,0,NULL,'nin','ninzo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4741,0,NULL,'nio','nganasan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4742,0,NULL,'niq','nandi','1248825600',NULL,NULL,NULL,NULL,'kln',NULL,NULL); +INSERT INTO "iana_records" VALUES(4743,0,NULL,'nir','nimboran','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4744,0,NULL,'nis','nimi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4745,0,NULL,'nit','southeastern kolami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4746,0,NULL,'niu','niuean','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4747,0,NULL,'niv','gilyak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4748,0,NULL,'niw','nimo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4749,0,NULL,'nix','hema','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4750,0,NULL,'niy','ngiti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4751,0,NULL,'niz','ningil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4752,0,NULL,'nja','nzanyi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4753,0,NULL,'njb','nocte naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4754,0,NULL,'njd','ndonde hamba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4755,0,NULL,'njh','lotha naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4756,0,NULL,'nji','gudanji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4757,0,NULL,'njj','njen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4758,0,NULL,'njl','njalgulgule','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4759,0,NULL,'njm','angami naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4760,0,NULL,'njn','liangmai naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4761,0,NULL,'njo','ao naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4762,0,NULL,'njr','njerep','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4763,0,NULL,'njs','nisa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4764,0,NULL,'njt','ndyuka-trio pidgin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4765,0,NULL,'nju','ngadjunmaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4766,0,NULL,'njx','kunyi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4767,0,NULL,'njy','njyem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4768,0,NULL,'nka','nkoya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4769,0,NULL,'nkb','khoibu naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4770,0,NULL,'nkc','nkongho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4771,0,NULL,'nkd','koireng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4772,0,NULL,'nke','duke','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4773,0,NULL,'nkf','inpui naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4774,0,NULL,'nkg','nekgini','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4775,0,NULL,'nkh','khezha naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4776,0,NULL,'nki','thangal naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4777,0,NULL,'nkj','nakai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4778,0,NULL,'nkk','nokuku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4779,0,NULL,'nkm','namat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4780,0,NULL,'nkn','nkangala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4781,0,NULL,'nko','nkonya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4782,0,NULL,'nkp','niuatoputapu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4783,0,NULL,'nkq','nkami','1271376000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4784,0,NULL,'nkr','nukuoro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4785,0,NULL,'nks','north asmat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4786,0,NULL,'nkt','nyika (tanzania)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4787,0,NULL,'nku','bouna kulango','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4788,0,NULL,'nkv','nyika (malawi and zambia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4789,0,NULL,'nkw','nkutu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4790,0,NULL,'nkx','nkoroo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4791,0,NULL,'nkz','nkari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4792,0,NULL,'nla','ngombale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4793,0,NULL,'nlc','nalca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4794,0,NULL,'nle','east nyala','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(4795,0,NULL,'nlg','gela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4796,0,NULL,'nli','grangali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4797,0,NULL,'nlj','nyali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4798,0,NULL,'nlk','ninia yali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4799,0,NULL,'nll','nihali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4800,0,NULL,'nln','durango nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4801,0,NULL,'nlo','ngul','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4802,0,NULL,'nlr','ngarla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4803,0,NULL,'nlu','nchumbulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4804,0,NULL,'nlv','orizaba nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4805,0,NULL,'nlx','nahali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4806,0,NULL,'nly','nyamal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4807,0,NULL,'nlz','nalögo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4808,0,NULL,'nma','maram naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4809,0,NULL,'nmb','big nambas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4809,0,NULL,'nmb','v''ënen taut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4810,0,NULL,'nmc','ngam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4811,0,NULL,'nmd','ndumu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4812,0,NULL,'nme','mzieme naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4813,0,NULL,'nmf','tangkhul naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4814,0,NULL,'nmg','kwasio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4815,0,NULL,'nmh','monsang naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4816,0,NULL,'nmi','nyam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4817,0,NULL,'nmj','ngombe (central african republic)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4818,0,NULL,'nmk','namakura','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4819,0,NULL,'nml','ndemli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4820,0,NULL,'nmm','manangba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4821,0,NULL,'nmn','!xóõ','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4822,0,NULL,'nmo','moyon naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4823,0,NULL,'nmp','nimanbur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4824,0,NULL,'nmq','nambya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4825,0,NULL,'nmr','nimbari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4826,0,NULL,'nms','letemboi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4827,0,NULL,'nmt','namonuito','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4828,0,NULL,'nmu','northeast maidu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4829,0,NULL,'nmv','ngamini','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4830,0,NULL,'nmw','nimoa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4831,0,NULL,'nmx','nama (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4832,0,NULL,'nmy','namuyi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4833,0,NULL,'nmz','nawdm','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4834,0,NULL,'nna','nyangumarta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4835,0,NULL,'nnb','nande','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4836,0,NULL,'nnc','nancere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4837,0,NULL,'nnd','west ambae','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4838,0,NULL,'nne','ngandyera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4839,0,NULL,'nnf','ngaing','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4840,0,NULL,'nng','maring naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4841,0,NULL,'nnh','ngiemboon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4842,0,NULL,'nni','north nuaulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4843,0,NULL,'nnj','nyangatom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4844,0,NULL,'nnk','nankina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4845,0,NULL,'nnl','northern rengma naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4846,0,NULL,'nnm','namia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4847,0,NULL,'nnn','ngete','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4848,0,NULL,'nnp','wancho naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4849,0,NULL,'nnq','ngindo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4850,0,NULL,'nnr','narungga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4851,0,NULL,'nns','ningye','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4852,0,NULL,'nnt','nanticoke','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4853,0,NULL,'nnu','dwang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4854,0,NULL,'nnv','nugunu (australia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4855,0,NULL,'nnw','southern nuni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4856,0,NULL,'nnx','ngong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4857,0,NULL,'nny','nyangga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4858,0,NULL,'nnz','nda''nda''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4859,0,NULL,'noa','woun meu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4860,0,NULL,'noc','nuk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4861,0,NULL,'nod','northern thai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4862,0,NULL,'noe','nimadi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4863,0,NULL,'nof','nomane','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4864,0,NULL,'nog','nogai','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4865,0,NULL,'noh','nomu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4866,0,NULL,'noi','noiri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4867,0,NULL,'noj','nonuya','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4868,0,NULL,'nok','nooksack','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4869,0,NULL,'nom','nocamán','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4870,0,NULL,'non','old norse','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4871,0,NULL,'noo','nootka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4872,0,NULL,'nop','numanggang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4873,0,NULL,'noq','ngongo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4874,0,NULL,'nos','eastern nisu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4875,0,NULL,'not','nomatsiguenga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4876,0,NULL,'nou','ewage-notu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4877,0,NULL,'nov','novial','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4878,0,NULL,'now','nyambo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4879,0,NULL,'noy','noy','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4880,0,NULL,'noz','nayi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4881,0,NULL,'npa','nar phu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4882,0,NULL,'npb','nupbikha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4883,0,NULL,'nph','phom naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4884,0,NULL,'npl','southeastern puebla nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4885,0,NULL,'npn','mondropolon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4886,0,NULL,'npo','pochuri naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4887,0,NULL,'nps','nipsan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4888,0,NULL,'npu','puimei naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4889,0,NULL,'npy','napu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4890,0,NULL,'nqg','southern nago','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4891,0,NULL,'nqk','kura ede nago','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4892,0,NULL,'nqm','ndom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4893,0,NULL,'nqn','nen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4894,0,NULL,'nqo','n''ko','1149465600',NULL,NULL,NULL,'nkoo',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4894,0,NULL,'nqo','n’ko','1149465600',NULL,NULL,NULL,'nkoo',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4895,0,NULL,'nra','ngom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4896,0,NULL,'nrb','nara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4897,0,NULL,'nrc','noric','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4898,0,NULL,'nre','southern rengma naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4899,0,NULL,'nrg','narango','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4900,0,NULL,'nri','chokri naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4901,0,NULL,'nrl','ngarluma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4902,0,NULL,'nrm','narom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4903,0,NULL,'nrn','norn','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4904,0,NULL,'nrp','north picene','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4905,0,NULL,'nrr','norra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4906,0,NULL,'nrt','northern kalapuya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4907,0,NULL,'nrx','ngurmbur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4908,0,NULL,'nrz','lala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4909,0,NULL,'nsa','sangtam naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4910,0,NULL,'nsc','nshi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4911,0,NULL,'nsd','southern nisu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4912,0,NULL,'nse','nsenga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4913,0,NULL,'nsg','ngasa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4914,0,NULL,'nsh','ngoshie','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4915,0,NULL,'nsi','nigerian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4916,0,NULL,'nsk','naskapi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4917,0,NULL,'nsl','norwegian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4918,0,NULL,'nsm','sumi naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4919,0,NULL,'nsn','nehan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4920,0,NULL,'nso','northern sotho','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4920,0,NULL,'nso','pedi','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4920,0,NULL,'nso','sepedi','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4921,0,NULL,'nsp','nepalese sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4922,0,NULL,'nsq','northern sierra miwok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4923,0,NULL,'nsr','maritime sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4924,0,NULL,'nss','nali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4925,0,NULL,'nst','tase naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4926,0,NULL,'nsu','sierra negra nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4927,0,NULL,'nsv','southwestern nisu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4928,0,NULL,'nsw','navut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4929,0,NULL,'nsx','nsongo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4930,0,NULL,'nsy','nasal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4931,0,NULL,'nsz','nisenan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4932,0,NULL,'nte','nathembo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4933,0,NULL,'nti','natioro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4934,0,NULL,'ntj','ngaanyatjarra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4935,0,NULL,'ntk','ikoma-nata-isenye','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4936,0,NULL,'ntm','nateni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4937,0,NULL,'nto','ntomba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4938,0,NULL,'ntp','northern tepehuan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4939,0,NULL,'ntr','delo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4940,0,NULL,'nts','natagaimas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4941,0,NULL,'ntu','natügu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4942,0,NULL,'ntw','nottoway','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4943,0,NULL,'nty','mantsi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4944,0,NULL,'ntz','natanzi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4945,0,NULL,'nua','yuaga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4946,0,NULL,'nub','nubian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(4947,0,NULL,'nuc','nukuini','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4948,0,NULL,'nud','ngala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4949,0,NULL,'nue','ngundu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4950,0,NULL,'nuf','nusu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4951,0,NULL,'nug','nungali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4952,0,NULL,'nuh','ndunda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4953,0,NULL,'nui','ngumbi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4954,0,NULL,'nuj','nyole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4955,0,NULL,'nul','nusa laut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4956,0,NULL,'num','niuafo''ou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4957,0,NULL,'nun','nung (myanmar)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4958,0,NULL,'nuo','nguôn','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4959,0,NULL,'nup','nupe-nupe-tako','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4960,0,NULL,'nuq','nukumanu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4961,0,NULL,'nur','nukuria','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4962,0,NULL,'nus','nuer','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4963,0,NULL,'nut','nung (viet nam)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4964,0,NULL,'nuu','ngbundu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4965,0,NULL,'nuv','northern nuni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4966,0,NULL,'nuw','nguluwan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4967,0,NULL,'nux','mehek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4968,0,NULL,'nuy','nunggubuyu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4969,0,NULL,'nuz','tlamacazapa nahuatl','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4970,0,NULL,'nvh','nasarian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4971,0,NULL,'nvm','namiae','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4972,0,NULL,'nwa','nawathinehena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4973,0,NULL,'nwb','nyabwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4974,0,NULL,'nwc','classical nepal bhasa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4974,0,NULL,'nwc','classical newari','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4974,0,NULL,'nwc','old newari','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4975,0,NULL,'nwe','ngwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4976,0,NULL,'nwi','southwest tanna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4977,0,NULL,'nwm','nyamusa-molo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4978,0,NULL,'nwr','nawaru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4979,0,NULL,'nwx','middle newar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4980,0,NULL,'nwy','nottoway-meherrin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4981,0,NULL,'nxa','nauete','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4982,0,NULL,'nxd','ngando (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4983,0,NULL,'nxe','nage','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4984,0,NULL,'nxg','ngad''a','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4985,0,NULL,'nxi','nindi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4986,0,NULL,'nxl','south nuaulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4987,0,NULL,'nxm','numidian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4988,0,NULL,'nxn','ngawun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4989,0,NULL,'nxr','ninggerum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4990,0,NULL,'nxu','narau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4991,0,NULL,'nxx','nafri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4992,0,NULL,'nyb','nyangbo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4993,0,NULL,'nyc','nyanga-li','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4994,0,NULL,'nyd','nyore','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(4994,0,NULL,'nyd','olunyole','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(4995,0,NULL,'nye','nyengo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4996,0,NULL,'nyf','giryama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4996,0,NULL,'nyf','kigiryama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4997,0,NULL,'nyg','nyindu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4998,0,NULL,'nyh','nyigina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(4999,0,NULL,'nyi','ama (sudan)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5000,0,NULL,'nyj','nyanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5001,0,NULL,'nyk','nyaneka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5002,0,NULL,'nyl','nyeu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5003,0,NULL,'nym','nyamwezi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5004,0,NULL,'nyn','nyankole','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5005,0,NULL,'nyo','nyoro','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5006,0,NULL,'nyp','nyang''i','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5007,0,NULL,'nyq','nayini','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5008,0,NULL,'nyr','nyiha (malawi)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5009,0,NULL,'nys','nyunga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5010,0,NULL,'nyt','nyawaygi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5011,0,NULL,'nyu','nyungwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5012,0,NULL,'nyv','nyulnyul','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5013,0,NULL,'nyw','nyaw','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5014,0,NULL,'nyx','nganyaywana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5015,0,NULL,'nyy','nyakyusa-ngonde','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5016,0,NULL,'nza','tigon mbembe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5017,0,NULL,'nzb','njebi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5018,0,NULL,'nzi','nzima','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5019,0,NULL,'nzk','nzakara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5020,0,NULL,'nzm','zeme naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5021,0,NULL,'nzs','new zealand sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5022,0,NULL,'nzu','teke-nzikou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5023,0,NULL,'nzy','nzakambay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5024,0,NULL,'nzz','nanga dama dogon','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5025,0,NULL,'oaa','orok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5026,0,NULL,'oac','oroch','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5027,0,NULL,'oar','ancient aramaic (up to 700 bce)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5027,0,NULL,'oar','old aramaic (up to 700 bce)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5028,0,NULL,'oav','old avar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5029,0,NULL,'obi','obispeño','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5030,0,NULL,'obk','southern bontok','1268265600',NULL,NULL,NULL,NULL,'bnc',NULL,NULL); +INSERT INTO "iana_records" VALUES(5031,0,NULL,'obl','oblo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5032,0,NULL,'obm','moabite','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5033,0,NULL,'obo','obo manobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5034,0,NULL,'obr','old burmese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5035,0,NULL,'obt','old breton','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5036,0,NULL,'obu','obulom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5037,0,NULL,'oca','ocaina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5038,0,NULL,'och','old chinese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5039,0,NULL,'oco','old cornish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5040,0,NULL,'ocu','atzingo matlatzinca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5041,0,NULL,'oda','odut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5042,0,NULL,'odk','od','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5043,0,NULL,'odt','old dutch','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5044,0,NULL,'odu','odual','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5045,0,NULL,'ofo','ofo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5046,0,NULL,'ofs','old frisian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5047,0,NULL,'ofu','efutop','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5048,0,NULL,'ogb','ogbia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5049,0,NULL,'ogc','ogbah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5050,0,NULL,'oge','old georgian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5051,0,NULL,'ogg','ogbogolo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5052,0,NULL,'ogo','khana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5053,0,NULL,'ogu','ogbronuagum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5054,0,NULL,'oht','old hittite','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5055,0,NULL,'ohu','old hungarian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5056,0,NULL,'oia','oirata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5057,0,NULL,'oin','inebu one','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5058,0,NULL,'ojb','northwestern ojibwa','1248825600',NULL,NULL,NULL,NULL,'oj',NULL,NULL); +INSERT INTO "iana_records" VALUES(5059,0,NULL,'ojc','central ojibwa','1248825600',NULL,NULL,NULL,NULL,'oj',NULL,NULL); +INSERT INTO "iana_records" VALUES(5060,0,NULL,'ojg','eastern ojibwa','1248825600',NULL,NULL,NULL,NULL,'oj',NULL,NULL); +INSERT INTO "iana_records" VALUES(5061,0,NULL,'ojp','old japanese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5062,0,NULL,'ojs','severn ojibwa','1248825600',NULL,NULL,NULL,NULL,'oj',NULL,NULL); +INSERT INTO "iana_records" VALUES(5063,0,NULL,'ojv','ontong java','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5064,0,NULL,'ojw','western ojibwa','1248825600',NULL,NULL,NULL,NULL,'oj',NULL,NULL); +INSERT INTO "iana_records" VALUES(5065,0,NULL,'oka','okanagan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5066,0,NULL,'okb','okobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5067,0,NULL,'okd','okodia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5068,0,NULL,'oke','okpe (southwestern edo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5069,0,NULL,'okh','koresh-e rostam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5070,0,NULL,'oki','okiek','1248825600',NULL,NULL,NULL,NULL,'kln',NULL,NULL); +INSERT INTO "iana_records" VALUES(5071,0,NULL,'okj','oko-juwoi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5072,0,NULL,'okk','kwamtim one','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5073,0,NULL,'okl','old kentish sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5074,0,NULL,'okm','middle korean (10th-16th cent.)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5075,0,NULL,'okn','oki-no-erabu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5076,0,NULL,'oko','old korean (3rd-9th cent.)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5077,0,NULL,'okr','kirike','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5078,0,NULL,'oks','oko-eni-osayen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5079,0,NULL,'oku','oku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5080,0,NULL,'okv','orokaiva','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5081,0,NULL,'okx','okpe (northwestern edo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5082,0,NULL,'ola','walungge','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5083,0,NULL,'old','mochi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5084,0,NULL,'ole','olekha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5085,0,NULL,'olm','oloma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5086,0,NULL,'olo','livvi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5087,0,NULL,'olr','olrat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5088,0,NULL,'oma','omaha-ponca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5089,0,NULL,'omb','east ambae','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5090,0,NULL,'omc','mochica','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5091,0,NULL,'ome','omejes','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5092,0,NULL,'omg','omagua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5093,0,NULL,'omi','omi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5094,0,NULL,'omk','omok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5095,0,NULL,'oml','ombo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5096,0,NULL,'omn','minoan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5097,0,NULL,'omo','utarmbung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5098,0,NULL,'omp','old manipuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5099,0,NULL,'omq','oto-manguean languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5100,0,NULL,'omr','old marathi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5101,0,NULL,'omt','omotik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5102,0,NULL,'omu','omurano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5103,0,NULL,'omv','omotic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5104,0,NULL,'omw','south tairora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5105,0,NULL,'omx','old mon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5106,0,NULL,'ona','ona','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5107,0,NULL,'onb','lingao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5108,0,NULL,'one','oneida','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5109,0,NULL,'ong','olo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5110,0,NULL,'oni','onin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5111,0,NULL,'onj','onjob','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5112,0,NULL,'onk','kabore one','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5113,0,NULL,'onn','onobasulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5114,0,NULL,'ono','onondaga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5115,0,NULL,'onp','sartang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5116,0,NULL,'onr','northern one','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5117,0,NULL,'ons','ono','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5118,0,NULL,'ont','ontenu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5119,0,NULL,'onu','unua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5120,0,NULL,'onw','old nubian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5121,0,NULL,'onx','onin based pidgin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5122,0,NULL,'ood','tohono o''odham','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5123,0,NULL,'oog','ong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5124,0,NULL,'oon','Önge','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5125,0,NULL,'oor','oorlams','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5126,0,NULL,'oos','old ossetic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5127,0,NULL,'opa','okpamheri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5128,0,NULL,'opk','kopkaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5129,0,NULL,'opm','oksapmin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5130,0,NULL,'opo','opao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5131,0,NULL,'opt','opata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5132,0,NULL,'opy','ofayé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5133,0,NULL,'ora','oroha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5134,0,NULL,'orc','orma','1248825600',NULL,NULL,NULL,NULL,'om',NULL,NULL); +INSERT INTO "iana_records" VALUES(5135,0,NULL,'ore','orejón','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5136,0,NULL,'org','oring','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5137,0,NULL,'orh','oroqen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5138,0,NULL,'orn','orang kanaq','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(5139,0,NULL,'oro','orokolo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5140,0,NULL,'orr','oruma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5141,0,NULL,'ors','orang seletar','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(5142,0,NULL,'ort','adivasi oriya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5143,0,NULL,'oru','ormuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5144,0,NULL,'orv','old russian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5145,0,NULL,'orw','oro win','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5146,0,NULL,'orx','oro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5147,0,NULL,'orz','ormu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5148,0,NULL,'osa','osage','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5149,0,NULL,'osc','oscan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5150,0,NULL,'osi','osing','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5151,0,NULL,'oso','ososo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5152,0,NULL,'osp','old spanish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5153,0,NULL,'ost','osatu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5154,0,NULL,'osu','southern one','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5155,0,NULL,'osx','old saxon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5156,0,NULL,'ota','ottoman turkish (1500-1928)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5157,0,NULL,'otb','old tibetan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5158,0,NULL,'otd','ot danum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5159,0,NULL,'ote','mezquital otomi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5160,0,NULL,'oti','oti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5161,0,NULL,'otk','old turkish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5162,0,NULL,'otl','tilapa otomi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5163,0,NULL,'otm','eastern highland otomi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5164,0,NULL,'otn','tenango otomi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5165,0,NULL,'oto','otomian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5166,0,NULL,'otq','querétaro otomi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5167,0,NULL,'otr','otoro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5168,0,NULL,'ots','estado de méxico otomi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5169,0,NULL,'ott','temoaya otomi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5170,0,NULL,'otu','otuke','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5171,0,NULL,'otw','ottawa','1248825600',NULL,NULL,NULL,NULL,'oj',NULL,NULL); +INSERT INTO "iana_records" VALUES(5172,0,NULL,'otx','texcatepec otomi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5173,0,NULL,'oty','old tamil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5174,0,NULL,'otz','ixtenco otomi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5175,0,NULL,'oua','tagargrent','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5176,0,NULL,'oub','glio-oubi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5177,0,NULL,'oue','ounge','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5178,0,NULL,'oui','old uighur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5179,0,NULL,'oum','ouma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5180,0,NULL,'oun','!o!ung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5181,0,NULL,'owi','owiniga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5182,0,NULL,'owl','old welsh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5183,0,NULL,'oyb','oy','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5184,0,NULL,'oyd','oyda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5185,0,NULL,'oym','wayampi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5186,0,NULL,'oyy','oya''oya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5187,0,NULL,'ozm','koonzime','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5188,0,NULL,'paa','papuan languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5189,0,NULL,'pab','parecís','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5190,0,NULL,'pac','pacoh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5191,0,NULL,'pad','paumarí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5192,0,NULL,'pae','pagibete','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5193,0,NULL,'paf','paranawát','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5194,0,NULL,'pag','pangasinan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5195,0,NULL,'pah','tenharim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5196,0,NULL,'pai','pe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5197,0,NULL,'pak','parakanã','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5198,0,NULL,'pal','pahlavi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5199,0,NULL,'pam','kapampangan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5199,0,NULL,'pam','pampanga','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5200,0,NULL,'pao','northern paiute','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5201,0,NULL,'pap','papiamento','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5202,0,NULL,'paq','parya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5203,0,NULL,'par','panamint','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5203,0,NULL,'par','timbisha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5204,0,NULL,'pas','papasena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5205,0,NULL,'pat','papitalai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5206,0,NULL,'pau','palauan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5207,0,NULL,'pav','pakaásnovos','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5208,0,NULL,'paw','pawnee','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5209,0,NULL,'pax','pankararé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5210,0,NULL,'pay','pech','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5211,0,NULL,'paz','pankararú','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5212,0,NULL,'pbb','páez','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5213,0,NULL,'pbc','patamona','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5214,0,NULL,'pbe','mezontla popoloca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5215,0,NULL,'pbf','coyotepec popoloca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5216,0,NULL,'pbg','paraujano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5217,0,NULL,'pbh','e''ñapa woromaipu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5218,0,NULL,'pbi','parkwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5219,0,NULL,'pbl','mak (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5220,0,NULL,'pbn','kpasam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5221,0,NULL,'pbo','papel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5222,0,NULL,'pbp','badyara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5223,0,NULL,'pbr','pangwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5224,0,NULL,'pbs','central pame','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5225,0,NULL,'pbt','southern pashto','1248825600',NULL,NULL,NULL,NULL,'ps',NULL,NULL); +INSERT INTO "iana_records" VALUES(5226,0,NULL,'pbu','northern pashto','1248825600',NULL,NULL,NULL,NULL,'ps',NULL,NULL); +INSERT INTO "iana_records" VALUES(5227,0,NULL,'pbv','pnar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5228,0,NULL,'pby','pyu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5229,0,NULL,'pbz','palu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5230,0,NULL,'pca','santa inés ahuatempan popoloca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5231,0,NULL,'pcb','pear','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5232,0,NULL,'pcc','bouyei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5233,0,NULL,'pcd','picard','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5234,0,NULL,'pce','ruching palaung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5235,0,NULL,'pcf','paliyan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5236,0,NULL,'pcg','paniya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5237,0,NULL,'pch','pardhan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5238,0,NULL,'pci','duruwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5239,0,NULL,'pcj','parenga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5240,0,NULL,'pck','paite chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5241,0,NULL,'pcl','pardhi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5242,0,NULL,'pcm','nigerian pidgin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5243,0,NULL,'pcn','piti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5244,0,NULL,'pcp','pacahuara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5245,0,NULL,'pcr','panang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5246,0,NULL,'pcw','pyapun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5247,0,NULL,'pda','anam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5248,0,NULL,'pdc','pennsylvania german','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5249,0,NULL,'pdi','pa di','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5250,0,NULL,'pdn','fedan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5250,0,NULL,'pdn','podena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5251,0,NULL,'pdo','padoe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5252,0,NULL,'pdt','plautdietsch','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5253,0,NULL,'pdu','kayan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5254,0,NULL,'pea','peranakan indonesian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5255,0,NULL,'peb','eastern pomo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5256,0,NULL,'ped','mala (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5257,0,NULL,'pee','taje','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5258,0,NULL,'pef','northeastern pomo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5259,0,NULL,'peg','pengo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5260,0,NULL,'peh','bonan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5261,0,NULL,'pei','chichimeca-jonaz','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5262,0,NULL,'pej','northern pomo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5263,0,NULL,'pek','penchal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5264,0,NULL,'pel','pekal','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(5265,0,NULL,'pem','phende','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5266,0,NULL,'peo','old persian (ca. 600-400 b.c.)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5267,0,NULL,'pep','kunja','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5268,0,NULL,'peq','southern pomo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5269,0,NULL,'pes','iranian persian','1248825600',NULL,NULL,NULL,NULL,'fa',NULL,NULL); +INSERT INTO "iana_records" VALUES(5270,0,NULL,'pev','pémono','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5271,0,NULL,'pex','petats','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5272,0,NULL,'pey','petjo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5273,0,NULL,'pez','eastern penan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5274,0,NULL,'pfa','pááfang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5275,0,NULL,'pfe','peere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5276,0,NULL,'pfl','pfaelzisch','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5277,0,NULL,'pga','sudanese creole arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(5278,0,NULL,'pgg','pangwali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5279,0,NULL,'pgi','pagi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5280,0,NULL,'pgk','rerep','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5281,0,NULL,'pgn','paelignian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5282,0,NULL,'pgs','pangseng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5283,0,NULL,'pgu','pagu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5284,0,NULL,'pgy','pongyong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5285,0,NULL,'pha','pa-hng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5286,0,NULL,'phd','phudagi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5287,0,NULL,'phg','phuong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5288,0,NULL,'phh','phukha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5289,0,NULL,'phi','philippine languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5290,0,NULL,'phk','phake','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5291,0,NULL,'phl','palula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5291,0,NULL,'phl','phalura','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5292,0,NULL,'phm','phimbi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5293,0,NULL,'phn','phoenician','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5294,0,NULL,'pho','phunoi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5295,0,NULL,'phq','phana''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5296,0,NULL,'phr','pahari-potwari','1248825600',NULL,NULL,NULL,NULL,'lah',NULL,NULL); +INSERT INTO "iana_records" VALUES(5297,0,NULL,'pht','phu thai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5298,0,NULL,'phu','phuan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5299,0,NULL,'phv','pahlavani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5300,0,NULL,'phw','phangduwali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5301,0,NULL,'pia','pima bajo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5302,0,NULL,'pib','yine','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5303,0,NULL,'pic','pinji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5304,0,NULL,'pid','piaroa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5305,0,NULL,'pie','piro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5306,0,NULL,'pif','pingelapese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5307,0,NULL,'pig','pisabo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5308,0,NULL,'pih','pitcairn-norfolk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5309,0,NULL,'pii','pini','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5310,0,NULL,'pij','pijao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5311,0,NULL,'pil','yom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5312,0,NULL,'pim','powhatan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5313,0,NULL,'pin','piame','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5314,0,NULL,'pio','piapoco','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5315,0,NULL,'pip','pero','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5316,0,NULL,'pir','piratapuyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5317,0,NULL,'pis','pijin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5318,0,NULL,'pit','pitta pitta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5319,0,NULL,'piu','pintupi-luritja','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5320,0,NULL,'piv','pileni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5320,0,NULL,'piv','vaeakau-taumako','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5321,0,NULL,'piw','pimbwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5322,0,NULL,'pix','piu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5323,0,NULL,'piy','piya-kwonci','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5324,0,NULL,'piz','pije','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5325,0,NULL,'pjt','pitjantjatjara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5326,0,NULL,'pka','ardhamāgadhī prākrit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5327,0,NULL,'pkb','kipfokomo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5327,0,NULL,'pkb','pokomo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5328,0,NULL,'pkc','paekche','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5329,0,NULL,'pkg','pak-tong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5330,0,NULL,'pkh','pankhu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5331,0,NULL,'pkn','pakanha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5332,0,NULL,'pko','pökoot','1248825600',NULL,NULL,NULL,NULL,'kln',NULL,NULL); +INSERT INTO "iana_records" VALUES(5333,0,NULL,'pkp','pukapuka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5334,0,NULL,'pkr','attapady kurumba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5335,0,NULL,'pks','pakistan sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5336,0,NULL,'pkt','maleng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5337,0,NULL,'pku','paku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5338,0,NULL,'pla','miani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5339,0,NULL,'plb','polonombauk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5340,0,NULL,'plc','central palawano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5341,0,NULL,'pld','polari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5342,0,NULL,'ple','palu''e','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5343,0,NULL,'plf','central malayo-polynesian languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5344,0,NULL,'plg','pilagá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5345,0,NULL,'plh','paulohi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5346,0,NULL,'plj','polci','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5347,0,NULL,'plk','kohistani shina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5348,0,NULL,'pll','shwe palaung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5349,0,NULL,'pln','palenquero','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5350,0,NULL,'plo','oluta popoluca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5351,0,NULL,'plp','palpa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5352,0,NULL,'plq','palaic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5353,0,NULL,'plr','palaka senoufo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5354,0,NULL,'pls','san marcos tlalcoyalco popoloca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5355,0,NULL,'plt','plateau malagasy','1248825600',NULL,NULL,NULL,NULL,'mg',NULL,NULL); +INSERT INTO "iana_records" VALUES(5356,0,NULL,'plu','palikúr','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5357,0,NULL,'plv','southwest palawano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5358,0,NULL,'plw','brooke''s point palawano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5359,0,NULL,'ply','bolyu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5360,0,NULL,'plz','paluan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5361,0,NULL,'pma','paama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5362,0,NULL,'pmb','pambia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5363,0,NULL,'pmc','palumata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5364,0,NULL,'pme','pwaamei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5365,0,NULL,'pmf','pamona','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5366,0,NULL,'pmh','māhārāṣṭri prākrit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5367,0,NULL,'pmi','northern pumi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5368,0,NULL,'pmj','southern pumi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5369,0,NULL,'pmk','pamlico','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5370,0,NULL,'pml','lingua franca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5371,0,NULL,'pmm','pomo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5372,0,NULL,'pmn','pam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5373,0,NULL,'pmo','pom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5374,0,NULL,'pmq','northern pame','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5375,0,NULL,'pmr','paynamar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5376,0,NULL,'pms','piemontese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5377,0,NULL,'pmt','tuamotuan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5378,0,NULL,'pmu','mirpur panjabi','1248825600',NULL,NULL,NULL,NULL,'lah',NULL,NULL); +INSERT INTO "iana_records" VALUES(5379,0,NULL,'pmw','plains miwok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5380,0,NULL,'pmx','poumei naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5381,0,NULL,'pmy','papuan malay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5382,0,NULL,'pmz','southern pame','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5383,0,NULL,'pna','punan bah-biau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5384,0,NULL,'pnb','western panjabi','1248825600',NULL,NULL,NULL,NULL,'lah',NULL,NULL); +INSERT INTO "iana_records" VALUES(5385,0,NULL,'pnc','pannei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5386,0,NULL,'pne','western penan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5387,0,NULL,'png','pongu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5388,0,NULL,'pnh','penrhyn','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5389,0,NULL,'pni','aoheng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5390,0,NULL,'pnm','punan batu 1','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5391,0,NULL,'pnn','pinai-hagahai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5392,0,NULL,'pno','panobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5393,0,NULL,'pnp','pancana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5394,0,NULL,'pnq','pana (burkina faso)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5395,0,NULL,'pnr','panim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5396,0,NULL,'pns','ponosakan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5397,0,NULL,'pnt','pontic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5398,0,NULL,'pnu','jiongnai bunu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5399,0,NULL,'pnv','pinigura','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5400,0,NULL,'pnw','panytyima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5401,0,NULL,'pnx','phong-kniang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5402,0,NULL,'pny','pinyin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,'a niger-congo language spoken in cameroon; not to be confused with the pinyin romanization systems used for chinese and tibetan'); +INSERT INTO "iana_records" VALUES(5403,0,NULL,'pnz','pana (central african republic)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5404,0,NULL,'poc','poqomam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5405,0,NULL,'pod','ponares','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5406,0,NULL,'poe','san juan atzingo popoloca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5407,0,NULL,'pof','poke','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5408,0,NULL,'pog','potiguára','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5409,0,NULL,'poh','poqomchi''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5410,0,NULL,'poi','highland popoluca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5411,0,NULL,'pok','pokangá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5412,0,NULL,'pom','southeastern pomo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5413,0,NULL,'pon','pohnpeian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5414,0,NULL,'poo','central pomo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5415,0,NULL,'pop','pwapwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5416,0,NULL,'poq','texistepec popoluca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5417,0,NULL,'pos','sayula popoluca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5418,0,NULL,'pot','potawatomi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5419,0,NULL,'pov','upper guinea crioulo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5420,0,NULL,'pow','san felipe otlaltepec popoloca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5421,0,NULL,'pox','polabian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5422,0,NULL,'poy','pogolo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5423,0,NULL,'poz','malayo-polynesian languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5424,0,NULL,'ppa','pao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5425,0,NULL,'ppe','papi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5426,0,NULL,'ppi','paipai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5427,0,NULL,'ppk','uma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5428,0,NULL,'ppl','nicarao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5428,0,NULL,'ppl','pipil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5429,0,NULL,'ppm','papuma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5430,0,NULL,'ppn','papapana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5431,0,NULL,'ppo','folopa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5432,0,NULL,'ppp','pelende','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5433,0,NULL,'ppq','pei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5434,0,NULL,'ppr','piru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5435,0,NULL,'pps','san luís temalacayuca popoloca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5436,0,NULL,'ppt','pare','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5437,0,NULL,'ppu','papora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5438,0,NULL,'pqa','pa''a','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5439,0,NULL,'pqe','eastern malayo-polynesian languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5440,0,NULL,'pqm','malecite-passamaquoddy','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5441,0,NULL,'pqw','western malayo-polynesian languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5442,0,NULL,'pra','prakrit languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5443,0,NULL,'prb','lua''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5444,0,NULL,'prc','parachi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5445,0,NULL,'prd','parsi-dari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5446,0,NULL,'pre','principense','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5447,0,NULL,'prf','paranan','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5448,0,NULL,'prg','prussian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5449,0,NULL,'prh','porohanon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5450,0,NULL,'pri','paicî','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5451,0,NULL,'prk','parauk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5452,0,NULL,'prl','peruvian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5453,0,NULL,'prm','kibiri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5454,0,NULL,'prn','prasuni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5455,0,NULL,'pro','old occitan (to 1500)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5455,0,NULL,'pro','old provençal (to 1500)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5456,0,NULL,'prp','parsi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5457,0,NULL,'prq','ashéninka perené','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5458,0,NULL,'prr','puri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5459,0,NULL,'prs','afghan persian','1248825600',NULL,NULL,NULL,NULL,'fa',NULL,NULL); +INSERT INTO "iana_records" VALUES(5459,0,NULL,'prs','dari','1248825600',NULL,NULL,NULL,NULL,'fa',NULL,NULL); +INSERT INTO "iana_records" VALUES(5460,0,NULL,'prt','phai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5461,0,NULL,'pru','puragi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5462,0,NULL,'prw','parawen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5463,0,NULL,'prx','purik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5464,0,NULL,'pry','pray 3','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5465,0,NULL,'prz','providencia sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5466,0,NULL,'psa','asue awyu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5467,0,NULL,'psc','persian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5468,0,NULL,'psd','plains indian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5469,0,NULL,'pse','central malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(5470,0,NULL,'psg','penang sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5471,0,NULL,'psh','southwest pashayi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5472,0,NULL,'psi','southeast pashayi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5473,0,NULL,'psl','puerto rican sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5474,0,NULL,'psm','pauserna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5475,0,NULL,'psn','panasuan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5476,0,NULL,'pso','polish sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5477,0,NULL,'psp','philippine sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5478,0,NULL,'psq','pasi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5479,0,NULL,'psr','portuguese sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5480,0,NULL,'pss','kaulong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5481,0,NULL,'pst','central pashto','1248825600',NULL,NULL,NULL,NULL,'ps',NULL,NULL); +INSERT INTO "iana_records" VALUES(5482,0,NULL,'psu','sauraseni prākrit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5483,0,NULL,'psw','port sandwich','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5484,0,NULL,'psy','piscataway','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5485,0,NULL,'pta','pai tavytera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5486,0,NULL,'pth','pataxó hã-ha-hãe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5487,0,NULL,'pti','pintiini','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5488,0,NULL,'ptn','patani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5489,0,NULL,'pto','zo''é','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5490,0,NULL,'ptp','patep','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5491,0,NULL,'ptr','piamatsina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5492,0,NULL,'ptt','enrekang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5493,0,NULL,'ptu','bambam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5494,0,NULL,'ptv','port vato','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5495,0,NULL,'ptw','pentlatch','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5496,0,NULL,'pty','pathiya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5497,0,NULL,'pua','western highland purepecha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5498,0,NULL,'pub','purum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5499,0,NULL,'puc','punan merap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5500,0,NULL,'pud','punan aput','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5501,0,NULL,'pue','puelche','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5502,0,NULL,'puf','punan merah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5503,0,NULL,'pug','phuie','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5504,0,NULL,'pui','puinave','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5505,0,NULL,'puj','punan tubu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5506,0,NULL,'puk','pu ko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5507,0,NULL,'pum','puma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5508,0,NULL,'puo','puoc','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5509,0,NULL,'pup','pulabu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5510,0,NULL,'puq','puquina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5511,0,NULL,'pur','puruborá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5512,0,NULL,'put','putoh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5513,0,NULL,'puu','punu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5514,0,NULL,'puw','puluwatese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5515,0,NULL,'pux','puare','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5516,0,NULL,'puy','purisimeño','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5517,0,NULL,'puz','purum naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5518,0,NULL,'pwa','pawaia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5519,0,NULL,'pwb','panawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5520,0,NULL,'pwg','gapapaiwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5521,0,NULL,'pwm','molbog','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5522,0,NULL,'pwn','paiwan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5523,0,NULL,'pwo','pwo western karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5524,0,NULL,'pwr','powari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5525,0,NULL,'pww','pwo northern karen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5526,0,NULL,'pxm','quetzaltepec mixe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5527,0,NULL,'pye','pye krumen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5528,0,NULL,'pym','fyam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5529,0,NULL,'pyn','poyanáwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5530,0,NULL,'pys','lengua de señas del paraguay','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5530,0,NULL,'pys','paraguayan sign language','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5531,0,NULL,'pyu','puyuma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5532,0,NULL,'pyx','pyu (myanmar)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5533,0,NULL,'pyy','pyen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5534,0,NULL,'pzn','para naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5535,0,NULL,'qaa..qtz','private use','1129420800',NULL,NULL,NULL,NULL,NULL,'private-use',NULL); +INSERT INTO "iana_records" VALUES(5536,0,NULL,'qua','quapaw','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5537,0,NULL,'qub','huallaga huánuco quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5538,0,NULL,'quc','k''iche''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5538,0,NULL,'quc','quiché','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5539,0,NULL,'qud','calderón highland quichua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5540,0,NULL,'quf','lambayeque quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5541,0,NULL,'qug','chimborazo highland quichua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5542,0,NULL,'quh','south bolivian quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5543,0,NULL,'qui','quileute','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5544,0,NULL,'quk','chachapoyas quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5545,0,NULL,'qul','north bolivian quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5546,0,NULL,'qum','sipacapense','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5547,0,NULL,'qun','quinault','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5548,0,NULL,'qup','southern pastaza quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5549,0,NULL,'quq','quinqui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5550,0,NULL,'qur','yanahuanca pasco quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5551,0,NULL,'qus','santiago del estero quichua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5552,0,NULL,'quv','sacapulteco','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5553,0,NULL,'quw','tena lowland quichua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5554,0,NULL,'qux','yauyos quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5555,0,NULL,'quy','ayacucho quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5556,0,NULL,'quz','cusco quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5557,0,NULL,'qva','ambo-pasco quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5558,0,NULL,'qvc','cajamarca quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5559,0,NULL,'qve','eastern apurímac quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5560,0,NULL,'qvh','huamalíes-dos de mayo huánuco quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5561,0,NULL,'qvi','imbabura highland quichua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5562,0,NULL,'qvj','loja highland quichua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5563,0,NULL,'qvl','cajatambo north lima quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5564,0,NULL,'qvm','margos-yarowilca-lauricocha quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5565,0,NULL,'qvn','north junín quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5566,0,NULL,'qvo','napo lowland quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5567,0,NULL,'qvp','pacaraos quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5568,0,NULL,'qvs','san martín quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5569,0,NULL,'qvw','huaylla wanca quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5570,0,NULL,'qvy','queyu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5571,0,NULL,'qvz','northern pastaza quichua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5572,0,NULL,'qwa','corongo ancash quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5573,0,NULL,'qwc','classical quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5574,0,NULL,'qwe','quechuan (family)','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5575,0,NULL,'qwh','huaylas ancash quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5576,0,NULL,'qwm','kuman (russia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5577,0,NULL,'qws','sihuas ancash quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5578,0,NULL,'qwt','kwalhioqua-tlatskanai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5579,0,NULL,'qxa','chiquián ancash quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5580,0,NULL,'qxc','chincha quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5581,0,NULL,'qxh','panao huánuco quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5582,0,NULL,'qxl','salasaca highland quichua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5583,0,NULL,'qxn','northern conchucos ancash quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5584,0,NULL,'qxo','southern conchucos ancash quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5585,0,NULL,'qxp','puno quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5586,0,NULL,'qxq','qashqa''i','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5587,0,NULL,'qxr','cañar highland quichua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5588,0,NULL,'qxs','southern qiang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5589,0,NULL,'qxt','santa ana de tusi pasco quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5590,0,NULL,'qxu','arequipa-la unión quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5591,0,NULL,'qxw','jauja wanca quechua','1248825600',NULL,NULL,NULL,NULL,'qu',NULL,NULL); +INSERT INTO "iana_records" VALUES(5592,0,NULL,'qya','quenya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5593,0,NULL,'qyp','quiripi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5594,0,NULL,'raa','dungmali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5595,0,NULL,'rab','camling','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5596,0,NULL,'rac','rasawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5597,0,NULL,'rad','rade','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5598,0,NULL,'raf','western meohang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5599,0,NULL,'rag','logooli','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(5599,0,NULL,'rag','lulogooli','1248825600',NULL,NULL,NULL,NULL,'luy',NULL,NULL); +INSERT INTO "iana_records" VALUES(5600,0,NULL,'rah','rabha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5601,0,NULL,'rai','ramoaaina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5602,0,NULL,'raj','rajasthani','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(5603,0,NULL,'rak','tulu-bohuai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5604,0,NULL,'ral','ralte','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5605,0,NULL,'ram','canela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5606,0,NULL,'ran','riantana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5607,0,NULL,'rao','rao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5608,0,NULL,'rap','rapanui','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5609,0,NULL,'raq','saam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5610,0,NULL,'rar','cook islands maori','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5610,0,NULL,'rar','rarotongan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5611,0,NULL,'ras','tegali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5612,0,NULL,'rat','razajerdi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5613,0,NULL,'rau','raute','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5614,0,NULL,'rav','sampang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5615,0,NULL,'raw','rawang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5616,0,NULL,'rax','rang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5617,0,NULL,'ray','rapa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5618,0,NULL,'raz','rahambuu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5619,0,NULL,'rbb','rumai palaung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5620,0,NULL,'rbk','northern bontok','1268265600',NULL,NULL,NULL,NULL,'bnc',NULL,NULL); +INSERT INTO "iana_records" VALUES(5621,0,NULL,'rbl','miraya bikol','1268265600',NULL,NULL,NULL,NULL,'bik',NULL,NULL); +INSERT INTO "iana_records" VALUES(5622,0,NULL,'rcf','réunion creole french','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5623,0,NULL,'rdb','rudbari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5624,0,NULL,'rea','rerau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5625,0,NULL,'reb','rembong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5626,0,NULL,'ree','rejang kayan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5627,0,NULL,'reg','kara (tanzania)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5628,0,NULL,'rei','reli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5629,0,NULL,'rej','rejang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5630,0,NULL,'rel','rendille','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5631,0,NULL,'rem','remo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5632,0,NULL,'ren','rengao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5633,0,NULL,'rer','rer bare','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5634,0,NULL,'res','reshe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5635,0,NULL,'ret','retta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5636,0,NULL,'rey','reyesano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5637,0,NULL,'rga','roria','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5638,0,NULL,'rge','romano-greek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5639,0,NULL,'rgk','rangkas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5640,0,NULL,'rgn','romagnol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5641,0,NULL,'rgr','resígaro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5642,0,NULL,'rgs','southern roglai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5643,0,NULL,'rgu','ringgou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5644,0,NULL,'rhg','rohingya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5645,0,NULL,'rhp','yahang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5646,0,NULL,'ria','riang (india)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5647,0,NULL,'rie','rien','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5648,0,NULL,'rif','tarifit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5649,0,NULL,'ril','riang (myanmar)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5650,0,NULL,'rim','nyaturu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5651,0,NULL,'rin','nungu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5652,0,NULL,'rir','ribun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5653,0,NULL,'rit','ritarungo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5654,0,NULL,'riu','riung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5655,0,NULL,'rjg','rajong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5656,0,NULL,'rji','raji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5657,0,NULL,'rjs','rajbanshi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5658,0,NULL,'rka','kraol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5659,0,NULL,'rkb','rikbaktsa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5660,0,NULL,'rkh','rakahanga-manihiki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5661,0,NULL,'rki','rakhine','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5662,0,NULL,'rkm','marka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5663,0,NULL,'rkt','kamta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5663,0,NULL,'rkt','rangpuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5664,0,NULL,'rma','rama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5665,0,NULL,'rmb','rembarunga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5666,0,NULL,'rmc','carpathian romani','1248825600',NULL,NULL,NULL,NULL,'rom',NULL,NULL); +INSERT INTO "iana_records" VALUES(5667,0,NULL,'rmd','traveller danish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5668,0,NULL,'rme','angloromani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5669,0,NULL,'rmf','kalo finnish romani','1248825600',NULL,NULL,NULL,NULL,'rom',NULL,NULL); +INSERT INTO "iana_records" VALUES(5670,0,NULL,'rmg','traveller norwegian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5671,0,NULL,'rmh','murkim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5672,0,NULL,'rmi','lomavren','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5673,0,NULL,'rmk','romkun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5674,0,NULL,'rml','baltic romani','1248825600',NULL,NULL,NULL,NULL,'rom',NULL,NULL); +INSERT INTO "iana_records" VALUES(5675,0,NULL,'rmm','roma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5676,0,NULL,'rmn','balkan romani','1248825600',NULL,NULL,NULL,NULL,'rom',NULL,NULL); +INSERT INTO "iana_records" VALUES(5677,0,NULL,'rmo','sinte romani','1248825600',NULL,NULL,NULL,NULL,'rom',NULL,NULL); +INSERT INTO "iana_records" VALUES(5678,0,NULL,'rmp','rempi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5679,0,NULL,'rmq','caló','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5680,0,NULL,'rmr','caló','1248825600',1268265600,NULL,NULL,NULL,NULL,NULL,'see emx, rmq'); +INSERT INTO "iana_records" VALUES(5681,0,NULL,'rms','romanian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5682,0,NULL,'rmt','domari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5683,0,NULL,'rmu','tavringer romani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5684,0,NULL,'rmv','romanova','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5685,0,NULL,'rmw','welsh romani','1248825600',NULL,NULL,NULL,NULL,'rom',NULL,NULL); +INSERT INTO "iana_records" VALUES(5686,0,NULL,'rmx','romam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5687,0,NULL,'rmy','vlax romani','1248825600',NULL,NULL,NULL,NULL,'rom',NULL,NULL); +INSERT INTO "iana_records" VALUES(5688,0,NULL,'rmz','marma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5689,0,NULL,'rna','runa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5690,0,NULL,'rnd','ruund','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5691,0,NULL,'rng','ronga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5692,0,NULL,'rnl','ranglong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5693,0,NULL,'rnn','roon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5694,0,NULL,'rnp','rongpo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5695,0,NULL,'rnw','rungwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5696,0,NULL,'roa','romance languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5697,0,NULL,'rob','tae''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5698,0,NULL,'roc','cacgia roglai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5699,0,NULL,'rod','rogo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5700,0,NULL,'roe','ronji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5701,0,NULL,'rof','rombo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5702,0,NULL,'rog','northern roglai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5703,0,NULL,'rol','romblomanon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5704,0,NULL,'rom','romany','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(5705,0,NULL,'roo','rotokas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5706,0,NULL,'rop','kriol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5707,0,NULL,'ror','rongga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5708,0,NULL,'rou','runga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5709,0,NULL,'row','dela-oenale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5710,0,NULL,'rpn','repanbitip','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5711,0,NULL,'rpt','rapting','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5712,0,NULL,'rri','ririo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5713,0,NULL,'rro','waima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5714,0,NULL,'rsb','romano-serbian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5715,0,NULL,'rsi','rennellese sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5716,0,NULL,'rsl','russian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5717,0,NULL,'rth','ratahan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5718,0,NULL,'rtm','rotuman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5719,0,NULL,'rtw','rathawi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5720,0,NULL,'rub','gungu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5721,0,NULL,'ruc','ruuli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5722,0,NULL,'rue','rusyn','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5723,0,NULL,'ruf','luguru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5724,0,NULL,'rug','roviana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5725,0,NULL,'ruh','ruga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5726,0,NULL,'rui','rufiji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5727,0,NULL,'ruk','che','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5728,0,NULL,'ruo','istro romanian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5729,0,NULL,'rup','aromanian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5729,0,NULL,'rup','arumanian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5729,0,NULL,'rup','macedo-romanian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5730,0,NULL,'ruq','megleno romanian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5731,0,NULL,'rut','rutul','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5732,0,NULL,'ruu','lanas lobu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5733,0,NULL,'ruy','mala (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5734,0,NULL,'ruz','ruma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5735,0,NULL,'rwa','rawo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5736,0,NULL,'rwk','rwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5737,0,NULL,'rwm','amba (uganda)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5738,0,NULL,'rwo','rawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5739,0,NULL,'rwr','marwari (india)','1248825600',NULL,NULL,NULL,NULL,'mwr',NULL,NULL); +INSERT INTO "iana_records" VALUES(5740,0,NULL,'ryn','northern amami-oshima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5741,0,NULL,'rys','yaeyama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5742,0,NULL,'ryu','central okinawan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5743,0,NULL,'saa','saba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5744,0,NULL,'sab','buglere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5745,0,NULL,'sac','meskwaki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5746,0,NULL,'sad','sandawe','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5747,0,NULL,'sae','sabanê','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5748,0,NULL,'saf','safaliba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5749,0,NULL,'sah','yakut','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5750,0,NULL,'sai','south american indian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5751,0,NULL,'saj','sahu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5752,0,NULL,'sak','sake','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5753,0,NULL,'sal','salishan languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5754,0,NULL,'sam','samaritan aramaic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5755,0,NULL,'sao','sause','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5756,0,NULL,'sap','sanapaná','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5757,0,NULL,'saq','samburu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5758,0,NULL,'sar','saraveca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5759,0,NULL,'sas','sasak','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5760,0,NULL,'sat','santali','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5761,0,NULL,'sau','saleman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5762,0,NULL,'sav','saafi-saafi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5763,0,NULL,'saw','sawi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5764,0,NULL,'sax','sa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5765,0,NULL,'say','saya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5766,0,NULL,'saz','saurashtra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5767,0,NULL,'sba','ngambay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5768,0,NULL,'sbb','simbo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5769,0,NULL,'sbc','kele (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5770,0,NULL,'sbd','southern samo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5771,0,NULL,'sbe','saliba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5772,0,NULL,'sbf','shabo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5773,0,NULL,'sbg','seget','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5774,0,NULL,'sbh','sori-harengan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5775,0,NULL,'sbi','seti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5776,0,NULL,'sbj','surbakhal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5777,0,NULL,'sbk','safwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5778,0,NULL,'sbl','botolan sambal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5779,0,NULL,'sbm','sagala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5780,0,NULL,'sbn','sindhi bhil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5781,0,NULL,'sbo','sabüm','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5782,0,NULL,'sbp','sangu (tanzania)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5783,0,NULL,'sbq','sileibi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5784,0,NULL,'sbr','sembakung murut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5785,0,NULL,'sbs','subiya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5786,0,NULL,'sbt','kimki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5787,0,NULL,'sbu','stod bhoti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5788,0,NULL,'sbv','sabine','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5789,0,NULL,'sbw','simba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5790,0,NULL,'sbx','seberuang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5791,0,NULL,'sby','soli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5792,0,NULL,'sbz','sara kaba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5793,0,NULL,'sca','sansu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5794,0,NULL,'scb','chut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5795,0,NULL,'sce','dongxiang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5796,0,NULL,'scf','san miguel creole french','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5797,0,NULL,'scg','sanggau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5798,0,NULL,'sch','sakachep','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5799,0,NULL,'sci','sri lankan creole malay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5800,0,NULL,'sck','sadri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5801,0,NULL,'scl','shina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5802,0,NULL,'scn','sicilian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5803,0,NULL,'sco','scots','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5804,0,NULL,'scp','helambu sherpa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5805,0,NULL,'scq','sa''och','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5806,0,NULL,'scs','north slavey','1248825600',NULL,NULL,NULL,NULL,'den',NULL,NULL); +INSERT INTO "iana_records" VALUES(5807,0,NULL,'scu','shumcho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5808,0,NULL,'scv','sheni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5809,0,NULL,'scw','sha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5810,0,NULL,'scx','sicel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5811,0,NULL,'sda','toraja-sa''dan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5812,0,NULL,'sdb','shabak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5813,0,NULL,'sdc','sassarese sardinian','1248825600',NULL,NULL,NULL,NULL,'sc',NULL,NULL); +INSERT INTO "iana_records" VALUES(5814,0,NULL,'sde','surubu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5815,0,NULL,'sdf','sarli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5816,0,NULL,'sdg','savi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5817,0,NULL,'sdh','southern kurdish','1248825600',NULL,NULL,NULL,NULL,'ku',NULL,NULL); +INSERT INTO "iana_records" VALUES(5818,0,NULL,'sdj','suundi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5819,0,NULL,'sdk','sos kundi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5820,0,NULL,'sdl','saudi arabian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5821,0,NULL,'sdm','semandang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5822,0,NULL,'sdn','gallurese sardinian','1248825600',NULL,NULL,NULL,NULL,'sc',NULL,NULL); +INSERT INTO "iana_records" VALUES(5823,0,NULL,'sdo','bukar-sadung bidayuh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5824,0,NULL,'sdp','sherdukpen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5825,0,NULL,'sdr','oraon sadri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5826,0,NULL,'sds','sened','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5827,0,NULL,'sdt','shuadit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5828,0,NULL,'sdu','sarudu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5829,0,NULL,'sdv','eastern sudanic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5830,0,NULL,'sdx','sibu melanau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5831,0,NULL,'sdz','sallands','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5832,0,NULL,'sea','semai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5833,0,NULL,'seb','shempire senoufo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5834,0,NULL,'sec','sechelt','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5835,0,NULL,'sed','sedang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5836,0,NULL,'see','seneca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5837,0,NULL,'sef','cebaara senoufo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5838,0,NULL,'seg','segeju','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5839,0,NULL,'seh','sena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5840,0,NULL,'sei','seri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5841,0,NULL,'sej','sene','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5842,0,NULL,'sek','sekani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5843,0,NULL,'sel','selkup','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5844,0,NULL,'sem','semitic languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5845,0,NULL,'sen','nanerigé sénoufo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5846,0,NULL,'seo','suarmin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5847,0,NULL,'sep','sìcìté sénoufo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5848,0,NULL,'seq','senara sénoufo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5849,0,NULL,'ser','serrano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5850,0,NULL,'ses','koyraboro senni songhai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5851,0,NULL,'set','sentani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5852,0,NULL,'seu','serui-laut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5853,0,NULL,'sev','nyarafolo senoufo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5854,0,NULL,'sew','sewa bay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5855,0,NULL,'sey','secoya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5856,0,NULL,'sez','senthang chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5857,0,NULL,'sfb','french belgian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5857,0,NULL,'sfb','langue des signes de belgique francophone','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5858,0,NULL,'sfm','small flowery miao','1248825600',NULL,NULL,NULL,NULL,'hmn',NULL,NULL); +INSERT INTO "iana_records" VALUES(5859,0,NULL,'sfs','south african sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5860,0,NULL,'sfw','sehwi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5861,0,NULL,'sga','old irish (to 900)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5862,0,NULL,'sgb','mag-antsi ayta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5863,0,NULL,'sgc','kipsigis','1248825600',NULL,NULL,NULL,NULL,'kln',NULL,NULL); +INSERT INTO "iana_records" VALUES(5864,0,NULL,'sgd','surigaonon','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5865,0,NULL,'sge','segai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5866,0,NULL,'sgg','swiss-german sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5867,0,NULL,'sgh','shughni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5868,0,NULL,'sgi','suga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5869,0,NULL,'sgk','sangkong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5870,0,NULL,'sgl','sanglechi-ishkashimi','1248825600',1268265600,NULL,NULL,NULL,NULL,NULL,'see isk, sgy'); +INSERT INTO "iana_records" VALUES(5871,0,NULL,'sgm','singa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5872,0,NULL,'sgn','sign languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5873,0,NULL,'sgo','songa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5874,0,NULL,'sgp','singpho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5875,0,NULL,'sgr','sangisari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5876,0,NULL,'sgt','brokpake','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5877,0,NULL,'sgu','salas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5878,0,NULL,'sgw','sebat bet gurage','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5879,0,NULL,'sgx','sierra leone sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5880,0,NULL,'sgy','sanglechi','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5881,0,NULL,'sgz','sursurunga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5882,0,NULL,'sha','shall-zwall','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5883,0,NULL,'shb','ninam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5884,0,NULL,'shc','sonde','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5885,0,NULL,'shd','kundal shahi','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5886,0,NULL,'she','sheko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5887,0,NULL,'shg','shua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5888,0,NULL,'shh','shoshoni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5889,0,NULL,'shi','tachelhit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5890,0,NULL,'shj','shatt','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5891,0,NULL,'shk','shilluk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5892,0,NULL,'shl','shendu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5893,0,NULL,'shm','shahrudi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5894,0,NULL,'shn','shan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5895,0,NULL,'sho','shanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5896,0,NULL,'shp','shipibo-conibo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5897,0,NULL,'shq','sala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5898,0,NULL,'shr','shi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5899,0,NULL,'shs','shuswap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5900,0,NULL,'sht','shasta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5901,0,NULL,'shu','chadian arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(5902,0,NULL,'shv','shehri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5903,0,NULL,'shw','shwai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5904,0,NULL,'shx','she','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5905,0,NULL,'shy','tachawit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5906,0,NULL,'shz','syenara senoufo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5907,0,NULL,'sia','akkala sami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5908,0,NULL,'sib','sebop','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5909,0,NULL,'sid','sidamo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5910,0,NULL,'sie','simaa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5911,0,NULL,'sif','siamou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5912,0,NULL,'sig','paasaal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5913,0,NULL,'sih','zire','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5914,0,NULL,'sii','shom peng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5915,0,NULL,'sij','numbami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5916,0,NULL,'sik','sikiana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5917,0,NULL,'sil','tumulung sisaala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5918,0,NULL,'sim','mende (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5919,0,NULL,'sio','siouan languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5920,0,NULL,'sip','sikkimese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5921,0,NULL,'siq','sonia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5922,0,NULL,'sir','siri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5923,0,NULL,'sis','siuslaw','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5924,0,NULL,'sit','sino-tibetan languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5925,0,NULL,'siu','sinagen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5926,0,NULL,'siv','sumariup','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5927,0,NULL,'siw','siwai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5928,0,NULL,'six','sumau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5929,0,NULL,'siy','sivandi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5930,0,NULL,'siz','siwi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5931,0,NULL,'sja','epena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5932,0,NULL,'sjb','sajau basap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5933,0,NULL,'sjd','kildin sami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5934,0,NULL,'sje','pite sami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5935,0,NULL,'sjg','assangori','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5936,0,NULL,'sjk','kemi sami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5937,0,NULL,'sjl','miji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5937,0,NULL,'sjl','sajalong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5938,0,NULL,'sjm','mapun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5939,0,NULL,'sjn','sindarin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5940,0,NULL,'sjo','xibe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5941,0,NULL,'sjp','surjapuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5942,0,NULL,'sjr','siar-lak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5943,0,NULL,'sjs','senhaja de srair','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5944,0,NULL,'sjt','ter sami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5945,0,NULL,'sju','ume sami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5946,0,NULL,'sjw','shawnee','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5947,0,NULL,'ska','skagit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5948,0,NULL,'skb','saek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5949,0,NULL,'skc','sauk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5950,0,NULL,'skd','southern sierra miwok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5951,0,NULL,'ske','seke (vanuatu)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5952,0,NULL,'skf','sakirabiá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5953,0,NULL,'skg','sakalava malagasy','1248825600',NULL,NULL,NULL,NULL,'mg',NULL,NULL); +INSERT INTO "iana_records" VALUES(5954,0,NULL,'skh','sikule','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5955,0,NULL,'ski','sika','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5956,0,NULL,'skj','seke (nepal)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5957,0,NULL,'skk','sok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5958,0,NULL,'skm','sakam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5959,0,NULL,'skn','kolibugan subanon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5960,0,NULL,'sko','seko tengah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5961,0,NULL,'skp','sekapan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5962,0,NULL,'skq','sininkere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5963,0,NULL,'skr','seraiki','1248825600',NULL,NULL,NULL,NULL,'lah',NULL,NULL); +INSERT INTO "iana_records" VALUES(5964,0,NULL,'sks','maia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5965,0,NULL,'skt','sakata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5966,0,NULL,'sku','sakao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5967,0,NULL,'skv','skou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5968,0,NULL,'skw','skepi creole dutch','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5969,0,NULL,'skx','seko padang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5970,0,NULL,'sky','sikaiana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5971,0,NULL,'skz','sekar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5972,0,NULL,'sla','slavic languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(5973,0,NULL,'slc','sáliba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5974,0,NULL,'sld','sissala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5975,0,NULL,'sle','sholaga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5976,0,NULL,'slf','swiss-italian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5977,0,NULL,'slg','selungai murut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5978,0,NULL,'slh','southern puget sound salish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5979,0,NULL,'sli','lower silesian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5980,0,NULL,'slj','salumá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5981,0,NULL,'sll','salt-yui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5982,0,NULL,'slm','pangutaran sama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5983,0,NULL,'sln','salinan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5984,0,NULL,'slp','lamaholot','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5985,0,NULL,'slq','salchuq','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5986,0,NULL,'slr','salar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5987,0,NULL,'sls','singapore sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5988,0,NULL,'slt','sila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5989,0,NULL,'slu','selaru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5990,0,NULL,'slw','sialum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5991,0,NULL,'slx','salampasu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5992,0,NULL,'sly','selayar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5993,0,NULL,'slz','ma''ya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5994,0,NULL,'sma','southern sami','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5995,0,NULL,'smb','simbari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5996,0,NULL,'smc','som','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5997,0,NULL,'smd','sama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5998,0,NULL,'smf','auwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(5999,0,NULL,'smg','simbali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6000,0,NULL,'smh','samei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6001,0,NULL,'smi','sami languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(6002,0,NULL,'smj','lule sami','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6003,0,NULL,'smk','bolinao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6004,0,NULL,'sml','central sama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6005,0,NULL,'smm','musasa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6006,0,NULL,'smn','inari sami','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6007,0,NULL,'smp','samaritan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6008,0,NULL,'smq','samo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6009,0,NULL,'smr','simeulue','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6010,0,NULL,'sms','skolt sami','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6011,0,NULL,'smt','simte','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6012,0,NULL,'smu','somray','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6013,0,NULL,'smv','samvedi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6014,0,NULL,'smw','sumbawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6015,0,NULL,'smx','samba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6016,0,NULL,'smy','semnani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6017,0,NULL,'smz','simeku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6018,0,NULL,'snb','sebuyau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6019,0,NULL,'snc','sinaugoro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6020,0,NULL,'sne','bau bidayuh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6021,0,NULL,'snf','noon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6022,0,NULL,'sng','sanga (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6023,0,NULL,'snh','shinabo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6024,0,NULL,'sni','sensi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6025,0,NULL,'snj','riverain sango','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6026,0,NULL,'snk','soninke','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6027,0,NULL,'snl','sangil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6028,0,NULL,'snm','southern ma''di','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6029,0,NULL,'snn','siona','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6030,0,NULL,'sno','snohomish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6031,0,NULL,'snp','siane','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6032,0,NULL,'snq','sangu (gabon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6033,0,NULL,'snr','sihan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6034,0,NULL,'sns','nahavaq','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6034,0,NULL,'sns','south west bay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6035,0,NULL,'snu','senggi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6035,0,NULL,'snu','viid','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6036,0,NULL,'snv','sa''ban','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6037,0,NULL,'snw','selee','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6038,0,NULL,'snx','sam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6039,0,NULL,'sny','saniyo-hiyewe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6040,0,NULL,'snz','sinsauru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6041,0,NULL,'soa','thai song','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6042,0,NULL,'sob','sobei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6043,0,NULL,'soc','so (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6044,0,NULL,'sod','songoora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6045,0,NULL,'soe','songomeno','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6046,0,NULL,'sog','sogdian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6047,0,NULL,'soh','aka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6048,0,NULL,'soi','sonha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6049,0,NULL,'soj','soi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6050,0,NULL,'sok','sokoro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6051,0,NULL,'sol','solos','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6052,0,NULL,'son','songhai languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(6053,0,NULL,'soo','songo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6054,0,NULL,'sop','songe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6055,0,NULL,'soq','kanasi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6056,0,NULL,'sor','somrai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6057,0,NULL,'sos','seeku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6058,0,NULL,'sou','southern thai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6059,0,NULL,'sov','sonsorol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6060,0,NULL,'sow','sowanda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6061,0,NULL,'sox','so (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6062,0,NULL,'soy','miyobe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6063,0,NULL,'soz','temi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6064,0,NULL,'spb','sepa (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6065,0,NULL,'spc','sapé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6066,0,NULL,'spd','saep','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6067,0,NULL,'spe','sepa (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6068,0,NULL,'spg','sian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6069,0,NULL,'spi','saponi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6070,0,NULL,'spk','sengo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6071,0,NULL,'spl','selepet','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6072,0,NULL,'spm','sepen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6073,0,NULL,'spo','spokane','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6074,0,NULL,'spp','supyire senoufo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6075,0,NULL,'spq','loreto-ucayali spanish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6076,0,NULL,'spr','saparua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6077,0,NULL,'sps','saposa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6078,0,NULL,'spt','spiti bhoti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6079,0,NULL,'spu','sapuan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6080,0,NULL,'spx','south picene','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6081,0,NULL,'spy','sabaot','1248825600',NULL,NULL,NULL,NULL,'kln',NULL,NULL); +INSERT INTO "iana_records" VALUES(6082,0,NULL,'sqa','shama-sambuga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6083,0,NULL,'sqh','shau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6084,0,NULL,'sqj','albanian languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(6085,0,NULL,'sqm','suma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6086,0,NULL,'sqn','susquehannock','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6087,0,NULL,'sqo','sorkhei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6088,0,NULL,'sqq','sou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6089,0,NULL,'sqr','siculo arabic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6090,0,NULL,'sqs','sri lankan sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6091,0,NULL,'sqt','soqotri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6092,0,NULL,'squ','squamish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6093,0,NULL,'sra','saruga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6094,0,NULL,'srb','sora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6095,0,NULL,'src','logudorese sardinian','1248825600',NULL,NULL,NULL,NULL,'sc',NULL,NULL); +INSERT INTO "iana_records" VALUES(6096,0,NULL,'sre','sara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6097,0,NULL,'srf','nafi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6098,0,NULL,'srg','sulod','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6099,0,NULL,'srh','sarikoli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6100,0,NULL,'sri','siriano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6101,0,NULL,'srk','serudung murut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6102,0,NULL,'srl','isirawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6103,0,NULL,'srm','saramaccan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6104,0,NULL,'srn','sranan tongo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6105,0,NULL,'sro','campidanese sardinian','1248825600',NULL,NULL,NULL,NULL,'sc',NULL,NULL); +INSERT INTO "iana_records" VALUES(6106,0,NULL,'srq','sirionó','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6107,0,NULL,'srr','serer','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6108,0,NULL,'srs','sarsi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6109,0,NULL,'srt','sauri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6110,0,NULL,'sru','suruí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6111,0,NULL,'srv','southern sorsoganon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6112,0,NULL,'srw','serua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6113,0,NULL,'srx','sirmauri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6114,0,NULL,'sry','sera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6115,0,NULL,'srz','shahmirzadi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6116,0,NULL,'ssa','nilo-saharan languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(6117,0,NULL,'ssb','southern sama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6118,0,NULL,'ssc','suba-simbiti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6119,0,NULL,'ssd','siroi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6120,0,NULL,'sse','balangingi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6120,0,NULL,'sse','bangingih sama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6121,0,NULL,'ssf','thao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6122,0,NULL,'ssg','seimat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6123,0,NULL,'ssh','shihhi arabic','1248825600',NULL,NULL,NULL,NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(6124,0,NULL,'ssi','sansi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6125,0,NULL,'ssj','sausi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6126,0,NULL,'ssk','sunam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6127,0,NULL,'ssl','western sisaala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6128,0,NULL,'ssm','semnam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6129,0,NULL,'ssn','waata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6130,0,NULL,'sso','sissano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6131,0,NULL,'ssp','spanish sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6132,0,NULL,'ssq','so''a','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6133,0,NULL,'ssr','swiss-french sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6134,0,NULL,'sss','sô','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6135,0,NULL,'sst','sinasina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6136,0,NULL,'ssu','susuami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6137,0,NULL,'ssv','shark bay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6138,0,NULL,'ssx','samberigi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6139,0,NULL,'ssy','saho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6140,0,NULL,'ssz','sengseng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6141,0,NULL,'sta','settla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6142,0,NULL,'stb','northern subanen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6143,0,NULL,'std','sentinel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6144,0,NULL,'ste','liana-seti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6145,0,NULL,'stf','seta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6146,0,NULL,'stg','trieng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6147,0,NULL,'sth','shelta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6148,0,NULL,'sti','bulo stieng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6149,0,NULL,'stj','matya samo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6150,0,NULL,'stk','arammba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6151,0,NULL,'stl','stellingwerfs','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6152,0,NULL,'stm','setaman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6153,0,NULL,'stn','owa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6154,0,NULL,'sto','stoney','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6155,0,NULL,'stp','southeastern tepehuan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6156,0,NULL,'stq','saterfriesisch','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6157,0,NULL,'str','straits salish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6158,0,NULL,'sts','shumashti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6159,0,NULL,'stt','budeh stieng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6160,0,NULL,'stu','samtao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6161,0,NULL,'stv','silt''e','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6162,0,NULL,'stw','satawalese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6163,0,NULL,'sua','sulka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6164,0,NULL,'sub','suku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6165,0,NULL,'suc','western subanon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6166,0,NULL,'sue','suena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6167,0,NULL,'sug','suganga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6168,0,NULL,'sui','suki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6169,0,NULL,'suj','shubi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6170,0,NULL,'suk','sukuma','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6171,0,NULL,'sul','surigaonon','1248825600',1268265600,NULL,NULL,NULL,NULL,NULL,'see sgd, tgn'); +INSERT INTO "iana_records" VALUES(6172,0,NULL,'sum','sumo-mayangna','1248825600',1268265600,NULL,NULL,NULL,NULL,NULL,'see ulw, yan'); +INSERT INTO "iana_records" VALUES(6173,0,NULL,'suq','suri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6174,0,NULL,'sur','mwaghavul','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6175,0,NULL,'sus','susu','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6176,0,NULL,'sut','subtiaba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6177,0,NULL,'suv','sulung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6178,0,NULL,'suw','sumbwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6179,0,NULL,'sux','sumerian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6180,0,NULL,'suy','suyá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6181,0,NULL,'suz','sunwar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6182,0,NULL,'sva','svan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6183,0,NULL,'svb','ulau-suain','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6184,0,NULL,'svc','vincentian creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6185,0,NULL,'sve','serili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6186,0,NULL,'svk','slovakian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6187,0,NULL,'svr','savara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6188,0,NULL,'svs','savosavo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6189,0,NULL,'svx','skalvian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6190,0,NULL,'swb','maore comorian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6191,0,NULL,'swc','congo swahili','1248825600',NULL,NULL,NULL,NULL,'sw',NULL,NULL); +INSERT INTO "iana_records" VALUES(6192,0,NULL,'swf','sere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6193,0,NULL,'swg','swabian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6194,0,NULL,'swh','kiswahili','1248825600',NULL,NULL,NULL,NULL,'sw',NULL,NULL); +INSERT INTO "iana_records" VALUES(6194,0,NULL,'swh','swahili (individual language)','1248825600',NULL,NULL,NULL,NULL,'sw',NULL,NULL); +INSERT INTO "iana_records" VALUES(6195,0,NULL,'swi','sui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6196,0,NULL,'swj','sira','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6197,0,NULL,'swk','malawi sena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6198,0,NULL,'swl','swedish sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6199,0,NULL,'swm','samosa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6200,0,NULL,'swn','sawknah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6201,0,NULL,'swo','shanenawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6202,0,NULL,'swp','suau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6203,0,NULL,'swq','sharwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6204,0,NULL,'swr','saweru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6205,0,NULL,'sws','seluwasan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6206,0,NULL,'swt','sawila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6207,0,NULL,'swu','suwawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6208,0,NULL,'swv','shekhawati','1248825600',NULL,NULL,NULL,NULL,'mwr',NULL,NULL); +INSERT INTO "iana_records" VALUES(6209,0,NULL,'sww','sowa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6210,0,NULL,'swx','suruahá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6211,0,NULL,'swy','sarua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6212,0,NULL,'sxb','suba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6213,0,NULL,'sxc','sicanian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6214,0,NULL,'sxe','sighu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6215,0,NULL,'sxg','shixing','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6216,0,NULL,'sxk','southern kalapuya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6217,0,NULL,'sxl','selian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6218,0,NULL,'sxm','samre','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6219,0,NULL,'sxn','sangir','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6220,0,NULL,'sxo','sorothaptic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6221,0,NULL,'sxr','saaroa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6222,0,NULL,'sxs','sasaru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6223,0,NULL,'sxu','upper saxon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6224,0,NULL,'sxw','saxwe gbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6225,0,NULL,'sya','siang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6226,0,NULL,'syb','central subanen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6227,0,NULL,'syc','classical syriac','1175558400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6228,0,NULL,'syd','samoyedic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(6229,0,NULL,'syi','seki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6230,0,NULL,'syk','sukur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6231,0,NULL,'syl','sylheti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6232,0,NULL,'sym','maya samo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6233,0,NULL,'syn','senaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6234,0,NULL,'syo','suoy','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6235,0,NULL,'syr','syriac','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(6236,0,NULL,'sys','sinyar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6237,0,NULL,'syw','kagate','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6238,0,NULL,'syy','al-sayyid bedouin sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6239,0,NULL,'sza','semelai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6240,0,NULL,'szb','ngalum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6241,0,NULL,'szc','semaq beri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6242,0,NULL,'szd','seru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6243,0,NULL,'sze','seze','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6244,0,NULL,'szg','sengele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6245,0,NULL,'szl','silesian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6246,0,NULL,'szn','sula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6247,0,NULL,'szp','suabo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6248,0,NULL,'szv','isu (fako division)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6249,0,NULL,'szw','sawai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6250,0,NULL,'taa','lower tanana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6251,0,NULL,'tab','tabassaran','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6252,0,NULL,'tac','lowland tarahumara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6253,0,NULL,'tad','tause','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6254,0,NULL,'tae','tariana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6255,0,NULL,'taf','tapirapé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6256,0,NULL,'tag','tagoi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6257,0,NULL,'tai','tai languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(6258,0,NULL,'taj','eastern tamang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6259,0,NULL,'tak','tala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6260,0,NULL,'tal','tal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6261,0,NULL,'tan','tangale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6262,0,NULL,'tao','yami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6263,0,NULL,'tap','taabwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6264,0,NULL,'taq','tamasheq','1248825600',NULL,NULL,NULL,NULL,'tmh',NULL,NULL); +INSERT INTO "iana_records" VALUES(6265,0,NULL,'tar','central tarahumara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6266,0,NULL,'tas','tay boi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6267,0,NULL,'tau','upper tanana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6268,0,NULL,'tav','tatuyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6269,0,NULL,'taw','tai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6270,0,NULL,'tax','tamki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6271,0,NULL,'tay','atayal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6272,0,NULL,'taz','tocho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6273,0,NULL,'tba','aikanã','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6274,0,NULL,'tbb','tapeba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6275,0,NULL,'tbc','takia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6276,0,NULL,'tbd','kaki ae','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6277,0,NULL,'tbe','tanimbili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6278,0,NULL,'tbf','mandara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6279,0,NULL,'tbg','north tairora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6280,0,NULL,'tbh','thurawal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6281,0,NULL,'tbi','gaam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6282,0,NULL,'tbj','tiang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6283,0,NULL,'tbk','calamian tagbanwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6284,0,NULL,'tbl','tboli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6285,0,NULL,'tbm','tagbu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6286,0,NULL,'tbn','barro negro tunebo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6287,0,NULL,'tbo','tawala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6288,0,NULL,'tbp','diebroud','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6288,0,NULL,'tbp','taworta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6289,0,NULL,'tbq','tibeto-burman languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(6290,0,NULL,'tbr','tumtum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6291,0,NULL,'tbs','tanguat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6292,0,NULL,'tbt','tembo (kitembo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6293,0,NULL,'tbu','tubar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6294,0,NULL,'tbv','tobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6295,0,NULL,'tbw','tagbanwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6296,0,NULL,'tbx','kapin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6297,0,NULL,'tby','tabaru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6298,0,NULL,'tbz','ditammari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6299,0,NULL,'tca','ticuna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6300,0,NULL,'tcb','tanacross','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6301,0,NULL,'tcc','datooga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6302,0,NULL,'tcd','tafi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6303,0,NULL,'tce','southern tutchone','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6304,0,NULL,'tcf','malinaltepec me''phaa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6304,0,NULL,'tcf','malinaltepec tlapanec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6305,0,NULL,'tcg','tamagario','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6306,0,NULL,'tch','turks and caicos creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6307,0,NULL,'tci','wára','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6308,0,NULL,'tck','tchitchege','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6309,0,NULL,'tcl','taman (myanmar)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6310,0,NULL,'tcm','tanahmerah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6311,0,NULL,'tcn','tichurong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6312,0,NULL,'tco','taungyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6313,0,NULL,'tcp','tawr chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6314,0,NULL,'tcq','kaiy','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6315,0,NULL,'tcs','torres strait creole','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6316,0,NULL,'tct','t''en','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6317,0,NULL,'tcu','southeastern tarahumara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6318,0,NULL,'tcw','tecpatlán totonac','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6319,0,NULL,'tcx','toda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6320,0,NULL,'tcy','tulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6321,0,NULL,'tcz','thado chin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6322,0,NULL,'tda','tagdal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6323,0,NULL,'tdb','panchpargania','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6324,0,NULL,'tdc','emberá-tadó','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6325,0,NULL,'tdd','tai nüa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6326,0,NULL,'tde','tiranige diga dogon','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6327,0,NULL,'tdf','talieng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6328,0,NULL,'tdg','western tamang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6329,0,NULL,'tdh','thulung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6330,0,NULL,'tdi','tomadino','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6331,0,NULL,'tdj','tajio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6332,0,NULL,'tdk','tambas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6333,0,NULL,'tdl','sur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6334,0,NULL,'tdn','tondano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6335,0,NULL,'tdo','teme','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6336,0,NULL,'tdq','tita','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6337,0,NULL,'tdr','todrah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6338,0,NULL,'tds','doutai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6339,0,NULL,'tdt','tetun dili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6340,0,NULL,'tdu','tempasuk dusun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6341,0,NULL,'tdv','toro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6342,0,NULL,'tdx','tandroy-mahafaly malagasy','1248825600',NULL,NULL,NULL,NULL,'mg',NULL,NULL); +INSERT INTO "iana_records" VALUES(6343,0,NULL,'tdy','tadyawan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6344,0,NULL,'tea','temiar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6345,0,NULL,'teb','tetete','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6346,0,NULL,'tec','terik','1248825600',NULL,NULL,NULL,NULL,'kln',NULL,NULL); +INSERT INTO "iana_records" VALUES(6347,0,NULL,'ted','tepo krumen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6348,0,NULL,'tee','huehuetla tepehua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6349,0,NULL,'tef','teressa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6350,0,NULL,'teg','teke-tege','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6351,0,NULL,'teh','tehuelche','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6352,0,NULL,'tei','torricelli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6353,0,NULL,'tek','ibali teke','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6354,0,NULL,'tem','timne','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6355,0,NULL,'ten','tama (colombia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6356,0,NULL,'teo','teso','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6357,0,NULL,'tep','tepecano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6358,0,NULL,'teq','temein','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6359,0,NULL,'ter','tereno','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6360,0,NULL,'tes','tengger','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6361,0,NULL,'tet','tetum','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6362,0,NULL,'teu','soo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6363,0,NULL,'tev','teor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6364,0,NULL,'tew','tewa (usa)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6365,0,NULL,'tex','tennet','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6366,0,NULL,'tey','tulishi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6367,0,NULL,'tfi','tofin gbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6368,0,NULL,'tfn','tanaina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6369,0,NULL,'tfo','tefaro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6370,0,NULL,'tfr','teribe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6371,0,NULL,'tft','ternate','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6372,0,NULL,'tga','sagalla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6373,0,NULL,'tgb','tobilung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6374,0,NULL,'tgc','tigak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6375,0,NULL,'tgd','ciwogai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6376,0,NULL,'tge','eastern gorkha tamang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6377,0,NULL,'tgf','chalikha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6378,0,NULL,'tgg','tangga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6379,0,NULL,'tgh','tobagonian creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6380,0,NULL,'tgi','lawunuia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6381,0,NULL,'tgn','tandaganon','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6382,0,NULL,'tgo','sudest','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6383,0,NULL,'tgp','tangoa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6384,0,NULL,'tgq','tring','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6385,0,NULL,'tgr','tareng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6386,0,NULL,'tgs','nume','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6387,0,NULL,'tgt','central tagbanwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6388,0,NULL,'tgu','tanggu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6389,0,NULL,'tgv','tingui-boto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6390,0,NULL,'tgw','tagwana senoufo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6391,0,NULL,'tgx','tagish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6392,0,NULL,'tgy','togoyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6393,0,NULL,'thc','tai hang tong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6394,0,NULL,'thd','thayore','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6395,0,NULL,'the','chitwania tharu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6396,0,NULL,'thf','thangmi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6397,0,NULL,'thh','northern tarahumara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6398,0,NULL,'thi','tai long','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6399,0,NULL,'thk','kitharaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6399,0,NULL,'thk','tharaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6400,0,NULL,'thl','dangaura tharu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6401,0,NULL,'thm','aheu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6402,0,NULL,'thn','thachanadan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6403,0,NULL,'thp','thompson','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6404,0,NULL,'thq','kochila tharu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6405,0,NULL,'thr','rana tharu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6406,0,NULL,'ths','thakali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6407,0,NULL,'tht','tahltan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6408,0,NULL,'thu','thuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6409,0,NULL,'thv','tahaggart tamahaq','1248825600',NULL,NULL,NULL,NULL,'tmh',NULL,NULL); +INSERT INTO "iana_records" VALUES(6410,0,NULL,'thw','thudam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6411,0,NULL,'thx','the','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6412,0,NULL,'thy','tha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6413,0,NULL,'thz','tayart tamajeq','1248825600',NULL,NULL,NULL,NULL,'tmh',NULL,NULL); +INSERT INTO "iana_records" VALUES(6414,0,NULL,'tia','tidikelt tamazight','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6415,0,NULL,'tic','tira','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6416,0,NULL,'tid','tidong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6417,0,NULL,'tie','tingal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6418,0,NULL,'tif','tifal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6419,0,NULL,'tig','tigre','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6420,0,NULL,'tih','timugon murut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6421,0,NULL,'tii','tiene','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6422,0,NULL,'tij','tilung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6423,0,NULL,'tik','tikar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6424,0,NULL,'til','tillamook','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6425,0,NULL,'tim','timbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6426,0,NULL,'tin','tindi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6427,0,NULL,'tio','teop','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6428,0,NULL,'tip','trimuris','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6429,0,NULL,'tiq','tiéfo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6430,0,NULL,'tis','masadiit itneg','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6431,0,NULL,'tit','tinigua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6432,0,NULL,'tiu','adasen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6433,0,NULL,'tiv','tiv','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6434,0,NULL,'tiw','tiwi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6435,0,NULL,'tix','southern tiwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6436,0,NULL,'tiy','tiruray','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6437,0,NULL,'tiz','tai hongjin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6438,0,NULL,'tja','tajuasohn','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6439,0,NULL,'tjg','tunjung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6440,0,NULL,'tji','northern tujia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6441,0,NULL,'tjm','timucua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6442,0,NULL,'tjn','tonjon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6443,0,NULL,'tjo','temacine tamazight','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6444,0,NULL,'tjs','southern tujia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6445,0,NULL,'tju','tjurruru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6446,0,NULL,'tka','truká','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6447,0,NULL,'tkb','buksa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6448,0,NULL,'tkd','tukudede','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6449,0,NULL,'tke','takwane','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6450,0,NULL,'tkf','tukumanféd','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6451,0,NULL,'tkk','takpa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6452,0,NULL,'tkl','tokelau','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6453,0,NULL,'tkm','takelma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6454,0,NULL,'tkn','toku-no-shima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6455,0,NULL,'tkp','tikopia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6456,0,NULL,'tkq','tee','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6457,0,NULL,'tkr','tsakhur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6458,0,NULL,'tks','takestani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6459,0,NULL,'tkt','kathoriya tharu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6460,0,NULL,'tku','upper necaxa totonac','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6461,0,NULL,'tkw','teanu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6462,0,NULL,'tkx','tangko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6463,0,NULL,'tkz','takua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6464,0,NULL,'tla','southwestern tepehuan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6465,0,NULL,'tlb','tobelo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6466,0,NULL,'tlc','yecuatla totonac','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6467,0,NULL,'tld','talaud','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6468,0,NULL,'tlf','telefol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6469,0,NULL,'tlg','tofanma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6470,0,NULL,'tlh','klingon','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6470,0,NULL,'tlh','tlhingan-hol','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6471,0,NULL,'tli','tlingit','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6472,0,NULL,'tlj','talinga-bwisi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6473,0,NULL,'tlk','taloki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6474,0,NULL,'tll','tetela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6475,0,NULL,'tlm','tolomako','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6476,0,NULL,'tln','talondo''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6477,0,NULL,'tlo','talodi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6478,0,NULL,'tlp','filomena mata-coahuitlán totonac','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6479,0,NULL,'tlq','tai loi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6480,0,NULL,'tlr','talise','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6481,0,NULL,'tls','tambotalo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6482,0,NULL,'tlt','teluti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6483,0,NULL,'tlu','tulehu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6484,0,NULL,'tlv','taliabu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6485,0,NULL,'tlw','south wemale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6486,0,NULL,'tlx','khehek','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6487,0,NULL,'tly','talysh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6488,0,NULL,'tma','tama (chad)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6489,0,NULL,'tmb','avava','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6489,0,NULL,'tmb','katbol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6490,0,NULL,'tmc','tumak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6491,0,NULL,'tmd','haruai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6492,0,NULL,'tme','tremembé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6493,0,NULL,'tmf','toba-maskoy','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6494,0,NULL,'tmg','ternateño','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6495,0,NULL,'tmh','tamashek','1129420800',NULL,NULL,NULL,'latn',NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(6496,0,NULL,'tmi','tutuba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6497,0,NULL,'tmj','samarokena','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6498,0,NULL,'tmk','northwestern tamang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6499,0,NULL,'tml','tamnim citak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6500,0,NULL,'tmm','tai thanh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6501,0,NULL,'tmn','taman (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6502,0,NULL,'tmo','temoq','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6503,0,NULL,'tmp','tai mène','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6504,0,NULL,'tmq','tumleo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6505,0,NULL,'tmr','jewish babylonian aramaic (ca. 200-1200 ce)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6506,0,NULL,'tms','tima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6507,0,NULL,'tmt','tasmate','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6508,0,NULL,'tmu','iau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6509,0,NULL,'tmv','tembo (motembo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6510,0,NULL,'tmw','temuan','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(6511,0,NULL,'tmy','tami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6512,0,NULL,'tmz','tamanaku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6513,0,NULL,'tna','tacana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6514,0,NULL,'tnb','western tunebo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6515,0,NULL,'tnc','tanimuca-retuarã','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6516,0,NULL,'tnd','angosturas tunebo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6517,0,NULL,'tne','tinoc kallahan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6518,0,NULL,'tnf','tangshewi','1248825600',1268265600,'prs',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6519,0,NULL,'tng','tobanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6520,0,NULL,'tnh','maiani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6521,0,NULL,'tni','tandia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6522,0,NULL,'tnk','kwamera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6523,0,NULL,'tnl','lenakel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6524,0,NULL,'tnm','tabla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6525,0,NULL,'tnn','north tanna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6526,0,NULL,'tno','toromono','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6527,0,NULL,'tnp','whitesands','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6528,0,NULL,'tnq','taino','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6529,0,NULL,'tnr','bedik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6530,0,NULL,'tns','tenis','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6531,0,NULL,'tnt','tontemboan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6532,0,NULL,'tnu','tay khang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6533,0,NULL,'tnv','tangchangya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6534,0,NULL,'tnw','tonsawang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6535,0,NULL,'tnx','tanema','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6536,0,NULL,'tny','tongwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6537,0,NULL,'tnz','tonga (thailand)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6538,0,NULL,'tob','toba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6539,0,NULL,'toc','coyutla totonac','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6540,0,NULL,'tod','toma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6541,0,NULL,'toe','tomedes','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6542,0,NULL,'tof','gizrra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6543,0,NULL,'tog','tonga (nyasa)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6544,0,NULL,'toh','gitonga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6545,0,NULL,'toi','tonga (zambia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6546,0,NULL,'toj','tojolabal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6547,0,NULL,'tol','tolowa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6548,0,NULL,'tom','tombulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6549,0,NULL,'too','xicotepec de juárez totonac','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6550,0,NULL,'top','papantla totonac','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6551,0,NULL,'toq','toposa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6552,0,NULL,'tor','togbo-vara banda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6553,0,NULL,'tos','highland totonac','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6554,0,NULL,'tou','tho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6555,0,NULL,'tov','upper taromi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6556,0,NULL,'tow','jemez','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6557,0,NULL,'tox','tobian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6558,0,NULL,'toy','topoiyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6559,0,NULL,'toz','to','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6560,0,NULL,'tpa','taupota','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6561,0,NULL,'tpc','azoyú me''phaa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6561,0,NULL,'tpc','azoyú tlapanec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6562,0,NULL,'tpe','tippera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6563,0,NULL,'tpf','tarpia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6564,0,NULL,'tpg','kula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6565,0,NULL,'tpi','tok pisin','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6566,0,NULL,'tpj','tapieté','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6567,0,NULL,'tpk','tupinikin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6568,0,NULL,'tpl','tlacoapa me''phaa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6568,0,NULL,'tpl','tlacoapa tlapanec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6569,0,NULL,'tpm','tampulma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6570,0,NULL,'tpn','tupinambá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6571,0,NULL,'tpo','tai pao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6572,0,NULL,'tpp','pisaflores tepehua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6573,0,NULL,'tpq','tukpa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6574,0,NULL,'tpr','tuparí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6575,0,NULL,'tpt','tlachichilco tepehua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6576,0,NULL,'tpu','tampuan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6577,0,NULL,'tpv','tanapag','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6578,0,NULL,'tpw','tupí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6579,0,NULL,'tpx','acatepec me''phaa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6579,0,NULL,'tpx','acatepec tlapanec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6580,0,NULL,'tpy','trumai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6581,0,NULL,'tpz','tinputz','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6582,0,NULL,'tqb','tembé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6583,0,NULL,'tql','lehali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6584,0,NULL,'tqm','turumsa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6585,0,NULL,'tqn','tenino','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6586,0,NULL,'tqo','toaripi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6587,0,NULL,'tqp','tomoip','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6588,0,NULL,'tqq','tunni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6589,0,NULL,'tqr','torona','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6590,0,NULL,'tqt','western totonac','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6591,0,NULL,'tqu','touo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6592,0,NULL,'tqw','tonkawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6593,0,NULL,'tra','tirahi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6594,0,NULL,'trb','terebu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6595,0,NULL,'trc','copala triqui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6596,0,NULL,'trd','turi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6597,0,NULL,'tre','east tarangan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6598,0,NULL,'trf','trinidadian creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6599,0,NULL,'trg','lishán didán','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6600,0,NULL,'trh','turaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6601,0,NULL,'tri','trió','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6602,0,NULL,'trj','toram','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6603,0,NULL,'trk','turkic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(6604,0,NULL,'trl','traveller scottish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6605,0,NULL,'trm','tregami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6606,0,NULL,'trn','trinitario','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6607,0,NULL,'tro','tarao naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6608,0,NULL,'trp','kok borok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6609,0,NULL,'trq','san martín itunyoso triqui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6610,0,NULL,'trr','taushiro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6611,0,NULL,'trs','chicahuaxtla triqui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6612,0,NULL,'trt','tunggare','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6613,0,NULL,'tru','turoyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6614,0,NULL,'trv','taroko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6615,0,NULL,'trw','torwali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6616,0,NULL,'trx','tringgus-sembaan bidayuh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6617,0,NULL,'try','turung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6618,0,NULL,'trz','torá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6619,0,NULL,'tsa','tsaangi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6620,0,NULL,'tsb','tsamai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6621,0,NULL,'tsc','tswa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6622,0,NULL,'tsd','tsakonian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6623,0,NULL,'tse','tunisian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6624,0,NULL,'tsf','southwestern tamang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6625,0,NULL,'tsg','tausug','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6626,0,NULL,'tsh','tsuvan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6627,0,NULL,'tsi','tsimshian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6628,0,NULL,'tsj','tshangla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6629,0,NULL,'tsk','tseku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6630,0,NULL,'tsl','ts''ün-lao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6631,0,NULL,'tsm','turkish sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6631,0,NULL,'tsm','türk İşaret dili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6632,0,NULL,'tsp','northern toussian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6633,0,NULL,'tsq','thai sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6634,0,NULL,'tsr','akei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6635,0,NULL,'tss','taiwan sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6636,0,NULL,'tsu','tsou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6637,0,NULL,'tsv','tsogo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6638,0,NULL,'tsw','tsishingini','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6639,0,NULL,'tsx','mubami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6640,0,NULL,'tsy','tebul sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6641,0,NULL,'tsz','purepecha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6642,0,NULL,'tta','tutelo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6643,0,NULL,'ttb','gaa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6644,0,NULL,'ttc','tektiteko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6645,0,NULL,'ttd','tauade','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6646,0,NULL,'tte','bwanabwana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6647,0,NULL,'ttf','tuotomb','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6648,0,NULL,'ttg','tutong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6649,0,NULL,'tth','upper ta''oih','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6650,0,NULL,'tti','tobati','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6651,0,NULL,'ttj','tooro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6652,0,NULL,'ttk','totoro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6653,0,NULL,'ttl','totela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6654,0,NULL,'ttm','northern tutchone','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6655,0,NULL,'ttn','towei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6656,0,NULL,'tto','lower ta''oih','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6657,0,NULL,'ttp','tombelala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6658,0,NULL,'ttq','tawallammat tamajaq','1248825600',NULL,NULL,NULL,NULL,'tmh',NULL,NULL); +INSERT INTO "iana_records" VALUES(6659,0,NULL,'ttr','tera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6660,0,NULL,'tts','northeastern thai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6661,0,NULL,'ttt','muslim tat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6662,0,NULL,'ttu','torau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6663,0,NULL,'ttv','titan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6664,0,NULL,'ttw','long wat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6665,0,NULL,'tty','sikaritai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6666,0,NULL,'ttz','tsum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6667,0,NULL,'tua','wiarumus','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6668,0,NULL,'tub','tübatulabal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6669,0,NULL,'tuc','mutu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6670,0,NULL,'tud','tuxá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6671,0,NULL,'tue','tuyuca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6672,0,NULL,'tuf','central tunebo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6673,0,NULL,'tug','tunia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6674,0,NULL,'tuh','taulil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6675,0,NULL,'tui','tupuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6676,0,NULL,'tuj','tugutil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6677,0,NULL,'tul','tula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6678,0,NULL,'tum','tumbuka','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6679,0,NULL,'tun','tunica','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6680,0,NULL,'tuo','tucano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6681,0,NULL,'tup','tupi languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(6682,0,NULL,'tuq','tedaga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6683,0,NULL,'tus','tuscarora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6684,0,NULL,'tut','altaic languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(6685,0,NULL,'tuu','tututni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6686,0,NULL,'tuv','turkana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6687,0,NULL,'tuw','tungus languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(6688,0,NULL,'tux','tuxináwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6689,0,NULL,'tuy','tugen','1248825600',NULL,NULL,NULL,NULL,'kln',NULL,NULL); +INSERT INTO "iana_records" VALUES(6690,0,NULL,'tuz','turka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6691,0,NULL,'tva','vaghua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6692,0,NULL,'tvd','tsuvadi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6693,0,NULL,'tve','te''un','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6694,0,NULL,'tvk','southeast ambrym','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6695,0,NULL,'tvl','tuvalu','1129420800',NULL,NULL,NULL,'latn',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6696,0,NULL,'tvm','tela-masbuar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6697,0,NULL,'tvn','tavoyan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6698,0,NULL,'tvo','tidore','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6699,0,NULL,'tvs','taveta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6700,0,NULL,'tvt','tutsa naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6701,0,NULL,'tvw','sedoa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6702,0,NULL,'tvy','timor pidgin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6703,0,NULL,'twa','twana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6704,0,NULL,'twb','western tawbuid','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6705,0,NULL,'twc','teshenawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6706,0,NULL,'twd','twents','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6707,0,NULL,'twe','tewa (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6708,0,NULL,'twf','northern tiwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6709,0,NULL,'twg','tereweng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6710,0,NULL,'twh','tai dón','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6711,0,NULL,'twl','tawara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6712,0,NULL,'twm','tawang monpa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6713,0,NULL,'twn','twendi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6714,0,NULL,'two','tswapong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6715,0,NULL,'twp','ere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6716,0,NULL,'twq','tasawaq','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6717,0,NULL,'twr','southwestern tarahumara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6718,0,NULL,'twt','turiwára','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6719,0,NULL,'twu','termanu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6720,0,NULL,'tww','tuwari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6721,0,NULL,'twx','tewe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6722,0,NULL,'twy','tawoyan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6723,0,NULL,'txa','tombonuo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6724,0,NULL,'txb','tokharian b','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6725,0,NULL,'txc','tsetsaut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6726,0,NULL,'txe','totoli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6727,0,NULL,'txg','tangut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6728,0,NULL,'txh','thracian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6729,0,NULL,'txi','ikpeng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6730,0,NULL,'txm','tomini','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6731,0,NULL,'txn','west tarangan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6732,0,NULL,'txo','toto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6733,0,NULL,'txq','tii','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6734,0,NULL,'txr','tartessian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6735,0,NULL,'txs','tonsea','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6736,0,NULL,'txt','citak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6737,0,NULL,'txu','kayapó','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6738,0,NULL,'txx','tatana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6739,0,NULL,'txy','tanosy malagasy','1248825600',NULL,NULL,NULL,NULL,'mg',NULL,NULL); +INSERT INTO "iana_records" VALUES(6740,0,NULL,'tya','tauya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6741,0,NULL,'tye','kyenga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6742,0,NULL,'tyh','o''du','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6743,0,NULL,'tyi','teke-tsaayi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6744,0,NULL,'tyj','tai do','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6745,0,NULL,'tyl','thu lao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6746,0,NULL,'tyn','kombai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6747,0,NULL,'typ','thaypan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6748,0,NULL,'tyr','tai daeng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6749,0,NULL,'tys','tày sa pa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6750,0,NULL,'tyt','tày tac','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6751,0,NULL,'tyu','kua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6752,0,NULL,'tyv','tuvinian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6753,0,NULL,'tyx','teke-tyee','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6754,0,NULL,'tyz','tày','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6755,0,NULL,'tza','tanzanian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6756,0,NULL,'tzh','tzeltal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6757,0,NULL,'tzj','tz''utujil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6758,0,NULL,'tzm','central atlas tamazight','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6759,0,NULL,'tzn','tugun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6760,0,NULL,'tzo','tzotzil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6761,0,NULL,'tzx','tabriak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6762,0,NULL,'uam','uamué','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6763,0,NULL,'uan','kuan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6764,0,NULL,'uar','tairuma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6765,0,NULL,'uba','ubang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6766,0,NULL,'ubi','ubi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6767,0,NULL,'ubl','buhi''non bikol','1268265600',NULL,NULL,NULL,NULL,'bik',NULL,NULL); +INSERT INTO "iana_records" VALUES(6768,0,NULL,'ubr','ubir','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6769,0,NULL,'ubu','umbu-ungu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6770,0,NULL,'uby','ubykh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6771,0,NULL,'uda','uda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6772,0,NULL,'ude','udihe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6773,0,NULL,'udg','muduga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6774,0,NULL,'udi','udi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6775,0,NULL,'udj','ujir','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6776,0,NULL,'udl','wuzlam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6777,0,NULL,'udm','udmurt','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6778,0,NULL,'udu','uduk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6779,0,NULL,'ues','kioko','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6780,0,NULL,'ufi','ufim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6781,0,NULL,'uga','ugaritic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6782,0,NULL,'ugb','kuku-ugbanh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6783,0,NULL,'uge','ughele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6784,0,NULL,'ugn','ugandan sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6785,0,NULL,'ugo','ugong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6786,0,NULL,'ugy','uruguayan sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6787,0,NULL,'uha','uhami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6788,0,NULL,'uhn','damal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6789,0,NULL,'uis','uisai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6790,0,NULL,'uiv','iyive','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6791,0,NULL,'uji','tanjijili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6792,0,NULL,'uka','kaburi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6793,0,NULL,'ukg','ukuriguma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6794,0,NULL,'ukh','ukhwejo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6795,0,NULL,'ukl','ukrainian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6796,0,NULL,'ukp','ukpe-bayobiri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6797,0,NULL,'ukq','ukwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6798,0,NULL,'uks','kaapor sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6798,0,NULL,'uks','urubú-kaapor sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6799,0,NULL,'uku','ukue','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6800,0,NULL,'ukw','ukwuani-aboh-ndoni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6801,0,NULL,'ula','fungwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6802,0,NULL,'ulb','ulukwumi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6803,0,NULL,'ulc','ulch','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6804,0,NULL,'ulf','afra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6804,0,NULL,'ulf','usku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6805,0,NULL,'uli','ulithian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6806,0,NULL,'ulk','meriam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6807,0,NULL,'ull','ullatan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6808,0,NULL,'ulm','ulumanda''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6809,0,NULL,'uln','unserdeutsch','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6810,0,NULL,'ulu','uma'' lung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6811,0,NULL,'ulw','ulwa','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6812,0,NULL,'uma','umatilla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6813,0,NULL,'umb','umbundu','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6814,0,NULL,'umc','marrucinian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6815,0,NULL,'umd','umbindhamu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6816,0,NULL,'umg','umbuygamu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6817,0,NULL,'umi','ukit','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6818,0,NULL,'umm','umon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6819,0,NULL,'umn','makyan naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6820,0,NULL,'umo','umotína','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6821,0,NULL,'ump','umpila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6822,0,NULL,'umr','umbugarla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6823,0,NULL,'ums','pendau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6824,0,NULL,'umu','munsee','1248825600',NULL,NULL,NULL,NULL,'del',NULL,NULL); +INSERT INTO "iana_records" VALUES(6825,0,NULL,'una','north watut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6826,0,NULL,'und','undetermined','1129420800',NULL,NULL,NULL,NULL,NULL,'special',NULL); +INSERT INTO "iana_records" VALUES(6827,0,NULL,'une','uneme','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6828,0,NULL,'ung','ngarinyin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6829,0,NULL,'unk','enawené-nawé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6830,0,NULL,'unm','unami','1248825600',NULL,NULL,NULL,NULL,'del',NULL,NULL); +INSERT INTO "iana_records" VALUES(6831,0,NULL,'unp','worora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6832,0,NULL,'unr','mundari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6833,0,NULL,'unx','munda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6834,0,NULL,'unz','unde kaili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6835,0,NULL,'uok','uokha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6836,0,NULL,'upi','umeda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6837,0,NULL,'upv','uripiv-wala-rano-atchin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6838,0,NULL,'ura','urarina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6839,0,NULL,'urb','kaapor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6839,0,NULL,'urb','urubú-kaapor','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6840,0,NULL,'urc','urningangg','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6841,0,NULL,'ure','uru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6842,0,NULL,'urf','uradhi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6843,0,NULL,'urg','urigina','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6844,0,NULL,'urh','urhobo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6845,0,NULL,'uri','urim','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6846,0,NULL,'urj','uralic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(6847,0,NULL,'urk','urak lawoi''','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(6848,0,NULL,'url','urali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6849,0,NULL,'urm','urapmin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6850,0,NULL,'urn','uruangnirin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6851,0,NULL,'uro','ura (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6852,0,NULL,'urp','uru-pa-in','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6853,0,NULL,'urr','lehalurup','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6853,0,NULL,'urr','löyöp','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6854,0,NULL,'urt','urat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6855,0,NULL,'uru','urumi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6856,0,NULL,'urv','uruava','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6857,0,NULL,'urw','sop','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6858,0,NULL,'urx','urimo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6859,0,NULL,'ury','orya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6860,0,NULL,'urz','uru-eu-wau-wau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6861,0,NULL,'usa','usarufa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6862,0,NULL,'ush','ushojo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6863,0,NULL,'usi','usui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6864,0,NULL,'usk','usaghade','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6865,0,NULL,'usp','uspanteco','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6866,0,NULL,'usu','uya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6867,0,NULL,'uta','otank','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6868,0,NULL,'ute','ute-southern paiute','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6869,0,NULL,'utp','amba (solomon islands)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6870,0,NULL,'utr','etulo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6871,0,NULL,'utu','utu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6872,0,NULL,'uum','urum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6873,0,NULL,'uun','kulon-pazeh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6874,0,NULL,'uur','ura (vanuatu)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6875,0,NULL,'uuu','u','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6876,0,NULL,'uve','west uvean','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6877,0,NULL,'uvh','uri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6878,0,NULL,'uvl','lote','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6879,0,NULL,'uwa','kuku-uwanh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6880,0,NULL,'uya','doko-uyanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6881,0,NULL,'uzn','northern uzbek','1248825600',NULL,NULL,NULL,NULL,'uz',NULL,NULL); +INSERT INTO "iana_records" VALUES(6882,0,NULL,'uzs','southern uzbek','1248825600',NULL,NULL,NULL,NULL,'uz',NULL,NULL); +INSERT INTO "iana_records" VALUES(6883,0,NULL,'vaa','vaagri booli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6884,0,NULL,'vae','vale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6885,0,NULL,'vaf','vafsi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6886,0,NULL,'vag','vagla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6887,0,NULL,'vah','varhadi-nagpuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6888,0,NULL,'vai','vai','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6889,0,NULL,'vaj','vasekela bushman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6890,0,NULL,'val','vehes','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6891,0,NULL,'vam','vanimo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6892,0,NULL,'van','valman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6893,0,NULL,'vao','vao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6894,0,NULL,'vap','vaiphei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6895,0,NULL,'var','huarijio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6896,0,NULL,'vas','vasavi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6897,0,NULL,'vau','vanuma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6898,0,NULL,'vav','varli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6899,0,NULL,'vay','wayu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6900,0,NULL,'vbb','southeast babar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6901,0,NULL,'vbk','southwestern bontok','1268265600',NULL,NULL,NULL,NULL,'bnc',NULL,NULL); +INSERT INTO "iana_records" VALUES(6902,0,NULL,'vec','venetian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6903,0,NULL,'ved','veddah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6904,0,NULL,'vel','veluws','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6905,0,NULL,'vem','vemgo-mabas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6906,0,NULL,'veo','ventureño','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6907,0,NULL,'vep','veps','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6908,0,NULL,'ver','mom jango','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6909,0,NULL,'vgr','vaghri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6910,0,NULL,'vgt','flemish sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6910,0,NULL,'vgt','vlaamse gebarentaal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6911,0,NULL,'vic','virgin islands creole english','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6912,0,NULL,'vid','vidunda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6913,0,NULL,'vif','vili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6914,0,NULL,'vig','viemo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6915,0,NULL,'vil','vilela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6916,0,NULL,'vin','vinza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6917,0,NULL,'vis','vishavan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6918,0,NULL,'vit','viti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6919,0,NULL,'viv','iduna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6920,0,NULL,'vka','kariyarra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6921,0,NULL,'vki','ija-zuba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6922,0,NULL,'vkj','kujarge','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6923,0,NULL,'vkk','kaur','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(6924,0,NULL,'vkl','kulisusu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6925,0,NULL,'vkm','kamakan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6926,0,NULL,'vko','kodeoha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6927,0,NULL,'vkp','korlai creole portuguese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6928,0,NULL,'vkt','tenggarong kutai malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(6929,0,NULL,'vku','kurrama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6930,0,NULL,'vlp','valpei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6931,0,NULL,'vls','vlaams','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6932,0,NULL,'vma','martuyhunira','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6933,0,NULL,'vmb','mbabaram','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6934,0,NULL,'vmc','juxtlahuaca mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6935,0,NULL,'vmd','mudu koraga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6936,0,NULL,'vme','east masela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6937,0,NULL,'vmf','mainfränkisch','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6938,0,NULL,'vmg','minigir','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6939,0,NULL,'vmh','maraghei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6940,0,NULL,'vmi','miwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6941,0,NULL,'vmj','ixtayutla mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6942,0,NULL,'vmk','makhuwa-shirima','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6943,0,NULL,'vml','malgana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6944,0,NULL,'vmm','mitlatongo mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6945,0,NULL,'vmp','soyaltepec mazatec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6946,0,NULL,'vmq','soyaltepec mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6947,0,NULL,'vmr','marenje','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6948,0,NULL,'vms','moksela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6949,0,NULL,'vmu','muluridyi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6950,0,NULL,'vmv','valley maidu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6951,0,NULL,'vmw','makhuwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6952,0,NULL,'vmx','tamazola mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6953,0,NULL,'vmy','ayautla mazatec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6954,0,NULL,'vmz','mazatlán mazatec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6955,0,NULL,'vnk','lovono','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6955,0,NULL,'vnk','vano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6956,0,NULL,'vnm','neve''ei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6956,0,NULL,'vnm','vinmavis','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6957,0,NULL,'vnp','vunapu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6958,0,NULL,'vor','voro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6959,0,NULL,'vot','votic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6960,0,NULL,'vra','vera''a','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6961,0,NULL,'vro','võro','1248825600',NULL,NULL,NULL,NULL,'et',NULL,NULL); +INSERT INTO "iana_records" VALUES(6962,0,NULL,'vrs','varisi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6963,0,NULL,'vrt','banam bay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6963,0,NULL,'vrt','burmbar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6964,0,NULL,'vsi','moldova sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6965,0,NULL,'vsl','venezuelan sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6966,0,NULL,'vsv','llengua de signes valenciana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6966,0,NULL,'vsv','valencian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6967,0,NULL,'vto','vitou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6968,0,NULL,'vum','vumbu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6969,0,NULL,'vun','vunjo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6970,0,NULL,'vut','vute','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6971,0,NULL,'vwa','awa (china)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6972,0,NULL,'waa','walla walla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6973,0,NULL,'wab','wab','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6974,0,NULL,'wac','wasco-wishram','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6975,0,NULL,'wad','wandamen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6976,0,NULL,'wae','walser','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6977,0,NULL,'waf','wakoná','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6978,0,NULL,'wag','wa''ema','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6979,0,NULL,'wah','watubela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6980,0,NULL,'wai','wares','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6981,0,NULL,'waj','waffa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6982,0,NULL,'wak','wakashan languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(6983,0,NULL,'wal','wolaitta','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6983,0,NULL,'wal','wolaytta','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6984,0,NULL,'wam','wampanoag','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6985,0,NULL,'wan','wan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6986,0,NULL,'wao','wappo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6987,0,NULL,'wap','wapishana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6988,0,NULL,'waq','wageman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6989,0,NULL,'war','waray (philippines)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6990,0,NULL,'was','washo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6991,0,NULL,'wat','kaninuwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6992,0,NULL,'wau','waurá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6993,0,NULL,'wav','waka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6994,0,NULL,'waw','waiwai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6995,0,NULL,'wax','watam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6996,0,NULL,'way','wayana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6997,0,NULL,'waz','wampur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6998,0,NULL,'wba','warao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(6999,0,NULL,'wbb','wabo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7000,0,NULL,'wbe','waritai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7001,0,NULL,'wbf','wara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7002,0,NULL,'wbh','wanda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7003,0,NULL,'wbi','vwanji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7004,0,NULL,'wbj','alagwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7005,0,NULL,'wbk','waigali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7006,0,NULL,'wbl','wakhi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7007,0,NULL,'wbm','wa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7008,0,NULL,'wbp','warlpiri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7009,0,NULL,'wbq','waddar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7010,0,NULL,'wbr','wagdi','1248825600',NULL,NULL,NULL,NULL,'raj',NULL,NULL); +INSERT INTO "iana_records" VALUES(7011,0,NULL,'wbt','wanman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7012,0,NULL,'wbv','wajarri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7013,0,NULL,'wbw','woi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7014,0,NULL,'wca','yanomámi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7015,0,NULL,'wci','waci gbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7016,0,NULL,'wdd','wandji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7017,0,NULL,'wdg','wadaginam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7018,0,NULL,'wdj','wadjiginy','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7019,0,NULL,'wdu','wadjigu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7020,0,NULL,'wea','wewaw','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7021,0,NULL,'wec','wè western','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7022,0,NULL,'wed','wedau','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7023,0,NULL,'weh','weh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7024,0,NULL,'wei','were','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7025,0,NULL,'wem','weme gbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7026,0,NULL,'wen','sorbian languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(7027,0,NULL,'weo','north wemale','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7028,0,NULL,'wep','westphalien','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7029,0,NULL,'wer','weri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7030,0,NULL,'wes','cameroon pidgin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7031,0,NULL,'wet','perai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7032,0,NULL,'weu','welaung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7033,0,NULL,'wew','wejewa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7034,0,NULL,'wfg','yafi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7034,0,NULL,'wfg','zorop','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7035,0,NULL,'wga','wagaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7036,0,NULL,'wgb','wagawaga','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7037,0,NULL,'wgg','wangganguru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7038,0,NULL,'wgi','wahgi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7039,0,NULL,'wgo','waigeo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7040,0,NULL,'wgw','wagawaga','1248825600',1268265600,NULL,NULL,NULL,NULL,NULL,'see wgb, ylb'); +INSERT INTO "iana_records" VALUES(7041,0,NULL,'wgy','warrgamay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7042,0,NULL,'wha','manusela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7043,0,NULL,'whg','north wahgi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7044,0,NULL,'whk','wahau kenyah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7045,0,NULL,'whu','wahau kayan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7046,0,NULL,'wib','southern toussian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7047,0,NULL,'wic','wichita','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7048,0,NULL,'wie','wik-epa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7049,0,NULL,'wif','wik-keyangan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7050,0,NULL,'wig','wik-ngathana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7051,0,NULL,'wih','wik-me''anha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7052,0,NULL,'wii','minidien','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7053,0,NULL,'wij','wik-iiyanh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7054,0,NULL,'wik','wikalkan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7055,0,NULL,'wil','wilawila','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7056,0,NULL,'wim','wik-mungkan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7057,0,NULL,'win','ho-chunk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7058,0,NULL,'wir','wiraféd','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7059,0,NULL,'wit','wintu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7060,0,NULL,'wiu','wiru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7061,0,NULL,'wiv','muduapa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7062,0,NULL,'wiw','wirangu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7063,0,NULL,'wiy','wiyot','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7064,0,NULL,'wja','waja','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7065,0,NULL,'wji','warji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7066,0,NULL,'wka','kw''adza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7067,0,NULL,'wkb','kumbaran','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7068,0,NULL,'wkd','mo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7068,0,NULL,'wkd','wakde','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7069,0,NULL,'wkl','kalanadi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7070,0,NULL,'wku','kunduvadi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7071,0,NULL,'wkw','wakawaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7072,0,NULL,'wla','walio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7073,0,NULL,'wlc','mwali comorian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7074,0,NULL,'wle','wolane','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7075,0,NULL,'wlg','kunbarlang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7076,0,NULL,'wli','waioli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7077,0,NULL,'wlk','wailaki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7078,0,NULL,'wll','wali (sudan)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7079,0,NULL,'wlm','middle welsh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7080,0,NULL,'wlo','wolio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7081,0,NULL,'wlr','wailapa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7082,0,NULL,'wls','wallisian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7083,0,NULL,'wlu','wuliwuli','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7084,0,NULL,'wlv','wichí lhamtés vejoz','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7085,0,NULL,'wlw','walak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7086,0,NULL,'wlx','wali (ghana)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7087,0,NULL,'wly','waling','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7088,0,NULL,'wma','mawa (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7089,0,NULL,'wmb','wambaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7090,0,NULL,'wmc','wamas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7091,0,NULL,'wmd','mamaindé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7092,0,NULL,'wme','wambule','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7093,0,NULL,'wmh','waima''a','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7094,0,NULL,'wmi','wamin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7095,0,NULL,'wmm','maiwa (indonesia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7096,0,NULL,'wmn','waamwang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7097,0,NULL,'wmo','wom (papua new guinea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7098,0,NULL,'wms','wambon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7099,0,NULL,'wmt','walmajarri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7100,0,NULL,'wmw','mwani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7101,0,NULL,'wmx','womo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7102,0,NULL,'wnb','wanambre','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7103,0,NULL,'wnc','wantoat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7104,0,NULL,'wnd','wandarang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7105,0,NULL,'wne','waneci','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7106,0,NULL,'wng','wanggom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7107,0,NULL,'wni','ndzwani comorian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7108,0,NULL,'wnk','wanukaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7109,0,NULL,'wnm','wanggamala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7110,0,NULL,'wno','wano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7111,0,NULL,'wnp','wanap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7112,0,NULL,'wnu','usan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7113,0,NULL,'woa','tyaraity','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7114,0,NULL,'wob','wè northern','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7115,0,NULL,'woc','wogeo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7116,0,NULL,'wod','wolani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7117,0,NULL,'woe','woleaian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7118,0,NULL,'wof','gambian wolof','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7119,0,NULL,'wog','wogamusin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7120,0,NULL,'woi','kamang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7121,0,NULL,'wok','longto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7122,0,NULL,'wom','wom (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7123,0,NULL,'won','wongo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7124,0,NULL,'woo','manombai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7125,0,NULL,'wor','woria','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7126,0,NULL,'wos','hanga hundi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7127,0,NULL,'wow','wawonii','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7128,0,NULL,'woy','weyto','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7129,0,NULL,'wpc','maco','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7130,0,NULL,'wra','warapu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7131,0,NULL,'wrb','warluwara','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7132,0,NULL,'wrd','warduji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7133,0,NULL,'wrg','warungu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7134,0,NULL,'wrh','wiradhuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7135,0,NULL,'wri','wariyangga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7136,0,NULL,'wrl','warlmanpa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7137,0,NULL,'wrm','warumungu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7138,0,NULL,'wrn','warnang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7139,0,NULL,'wrp','waropen','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7140,0,NULL,'wrr','wardaman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7141,0,NULL,'wrs','waris','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7142,0,NULL,'wru','waru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7143,0,NULL,'wrv','waruna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7144,0,NULL,'wrw','gugu warra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7145,0,NULL,'wrx','wae rana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7146,0,NULL,'wry','merwari','1248825600',NULL,NULL,NULL,NULL,'mwr',NULL,NULL); +INSERT INTO "iana_records" VALUES(7147,0,NULL,'wrz','waray (australia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7148,0,NULL,'wsa','warembori','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7149,0,NULL,'wsi','wusi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7150,0,NULL,'wsk','waskia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7151,0,NULL,'wsr','owenia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7152,0,NULL,'wss','wasa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7153,0,NULL,'wsu','wasu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7154,0,NULL,'wsv','wotapuri-katarqalai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7155,0,NULL,'wtf','dumpu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7156,0,NULL,'wti','berta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7157,0,NULL,'wtk','watakataui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7158,0,NULL,'wtm','mewati','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7159,0,NULL,'wtw','wotu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7160,0,NULL,'wua','wikngenchera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7161,0,NULL,'wub','wunambal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7162,0,NULL,'wud','wudu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7163,0,NULL,'wuh','wutunhua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7164,0,NULL,'wul','silimo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7165,0,NULL,'wum','wumbvu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7166,0,NULL,'wun','bungu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7167,0,NULL,'wur','wurrugu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7168,0,NULL,'wut','wutung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7169,0,NULL,'wuu','wu chinese','1248825600',NULL,NULL,NULL,NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(7170,0,NULL,'wuv','wuvulu-aua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7171,0,NULL,'wux','wulna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7172,0,NULL,'wuy','wauyai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7173,0,NULL,'wwa','waama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7174,0,NULL,'wwo','dorig','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7174,0,NULL,'wwo','wetamut','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7175,0,NULL,'wwr','warrwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7176,0,NULL,'www','wawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7177,0,NULL,'wxa','waxianghua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7178,0,NULL,'wya','wyandot','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7179,0,NULL,'wyb','wangaaybuwan-ngiyambaa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7180,0,NULL,'wym','wymysorys','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7181,0,NULL,'wyr','wayoró','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7182,0,NULL,'wyy','western fijian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7183,0,NULL,'xaa','andalusian arabic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7184,0,NULL,'xab','sambe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7185,0,NULL,'xac','kachari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7186,0,NULL,'xad','adai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7187,0,NULL,'xae','aequian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7188,0,NULL,'xag','aghwan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7189,0,NULL,'xai','kaimbé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7190,0,NULL,'xal','kalmyk','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7190,0,NULL,'xal','oirat','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7191,0,NULL,'xam','/xam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7192,0,NULL,'xan','xamtanga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7193,0,NULL,'xao','khao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7194,0,NULL,'xap','apalachee','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7195,0,NULL,'xaq','aquitanian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7196,0,NULL,'xar','karami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7197,0,NULL,'xas','kamas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7198,0,NULL,'xat','katawixi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7199,0,NULL,'xau','kauwera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7200,0,NULL,'xav','xavánte','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7201,0,NULL,'xaw','kawaiisu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7202,0,NULL,'xay','kayan mahakam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7203,0,NULL,'xba','kamba (brazil)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7204,0,NULL,'xbb','lower burdekin','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7205,0,NULL,'xbc','bactrian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7206,0,NULL,'xbi','kombio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7207,0,NULL,'xbm','middle breton','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7208,0,NULL,'xbn','kenaboi','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7209,0,NULL,'xbo','bolgarian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7210,0,NULL,'xbr','kambera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7211,0,NULL,'xbw','kambiwá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7212,0,NULL,'xbx','kabixí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7213,0,NULL,'xcb','cumbric','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7214,0,NULL,'xcc','camunic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7215,0,NULL,'xce','celtiberian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7216,0,NULL,'xcg','cisalpine gaulish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7217,0,NULL,'xch','chemakum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7217,0,NULL,'xch','chimakum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7218,0,NULL,'xcl','classical armenian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7219,0,NULL,'xcm','comecrudo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7220,0,NULL,'xcn','cotoname','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7221,0,NULL,'xco','chorasmian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7222,0,NULL,'xcr','carian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7223,0,NULL,'xct','classical tibetan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7224,0,NULL,'xcu','curonian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7225,0,NULL,'xcv','chuvantsy','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7226,0,NULL,'xcw','coahuilteco','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7227,0,NULL,'xcy','cayuse','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7228,0,NULL,'xdc','dacian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7229,0,NULL,'xdm','edomite','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7230,0,NULL,'xdy','malayic dayak','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7231,0,NULL,'xeb','eblan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7232,0,NULL,'xed','hdi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7233,0,NULL,'xeg','//xegwi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7234,0,NULL,'xel','kelo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7235,0,NULL,'xem','kembayan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7236,0,NULL,'xep','epi-olmec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7237,0,NULL,'xer','xerénte','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7238,0,NULL,'xes','kesawai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7239,0,NULL,'xet','xetá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7240,0,NULL,'xeu','keoru-ahia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7241,0,NULL,'xfa','faliscan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7242,0,NULL,'xga','galatian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7243,0,NULL,'xgf','gabrielino-fernandeño','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7244,0,NULL,'xgl','galindan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7245,0,NULL,'xgn','mongolian languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(7246,0,NULL,'xgr','garza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7247,0,NULL,'xha','harami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7248,0,NULL,'xhc','hunnic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7249,0,NULL,'xhd','hadrami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7250,0,NULL,'xhe','khetrani','1248825600',NULL,NULL,NULL,NULL,'lah',NULL,NULL); +INSERT INTO "iana_records" VALUES(7251,0,NULL,'xhr','hernican','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7252,0,NULL,'xht','hattic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7253,0,NULL,'xhu','hurrian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7254,0,NULL,'xhv','khua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7255,0,NULL,'xia','xiandao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7256,0,NULL,'xib','iberian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7257,0,NULL,'xii','xiri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7258,0,NULL,'xil','illyrian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7259,0,NULL,'xin','xinca','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7260,0,NULL,'xip','xipináwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7261,0,NULL,'xir','xiriâna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7262,0,NULL,'xiv','indus valley language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7263,0,NULL,'xiy','xipaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7264,0,NULL,'xka','kalkoti','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7265,0,NULL,'xkb','northern nago','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7266,0,NULL,'xkc','kho''ini','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7267,0,NULL,'xkd','mendalam kayan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7268,0,NULL,'xke','kereho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7269,0,NULL,'xkf','khengkha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7270,0,NULL,'xkg','kagoro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7271,0,NULL,'xkh','karahawyana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7272,0,NULL,'xki','kenyan sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7273,0,NULL,'xkj','kajali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7274,0,NULL,'xkk','kaco''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7275,0,NULL,'xkl','mainstream kenyah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7276,0,NULL,'xkn','kayan river kayan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7277,0,NULL,'xko','kiorr','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7278,0,NULL,'xkp','kabatei','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7279,0,NULL,'xkq','koroni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7280,0,NULL,'xkr','xakriabá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7281,0,NULL,'xks','kumbewaha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7282,0,NULL,'xkt','kantosi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7283,0,NULL,'xku','kaamba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7284,0,NULL,'xkv','kgalagadi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7285,0,NULL,'xkw','kembra','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7286,0,NULL,'xkx','karore','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7287,0,NULL,'xky','uma'' lasan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7288,0,NULL,'xkz','kurtokha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7289,0,NULL,'xla','kamula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7290,0,NULL,'xlb','loup b','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7291,0,NULL,'xlc','lycian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7292,0,NULL,'xld','lydian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7293,0,NULL,'xle','lemnian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7294,0,NULL,'xlg','ligurian (ancient)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7295,0,NULL,'xli','liburnian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7296,0,NULL,'xln','alanic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7297,0,NULL,'xlo','loup a','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7298,0,NULL,'xlp','lepontic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7299,0,NULL,'xls','lusitanian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7300,0,NULL,'xlu','cuneiform luwian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7301,0,NULL,'xly','elymian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7302,0,NULL,'xma','mushungulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7303,0,NULL,'xmb','mbonga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7304,0,NULL,'xmc','makhuwa-marrevone','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7305,0,NULL,'xmd','mbedam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7306,0,NULL,'xme','median','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7307,0,NULL,'xmf','mingrelian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7308,0,NULL,'xmg','mengaka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7309,0,NULL,'xmh','kuku-muminh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7310,0,NULL,'xmj','majera','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7311,0,NULL,'xmk','ancient macedonian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7312,0,NULL,'xml','malaysian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7313,0,NULL,'xmm','manado malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7314,0,NULL,'xmn','manichaean middle persian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7315,0,NULL,'xmo','morerebi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7316,0,NULL,'xmp','kuku-mu''inh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7317,0,NULL,'xmq','kuku-mangk','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7318,0,NULL,'xmr','meroitic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7319,0,NULL,'xms','moroccan sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7320,0,NULL,'xmt','matbat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7321,0,NULL,'xmu','kamu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7322,0,NULL,'xmv','antankarana malagasy','1248825600',NULL,NULL,NULL,NULL,'mg',NULL,NULL); +INSERT INTO "iana_records" VALUES(7323,0,NULL,'xmw','tsimihety malagasy','1248825600',NULL,NULL,NULL,NULL,'mg',NULL,NULL); +INSERT INTO "iana_records" VALUES(7324,0,NULL,'xmx','maden','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7325,0,NULL,'xmy','mayaguduna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7326,0,NULL,'xmz','mori bawah','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7327,0,NULL,'xna','ancient north arabian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7328,0,NULL,'xnb','kanakanabu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7329,0,NULL,'xnd','na-dene languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(7330,0,NULL,'xng','middle mongolian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7331,0,NULL,'xnh','kuanhua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7332,0,NULL,'xnn','northern kankanay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7333,0,NULL,'xno','anglo-norman','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7334,0,NULL,'xnr','kangri','1248825600',NULL,NULL,NULL,NULL,'doi',NULL,NULL); +INSERT INTO "iana_records" VALUES(7335,0,NULL,'xns','kanashi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7336,0,NULL,'xnt','narragansett','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7337,0,NULL,'xoc','o''chi''chi''','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7338,0,NULL,'xod','kokoda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7339,0,NULL,'xog','soga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7340,0,NULL,'xoi','kominimung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7341,0,NULL,'xok','xokleng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7342,0,NULL,'xom','komo (sudan)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7343,0,NULL,'xon','konkomba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7344,0,NULL,'xoo','xukurú','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7345,0,NULL,'xop','kopar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7346,0,NULL,'xor','korubo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7347,0,NULL,'xow','kowaki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7348,0,NULL,'xpc','pecheneg','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7349,0,NULL,'xpe','liberia kpelle','1248825600',NULL,NULL,NULL,NULL,'kpe',NULL,NULL); +INSERT INTO "iana_records" VALUES(7350,0,NULL,'xpg','phrygian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7351,0,NULL,'xpi','pictish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7352,0,NULL,'xpk','kulina pano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7353,0,NULL,'xpm','pumpokol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7354,0,NULL,'xpn','kapinawá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7355,0,NULL,'xpo','pochutec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7356,0,NULL,'xpp','puyo-paekche','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7357,0,NULL,'xpq','mohegan-pequot','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7358,0,NULL,'xpr','parthian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7359,0,NULL,'xps','pisidian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7360,0,NULL,'xpu','punic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7361,0,NULL,'xpy','puyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7362,0,NULL,'xqa','karakhanid','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7363,0,NULL,'xqt','qatabanian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7364,0,NULL,'xra','krahô','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7365,0,NULL,'xrb','eastern karaboro','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7366,0,NULL,'xre','kreye','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7367,0,NULL,'xri','krikati-timbira','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7368,0,NULL,'xrm','armazic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7369,0,NULL,'xrn','arin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7370,0,NULL,'xrr','raetic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7371,0,NULL,'xrt','aranama-tamique','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7372,0,NULL,'xru','marriammu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7373,0,NULL,'xrw','karawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7374,0,NULL,'xsa','sabaean','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7375,0,NULL,'xsb','tinà sambal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7376,0,NULL,'xsc','scythian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7377,0,NULL,'xsd','sidetic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7378,0,NULL,'xse','sempan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7379,0,NULL,'xsh','shamang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7380,0,NULL,'xsi','sio','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7381,0,NULL,'xsj','subi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7382,0,NULL,'xsl','south slavey','1248825600',NULL,NULL,NULL,NULL,'den',NULL,NULL); +INSERT INTO "iana_records" VALUES(7383,0,NULL,'xsm','kasem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7384,0,NULL,'xsn','sanga (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7385,0,NULL,'xso','solano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7386,0,NULL,'xsp','silopi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7387,0,NULL,'xsq','makhuwa-saka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7388,0,NULL,'xsr','sherpa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7389,0,NULL,'xss','assan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7390,0,NULL,'xsu','sanumá','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7391,0,NULL,'xsv','sudovian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7392,0,NULL,'xsy','saisiyat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7393,0,NULL,'xta','alcozauca mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7394,0,NULL,'xtb','chazumba mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7395,0,NULL,'xtc','katcha-kadugli-miri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7396,0,NULL,'xtd','diuxi-tilantongo mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7397,0,NULL,'xte','ketengban','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7398,0,NULL,'xtg','transalpine gaulish','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7399,0,NULL,'xti','sinicahua mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7400,0,NULL,'xtj','san juan teita mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7401,0,NULL,'xtl','tijaltepec mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7402,0,NULL,'xtm','magdalena peñasco mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7403,0,NULL,'xtn','northern tlaxiaco mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7404,0,NULL,'xto','tokharian a','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7405,0,NULL,'xtp','san miguel piedras mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7406,0,NULL,'xtq','tumshuqese','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7407,0,NULL,'xtr','early tripuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7408,0,NULL,'xts','sindihui mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7409,0,NULL,'xtt','tacahua mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7410,0,NULL,'xtu','cuyamecalco mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7411,0,NULL,'xtw','tawandê','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7412,0,NULL,'xty','yoloxochitl mixtec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7413,0,NULL,'xtz','tasmanian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7414,0,NULL,'xua','alu kurumba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7415,0,NULL,'xub','betta kurumba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7416,0,NULL,'xug','kunigami','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7417,0,NULL,'xuj','jennu kurumba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7418,0,NULL,'xum','umbrian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7419,0,NULL,'xuo','kuo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7420,0,NULL,'xup','upper umpqua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7421,0,NULL,'xur','urartian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7422,0,NULL,'xut','kuthant','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7423,0,NULL,'xuu','kxoe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7424,0,NULL,'xve','venetic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7425,0,NULL,'xvi','kamviri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7426,0,NULL,'xvn','vandalic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7427,0,NULL,'xvo','volscian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7428,0,NULL,'xvs','vestinian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7429,0,NULL,'xwa','kwaza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7430,0,NULL,'xwc','woccon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7431,0,NULL,'xwe','xwela gbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7432,0,NULL,'xwg','kwegu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7433,0,NULL,'xwl','western xwla gbe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7434,0,NULL,'xwo','written oirat','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7435,0,NULL,'xwr','kwerba mamberamo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7436,0,NULL,'xxb','boro (ghana)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7437,0,NULL,'xxk','ke''o','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7438,0,NULL,'xxr','koropó','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7439,0,NULL,'xxt','tambora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7440,0,NULL,'xyl','yalakalore','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7441,0,NULL,'xzh','zhang-zhung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7442,0,NULL,'xzm','zemgalian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7443,0,NULL,'xzp','ancient zapotec','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7444,0,NULL,'yaa','yaminahua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7445,0,NULL,'yab','yuhup','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7446,0,NULL,'yac','pass valley yali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7447,0,NULL,'yad','yagua','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7448,0,NULL,'yae','pumé','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7449,0,NULL,'yaf','yaka (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7450,0,NULL,'yag','yámana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7451,0,NULL,'yah','yazgulyam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7452,0,NULL,'yai','yagnobi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7453,0,NULL,'yaj','banda-yangere','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7454,0,NULL,'yak','yakama','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7455,0,NULL,'yal','yalunka','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7456,0,NULL,'yam','yamba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7457,0,NULL,'yan','mayangna','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7458,0,NULL,'yao','yao','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7459,0,NULL,'yap','yapese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7460,0,NULL,'yaq','yaqui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7461,0,NULL,'yar','yabarana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7462,0,NULL,'yas','nugunu (cameroon)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7463,0,NULL,'yat','yambeta','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7464,0,NULL,'yau','yuwana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7465,0,NULL,'yav','yangben','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7466,0,NULL,'yaw','yawalapití','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7467,0,NULL,'yax','yauma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7468,0,NULL,'yay','agwagwune','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7469,0,NULL,'yaz','lokaa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7470,0,NULL,'yba','yala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7471,0,NULL,'ybb','yemba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7472,0,NULL,'ybd','yangbye','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7473,0,NULL,'ybe','west yugur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7474,0,NULL,'ybh','yakha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7475,0,NULL,'ybi','yamphu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7476,0,NULL,'ybj','hasha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7477,0,NULL,'ybk','bokha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7478,0,NULL,'ybl','yukuben','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7479,0,NULL,'ybm','yaben','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7480,0,NULL,'ybn','yabaâna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7481,0,NULL,'ybo','yabong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7482,0,NULL,'ybx','yawiyo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7483,0,NULL,'yby','yaweyuha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7484,0,NULL,'ych','chesu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7485,0,NULL,'ycl','lolopo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7486,0,NULL,'ycn','yucuna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7487,0,NULL,'ycp','chepya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7488,0,NULL,'ydd','eastern yiddish','1248825600',NULL,NULL,NULL,NULL,'yi',NULL,NULL); +INSERT INTO "iana_records" VALUES(7489,0,NULL,'yde','yangum dey','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7490,0,NULL,'ydg','yidgha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7491,0,NULL,'ydk','yoidik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7492,0,NULL,'yds','yiddish sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7493,0,NULL,'yea','ravula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7494,0,NULL,'yec','yeniche','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7495,0,NULL,'yee','yimas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7496,0,NULL,'yei','yeni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7497,0,NULL,'yej','yevanic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7498,0,NULL,'yel','yela','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7499,0,NULL,'yen','yendang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7500,0,NULL,'yer','tarok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7501,0,NULL,'yes','yeskwa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7502,0,NULL,'yet','yetfa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7503,0,NULL,'yeu','yerukula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7504,0,NULL,'yev','yapunda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7505,0,NULL,'yey','yeyi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7506,0,NULL,'ygl','yangum gel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7507,0,NULL,'ygm','yagomi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7508,0,NULL,'ygp','gepo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7509,0,NULL,'ygr','yagaria','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7510,0,NULL,'ygw','yagwoia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7511,0,NULL,'yha','baha buyang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7512,0,NULL,'yhd','judeo-iraqi arabic','1248825600',NULL,NULL,NULL,NULL,'jrb',NULL,NULL); +INSERT INTO "iana_records" VALUES(7513,0,NULL,'yhl','hlepho phowa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7514,0,NULL,'yia','yinggarda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7515,0,NULL,'yif','ache','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7516,0,NULL,'yig','wusa nasu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7517,0,NULL,'yih','western yiddish','1248825600',NULL,NULL,NULL,NULL,'yi',NULL,NULL); +INSERT INTO "iana_records" VALUES(7518,0,NULL,'yii','yidiny','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7519,0,NULL,'yij','yindjibarndi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7520,0,NULL,'yik','dongshanba lalo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7521,0,NULL,'yil','yindjilandji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7522,0,NULL,'yim','yimchungru naga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7523,0,NULL,'yin','yinchia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7524,0,NULL,'yip','pholo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7525,0,NULL,'yiq','miqie','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7526,0,NULL,'yir','north awyu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7527,0,NULL,'yis','yis','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7528,0,NULL,'yit','eastern lalu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7529,0,NULL,'yiu','awu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7530,0,NULL,'yiv','northern nisu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7531,0,NULL,'yix','axi yi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7532,0,NULL,'yiy','yir yoront','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7533,0,NULL,'yiz','azhe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7534,0,NULL,'yka','yakan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7535,0,NULL,'ykg','northern yukaghir','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7536,0,NULL,'yki','yoke','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7537,0,NULL,'ykk','yakaikeke','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7538,0,NULL,'ykl','khlula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7539,0,NULL,'ykm','kap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7540,0,NULL,'yko','yasa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7541,0,NULL,'ykr','yekora','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7542,0,NULL,'ykt','kathu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7543,0,NULL,'yky','yakoma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7544,0,NULL,'yla','yaul','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7545,0,NULL,'ylb','yaleba','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7546,0,NULL,'yle','yele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7547,0,NULL,'ylg','yelogu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7548,0,NULL,'yli','angguruk yali','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7549,0,NULL,'yll','yil','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7550,0,NULL,'ylm','limi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7551,0,NULL,'yln','langnian buyang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7552,0,NULL,'ylo','naluo yi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7553,0,NULL,'ylr','yalarnnga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7554,0,NULL,'ylu','aribwaung','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7555,0,NULL,'yly','nyâlayu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7556,0,NULL,'yma','yamphe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7557,0,NULL,'ymb','yambes','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7558,0,NULL,'ymc','southern muji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7559,0,NULL,'ymd','muda','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7560,0,NULL,'yme','yameo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7561,0,NULL,'ymg','yamongeri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7562,0,NULL,'ymh','mili','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7563,0,NULL,'ymi','moji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7564,0,NULL,'ymk','makwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7565,0,NULL,'yml','iamalele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7566,0,NULL,'ymm','maay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7567,0,NULL,'ymn','sunum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7567,0,NULL,'ymn','yamna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7568,0,NULL,'ymo','yangum mon','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7569,0,NULL,'ymp','yamap','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7570,0,NULL,'ymq','qila muji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7571,0,NULL,'ymr','malasar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7572,0,NULL,'yms','mysian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7573,0,NULL,'ymt','mator-taygi-karagas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7574,0,NULL,'ymx','northern muji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7575,0,NULL,'ymz','muzi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7576,0,NULL,'yna','aluo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7577,0,NULL,'ynd','yandruwandha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7578,0,NULL,'yne','lang''e','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7579,0,NULL,'yng','yango','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7580,0,NULL,'ynh','yangho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7581,0,NULL,'ynk','naukan yupik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7582,0,NULL,'ynl','yangulam','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7583,0,NULL,'ynn','yana','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7584,0,NULL,'yno','yong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7585,0,NULL,'yns','yansi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7586,0,NULL,'ynu','yahuna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7587,0,NULL,'yob','yoba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7588,0,NULL,'yog','yogad','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7589,0,NULL,'yoi','yonaguni','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7590,0,NULL,'yok','yokuts','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7591,0,NULL,'yol','yola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7592,0,NULL,'yom','yombe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7593,0,NULL,'yon','yonggom','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7594,0,NULL,'yos','yos','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7595,0,NULL,'yox','yoron','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7596,0,NULL,'yoy','yoy','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7597,0,NULL,'ypa','phala','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7598,0,NULL,'ypb','labo phowa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7599,0,NULL,'ypg','phola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7600,0,NULL,'yph','phupha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7601,0,NULL,'ypk','yupik languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(7602,0,NULL,'ypm','phuma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7603,0,NULL,'ypn','ani phowa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7604,0,NULL,'ypo','alo phola','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7605,0,NULL,'ypp','phupa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7606,0,NULL,'ypz','phuza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7607,0,NULL,'yra','yerakai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7608,0,NULL,'yrb','yareba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7609,0,NULL,'yre','yaouré','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7610,0,NULL,'yri','yarí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7611,0,NULL,'yrk','nenets','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7612,0,NULL,'yrl','nhengatu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7613,0,NULL,'yrn','yerong','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7614,0,NULL,'yrs','yarsun','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7615,0,NULL,'yrw','yarawata','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7616,0,NULL,'ysc','yassic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7617,0,NULL,'ysd','samatao','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7618,0,NULL,'ysl','yugoslavian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7619,0,NULL,'ysn','sani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7620,0,NULL,'yso','nisi (china)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7621,0,NULL,'ysp','southern lolopo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7622,0,NULL,'ysr','sirenik yupik','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7623,0,NULL,'yss','yessan-mayo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7624,0,NULL,'ysy','sanie','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7625,0,NULL,'yta','talu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7626,0,NULL,'ytl','tanglang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7627,0,NULL,'ytp','thopho','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7628,0,NULL,'ytw','yout wam','1268265600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7629,0,NULL,'yua','yucatec maya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7629,0,NULL,'yua','yucateco','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7630,0,NULL,'yub','yugambal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7631,0,NULL,'yuc','yuchi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7632,0,NULL,'yud','judeo-tripolitanian arabic','1248825600',NULL,NULL,NULL,NULL,'jrb',NULL,NULL); +INSERT INTO "iana_records" VALUES(7633,0,NULL,'yue','yue chinese','1248825600',NULL,NULL,NULL,NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(7634,0,NULL,'yuf','havasupai-walapai-yavapai','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7635,0,NULL,'yug','yug','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7636,0,NULL,'yui','yurutí','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7637,0,NULL,'yuj','karkar-yuri','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7638,0,NULL,'yuk','yuki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7639,0,NULL,'yul','yulu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7640,0,NULL,'yum','quechan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7641,0,NULL,'yun','bena (nigeria)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7642,0,NULL,'yup','yukpa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7643,0,NULL,'yuq','yuqui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7644,0,NULL,'yur','yurok','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7645,0,NULL,'yut','yopno','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7646,0,NULL,'yuu','yugh','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7647,0,NULL,'yuw','yau (morobe province)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7648,0,NULL,'yux','southern yukaghir','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7649,0,NULL,'yuy','east yugur','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7650,0,NULL,'yuz','yuracare','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7651,0,NULL,'yva','yawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7652,0,NULL,'yvt','yavitero','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7653,0,NULL,'ywa','kalou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7654,0,NULL,'ywl','western lalu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7655,0,NULL,'ywn','yawanawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7656,0,NULL,'ywq','wuding-luquan yi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7657,0,NULL,'ywr','yawuru','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7658,0,NULL,'ywt','xishanba lalo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7659,0,NULL,'ywu','wumeng nasu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7660,0,NULL,'yww','yawarawarga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7661,0,NULL,'yyu','yau (sandaun province)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7662,0,NULL,'yyz','ayizi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7663,0,NULL,'yzg','e''ma buyang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7664,0,NULL,'yzk','zokhuo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7665,0,NULL,'zaa','sierra de juárez zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7666,0,NULL,'zab','san juan guelavía zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7667,0,NULL,'zac','ocotlán zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7668,0,NULL,'zad','cajonos zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7669,0,NULL,'zae','yareni zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7670,0,NULL,'zaf','ayoquesco zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7671,0,NULL,'zag','zaghawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7672,0,NULL,'zah','zangwal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7673,0,NULL,'zai','isthmus zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7674,0,NULL,'zaj','zaramo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7675,0,NULL,'zak','zanaki','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7676,0,NULL,'zal','zauzou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7677,0,NULL,'zam','miahuatlán zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7678,0,NULL,'zao','ozolotepec zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7679,0,NULL,'zap','zapotec','1129420800',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(7680,0,NULL,'zaq','aloápam zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7681,0,NULL,'zar','rincón zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7682,0,NULL,'zas','santo domingo albarradas zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7683,0,NULL,'zat','tabaa zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7684,0,NULL,'zau','zangskari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7685,0,NULL,'zav','yatzachi zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7686,0,NULL,'zaw','mitla zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7687,0,NULL,'zax','xadani zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7688,0,NULL,'zay','zayse-zergulla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7688,0,NULL,'zay','zaysete','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7689,0,NULL,'zaz','zari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7690,0,NULL,'zbc','central berawan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7691,0,NULL,'zbe','east berawan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7692,0,NULL,'zbl','bliss','1187654400',NULL,NULL,NULL,'blis',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7692,0,NULL,'zbl','blissymbolics','1187654400',NULL,NULL,NULL,'blis',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7692,0,NULL,'zbl','blissymbols','1187654400',NULL,NULL,NULL,'blis',NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7693,0,NULL,'zbt','batui','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7694,0,NULL,'zbw','west berawan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7695,0,NULL,'zca','coatecas altas zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7696,0,NULL,'zch','central hongshuihe zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7697,0,NULL,'zdj','ngazidja comorian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7698,0,NULL,'zea','zeeuws','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7699,0,NULL,'zeg','zenag','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7700,0,NULL,'zeh','eastern hongshuihe zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7701,0,NULL,'zen','zenaga','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7702,0,NULL,'zga','kinga','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7703,0,NULL,'zgb','guibei zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7704,0,NULL,'zgm','minz zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7705,0,NULL,'zgn','guibian zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7706,0,NULL,'zgr','magori','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7707,0,NULL,'zhb','zhaba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7708,0,NULL,'zhd','dai zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7709,0,NULL,'zhi','zhire','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7710,0,NULL,'zhn','nong zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7711,0,NULL,'zhw','zhoa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7712,0,NULL,'zhx','chinese (family)','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(7713,0,NULL,'zia','zia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7714,0,NULL,'zib','zimbabwe sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7715,0,NULL,'zik','zimakani','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7716,0,NULL,'zim','mesme','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7717,0,NULL,'zin','zinza','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7718,0,NULL,'zir','ziriya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7719,0,NULL,'ziw','zigula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7720,0,NULL,'ziz','zizilivakan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7721,0,NULL,'zka','kaimbulawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7722,0,NULL,'zkb','koibal','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7723,0,NULL,'zkg','koguryo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7724,0,NULL,'zkh','khorezmian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7725,0,NULL,'zkk','karankawa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7726,0,NULL,'zko','kott','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7727,0,NULL,'zkp','são paulo kaingáng','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7728,0,NULL,'zkr','zakhring','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7729,0,NULL,'zkt','kitan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7730,0,NULL,'zku','kaurna','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7731,0,NULL,'zkv','krevinian','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7732,0,NULL,'zkz','khazar','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7733,0,NULL,'zle','east slavic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(7734,0,NULL,'zlj','liujiang zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7735,0,NULL,'zlm','malay (individual language)','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7736,0,NULL,'zln','lianshan zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7737,0,NULL,'zlq','liuqian zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7738,0,NULL,'zls','south slavic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(7739,0,NULL,'zlw','west slavic languages','1248825600',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(7740,0,NULL,'zma','manda (australia)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7741,0,NULL,'zmb','zimba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7742,0,NULL,'zmc','margany','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7743,0,NULL,'zmd','maridan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7744,0,NULL,'zme','mangerr','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7745,0,NULL,'zmf','mfinu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7746,0,NULL,'zmg','marti ke','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7747,0,NULL,'zmh','makolkol','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7748,0,NULL,'zmi','negeri sembilan malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7749,0,NULL,'zmj','maridjabin','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7750,0,NULL,'zmk','mandandanyi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7751,0,NULL,'zml','madngele','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7752,0,NULL,'zmm','marimanindji','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7753,0,NULL,'zmn','mbangwe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7754,0,NULL,'zmo','molo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7755,0,NULL,'zmp','mpuono','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7756,0,NULL,'zmq','mituku','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7757,0,NULL,'zmr','maranunggu','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7758,0,NULL,'zms','mbesa','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7759,0,NULL,'zmt','maringarr','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7760,0,NULL,'zmu','muruwari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7761,0,NULL,'zmv','mbariman-gudhinma','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7762,0,NULL,'zmw','mbo (democratic republic of congo)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7763,0,NULL,'zmx','bomitaba','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7764,0,NULL,'zmy','mariyedi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7765,0,NULL,'zmz','mbandja','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7766,0,NULL,'zna','zan gula','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7767,0,NULL,'znd','zande languages','1129420800',NULL,NULL,NULL,NULL,NULL,'collection',NULL); +INSERT INTO "iana_records" VALUES(7768,0,NULL,'zne','zande (individual language)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7769,0,NULL,'zng','mang','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7770,0,NULL,'znk','manangkari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7771,0,NULL,'zns','mangas','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7772,0,NULL,'zoc','copainalá zoque','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7773,0,NULL,'zoh','chimalapa zoque','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7774,0,NULL,'zom','zou','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7775,0,NULL,'zoo','asunción mixtepec zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7776,0,NULL,'zoq','tabasco zoque','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7777,0,NULL,'zor','rayón zoque','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7778,0,NULL,'zos','francisco león zoque','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7779,0,NULL,'zpa','lachiguiri zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7780,0,NULL,'zpb','yautepec zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7781,0,NULL,'zpc','choapan zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7782,0,NULL,'zpd','southeastern ixtlán zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7783,0,NULL,'zpe','petapa zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7784,0,NULL,'zpf','san pedro quiatoni zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7785,0,NULL,'zpg','guevea de humboldt zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7786,0,NULL,'zph','totomachapan zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7787,0,NULL,'zpi','santa maría quiegolani zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7788,0,NULL,'zpj','quiavicuzas zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7789,0,NULL,'zpk','tlacolulita zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7790,0,NULL,'zpl','lachixío zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7791,0,NULL,'zpm','mixtepec zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7792,0,NULL,'zpn','santa inés yatzechi zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7793,0,NULL,'zpo','amatlán zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7794,0,NULL,'zpp','el alto zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7795,0,NULL,'zpq','zoogocho zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7796,0,NULL,'zpr','santiago xanica zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7797,0,NULL,'zps','coatlán zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7798,0,NULL,'zpt','san vicente coatlán zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7799,0,NULL,'zpu','yalálag zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7800,0,NULL,'zpv','chichicapan zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7801,0,NULL,'zpw','zaniza zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7802,0,NULL,'zpx','san baltazar loxicha zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7803,0,NULL,'zpy','mazaltepec zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7804,0,NULL,'zpz','texmelucan zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7805,0,NULL,'zqe','qiubei zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7806,0,NULL,'zra','kara (korea)','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7807,0,NULL,'zrg','mirgan','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7808,0,NULL,'zrn','zerenkel','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7809,0,NULL,'zro','záparo','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7810,0,NULL,'zrp','zarphatic','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7811,0,NULL,'zrs','mairasi','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7812,0,NULL,'zsa','sarasira','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7813,0,NULL,'zsk','kaskean','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7814,0,NULL,'zsl','zambian sign language','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7815,0,NULL,'zsm','standard malay','1248825600',NULL,NULL,NULL,NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7816,0,NULL,'zsr','southern rincon zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7817,0,NULL,'zsu','sukurum','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7818,0,NULL,'zte','elotepec zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7819,0,NULL,'ztg','xanaguía zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7820,0,NULL,'ztl','lapaguía-guivini zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7821,0,NULL,'ztm','san agustín mixtepec zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7822,0,NULL,'ztn','santa catarina albarradas zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7823,0,NULL,'ztp','loxicha zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7824,0,NULL,'ztq','quioquitani-quierí zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7825,0,NULL,'zts','tilquiapan zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7826,0,NULL,'ztt','tejalapan zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7827,0,NULL,'ztu','güilá zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7828,0,NULL,'ztx','zaachila zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7829,0,NULL,'zty','yatee zapotec','1248825600',NULL,NULL,NULL,NULL,'zap',NULL,NULL); +INSERT INTO "iana_records" VALUES(7830,0,NULL,'zua','zeem','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7831,0,NULL,'zuh','tokano','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7832,0,NULL,'zum','kumzari','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7833,0,NULL,'zun','zuni','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7834,0,NULL,'zuy','zumaya','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7835,0,NULL,'zwa','zay','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7836,0,NULL,'zxx','no linguistic content','1141776000',NULL,NULL,NULL,NULL,NULL,'special',NULL); +INSERT INTO "iana_records" VALUES(7836,0,NULL,'zxx','not applicable','1141776000',NULL,NULL,NULL,NULL,NULL,'special',NULL); +INSERT INTO "iana_records" VALUES(7837,0,NULL,'zyb','yongbei zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7838,0,NULL,'zyg','yang zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7839,0,NULL,'zyj','youjiang zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7840,0,NULL,'zyn','yongnan zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7841,0,NULL,'zyp','zyphe','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7842,0,NULL,'zza','dimili','1156377600',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(7842,0,NULL,'zza','dimli (macrolanguage)','1156377600',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(7842,0,NULL,'zza','kirdki','1156377600',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(7842,0,NULL,'zza','kirmanjki (macrolanguage)','1156377600',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(7842,0,NULL,'zza','zaza','1156377600',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(7842,0,NULL,'zza','zazaki','1156377600',NULL,NULL,NULL,NULL,NULL,'macrolanguage',NULL); +INSERT INTO "iana_records" VALUES(7843,6,NULL,'zzj','zuojiang zhuang','1248825600',NULL,NULL,NULL,NULL,'za',NULL,NULL); +INSERT INTO "iana_records" VALUES(7844,6,NULL,'aao','algerian saharan arabic','1248825600',NULL,'aao','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7845,6,NULL,'abh','tajiki arabic','1248825600',NULL,'abh','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7846,6,NULL,'abv','baharna arabic','1248825600',NULL,'abv','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7847,6,NULL,'acm','mesopotamian arabic','1248825600',NULL,'acm','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7848,6,NULL,'acq','ta''izzi-adeni arabic','1248825600',NULL,'acq','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7849,6,NULL,'acw','hijazi arabic','1248825600',NULL,'acw','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7850,6,NULL,'acx','omani arabic','1248825600',NULL,'acx','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7851,6,NULL,'acy','cypriot arabic','1248825600',NULL,'acy','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7852,6,NULL,'adf','dhofari arabic','1248825600',NULL,'adf','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7853,6,NULL,'ads','adamorobe sign language','1248825600',NULL,'ads','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7854,6,NULL,'aeb','tunisian arabic','1248825600',NULL,'aeb','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7855,6,NULL,'aec','saidi arabic','1248825600',NULL,'aec','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7856,6,NULL,'aed','argentine sign language','1248825600',NULL,'aed','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7857,6,NULL,'aen','armenian sign language','1248825600',NULL,'aen','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7858,6,NULL,'afb','gulf arabic','1248825600',NULL,'afb','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7859,6,NULL,'afg','afghan sign language','1248825600',NULL,'afg','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7860,6,NULL,'ajp','south levantine arabic','1248825600',NULL,'ajp','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7861,6,NULL,'apc','north levantine arabic','1248825600',NULL,'apc','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7862,6,NULL,'apd','sudanese arabic','1248825600',NULL,'apd','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7863,6,NULL,'arb','standard arabic','1248825600',NULL,'arb','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7864,6,NULL,'arq','algerian arabic','1248825600',NULL,'arq','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7865,6,NULL,'ars','najdi arabic','1248825600',NULL,'ars','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7866,6,NULL,'ary','moroccan arabic','1248825600',NULL,'ary','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7867,6,NULL,'arz','egyptian arabic','1248825600',NULL,'arz','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7868,6,NULL,'ase','american sign language','1248825600',NULL,'ase','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7869,6,NULL,'asf','australian sign language','1248825600',NULL,'asf','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7870,6,NULL,'asp','algerian sign language','1248825600',NULL,'asp','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7871,6,NULL,'asq','austrian sign language','1248825600',NULL,'asq','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7872,6,NULL,'asw','australian aborigines sign language','1248825600',NULL,'asw','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7873,6,NULL,'auz','uzbeki arabic','1248825600',NULL,'auz','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7874,6,NULL,'avl','eastern egyptian bedawi arabic','1248825600',NULL,'avl','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7875,6,NULL,'ayh','hadrami arabic','1248825600',NULL,'ayh','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7876,6,NULL,'ayl','libyan arabic','1248825600',NULL,'ayl','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7877,6,NULL,'ayn','sanaani arabic','1248825600',NULL,'ayn','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7878,6,NULL,'ayp','north mesopotamian arabic','1248825600',NULL,'ayp','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7879,6,NULL,'bbz','babalia creole arabic','1248825600',NULL,'bbz','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(7880,6,NULL,'bfi','british sign language','1248825600',NULL,'bfi','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7881,6,NULL,'bfk','ban khor sign language','1248825600',NULL,'bfk','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7882,6,NULL,'bjn','banjar','1248825600',NULL,'bjn','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7883,6,NULL,'bog','bamako sign language','1248825600',NULL,'bog','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7884,6,NULL,'bqn','bulgarian sign language','1248825600',NULL,'bqn','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7885,6,NULL,'bqy','bengkala sign language','1248825600',NULL,'bqy','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7886,6,NULL,'btj','bacanese malay','1248825600',NULL,'btj','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7887,6,NULL,'bve','berau malay','1248825600',NULL,'bve','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7888,6,NULL,'bvl','bolivian sign language','1248825600',NULL,'bvl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7889,6,NULL,'bvu','bukit malay','1248825600',NULL,'bvu','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7890,6,NULL,'bzs','brazilian sign language','1248825600',NULL,'bzs','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7891,6,NULL,'cdo','min dong chinese','1248825600',NULL,'cdo','zh',NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(7892,6,NULL,'cds','chadian sign language','1248825600',NULL,'cds','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7893,6,NULL,'cjy','jinyu chinese','1248825600',NULL,'cjy','zh',NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(7894,6,NULL,'cmn','mandarin chinese','1248825600',NULL,'cmn','zh',NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(7895,6,NULL,'coa','cocos islands malay','1248825600',NULL,'coa','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7896,6,NULL,'cpx','pu-xian chinese','1248825600',NULL,'cpx','zh',NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(7897,6,NULL,'csc','catalan sign language','1248825600',NULL,'csc','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7897,6,NULL,'csc','lengua de señas catalana','1248825600',NULL,'csc','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7897,6,NULL,'csc','llengua de signes catalana','1248825600',NULL,'csc','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7898,6,NULL,'csd','chiangmai sign language','1248825600',NULL,'csd','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7899,6,NULL,'cse','czech sign language','1248825600',NULL,'cse','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7900,6,NULL,'csf','cuba sign language','1248825600',NULL,'csf','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7901,6,NULL,'csg','chilean sign language','1248825600',NULL,'csg','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7902,6,NULL,'csl','chinese sign language','1248825600',NULL,'csl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7903,6,NULL,'csn','colombian sign language','1248825600',NULL,'csn','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7904,6,NULL,'csq','croatia sign language','1248825600',NULL,'csq','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7905,6,NULL,'csr','costa rican sign language','1248825600',NULL,'csr','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7906,6,NULL,'czh','huizhou chinese','1248825600',NULL,'czh','zh',NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(7907,6,NULL,'czo','min zhong chinese','1248825600',NULL,'czo','zh',NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(7908,6,NULL,'doq','dominican sign language','1248825600',NULL,'doq','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7909,6,NULL,'dse','dutch sign language','1248825600',NULL,'dse','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7910,6,NULL,'dsl','danish sign language','1248825600',NULL,'dsl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7911,6,NULL,'dup','duano','1248825600',NULL,'dup','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7912,6,NULL,'ecs','ecuadorian sign language','1248825600',NULL,'ecs','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7913,6,NULL,'esl','egypt sign language','1248825600',NULL,'esl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7914,6,NULL,'esn','salvadoran sign language','1248825600',NULL,'esn','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7915,6,NULL,'eso','estonian sign language','1248825600',NULL,'eso','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7916,6,NULL,'eth','ethiopian sign language','1248825600',NULL,'eth','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7917,6,NULL,'fcs','quebec sign language','1248825600',NULL,'fcs','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7918,6,NULL,'fse','finnish sign language','1248825600',NULL,'fse','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7919,6,NULL,'fsl','french sign language','1248825600',NULL,'fsl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7920,6,NULL,'fss','finland-swedish sign language','1248825600',NULL,'fss','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7920,6,NULL,'fss','finlandssvenskt teckenspråk','1248825600',NULL,'fss','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7920,6,NULL,'fss','suomenruotsalainen viittomakieli','1248825600',NULL,'fss','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7921,6,NULL,'gan','gan chinese','1248825600',NULL,'gan','zh',NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(7922,6,NULL,'gom','goan konkani','1248825600',NULL,'gom','kok',NULL,'kok',NULL,NULL); +INSERT INTO "iana_records" VALUES(7923,6,NULL,'gse','ghanaian sign language','1248825600',NULL,'gse','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7924,6,NULL,'gsg','german sign language','1248825600',NULL,'gsg','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7925,6,NULL,'gsm','guatemalan sign language','1248825600',NULL,'gsm','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7926,6,NULL,'gss','greek sign language','1248825600',NULL,'gss','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7927,6,NULL,'gus','guinean sign language','1248825600',NULL,'gus','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7928,6,NULL,'hab','hanoi sign language','1248825600',NULL,'hab','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7929,6,NULL,'haf','haiphong sign language','1248825600',NULL,'haf','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7930,6,NULL,'hak','hakka chinese','1248825600',NULL,'hak','zh',NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(7931,6,NULL,'hds','honduras sign language','1248825600',NULL,'hds','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7932,6,NULL,'hji','haji','1248825600',NULL,'hji','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7933,6,NULL,'hks','heung kong sau yue','1248825600',NULL,'hks','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7933,6,NULL,'hks','hong kong sign language','1248825600',NULL,'hks','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7934,6,NULL,'hos','ho chi minh city sign language','1248825600',NULL,'hos','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7935,6,NULL,'hps','hawai''i pidgin sign language','1248825600',NULL,'hps','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7936,6,NULL,'hsh','hungarian sign language','1248825600',NULL,'hsh','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7937,6,NULL,'hsl','hausa sign language','1248825600',NULL,'hsl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7938,6,NULL,'hsn','xiang chinese','1248825600',NULL,'hsn','zh',NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(7939,6,NULL,'icl','icelandic sign language','1248825600',NULL,'icl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7940,6,NULL,'ils','international sign','1248825600',NULL,'ils','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7941,6,NULL,'inl','indonesian sign language','1248825600',NULL,'inl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7942,6,NULL,'ins','indian sign language','1248825600',NULL,'ins','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7943,6,NULL,'ise','italian sign language','1248825600',NULL,'ise','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7944,6,NULL,'isg','irish sign language','1248825600',NULL,'isg','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7945,6,NULL,'isr','israeli sign language','1248825600',NULL,'isr','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7946,6,NULL,'jak','jakun','1248825600',NULL,'jak','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7947,6,NULL,'jax','jambi malay','1248825600',NULL,'jax','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7948,6,NULL,'jcs','jamaican country sign language','1248825600',NULL,'jcs','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7949,6,NULL,'jhs','jhankot sign language','1248825600',NULL,'jhs','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7950,6,NULL,'jls','jamaican sign language','1268265600',NULL,'jls','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7951,6,NULL,'jos','jordanian sign language','1248825600',NULL,'jos','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7952,6,NULL,'jsl','japanese sign language','1248825600',NULL,'jsl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7953,6,NULL,'jus','jumla sign language','1248825600',NULL,'jus','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7954,6,NULL,'kgi','selangor sign language','1248825600',NULL,'kgi','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7955,6,NULL,'knn','konkani (individual language)','1248825600',NULL,'knn','kok',NULL,'kok',NULL,NULL); +INSERT INTO "iana_records" VALUES(7956,6,NULL,'kvb','kubu','1248825600',NULL,'kvb','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7957,6,NULL,'kvk','korean sign language','1248825600',NULL,'kvk','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7958,6,NULL,'kvr','kerinci','1248825600',NULL,'kvr','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7959,6,NULL,'kxd','brunei','1248825600',NULL,'kxd','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7960,6,NULL,'lbs','libyan sign language','1248825600',NULL,'lbs','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7961,6,NULL,'lce','loncong','1248825600',NULL,'lce','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7962,6,NULL,'lcf','lubu','1248825600',NULL,'lcf','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7963,6,NULL,'liw','col','1248825600',NULL,'liw','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7964,6,NULL,'lls','lithuanian sign language','1248825600',NULL,'lls','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7965,6,NULL,'lsg','lyons sign language','1248825600',NULL,'lsg','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7966,6,NULL,'lsl','latvian sign language','1248825600',NULL,'lsl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7967,6,NULL,'lso','laos sign language','1248825600',NULL,'lso','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7968,6,NULL,'lsp','lengua de señas panameñas','1248825600',NULL,'lsp','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7968,6,NULL,'lsp','panamanian sign language','1248825600',NULL,'lsp','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7969,6,NULL,'lst','trinidad and tobago sign language','1248825600',NULL,'lst','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7970,6,NULL,'lsy','mauritian sign language','1268265600',NULL,'lsy','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7971,6,NULL,'ltg','latgalian','1268265600',NULL,'ltg','lv',NULL,'lv',NULL,NULL); +INSERT INTO "iana_records" VALUES(7972,6,NULL,'lvs','standard latvian','1268265600',NULL,'lvs','lv',NULL,'lv',NULL,NULL); +INSERT INTO "iana_records" VALUES(7973,6,NULL,'lzh','literary chinese','1248825600',NULL,'lzh','zh',NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(7974,6,NULL,'max','north moluccan malay','1248825600',NULL,'max','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7975,6,NULL,'mdl','maltese sign language','1248825600',NULL,'mdl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7976,6,NULL,'meo','kedah malay','1248825600',NULL,'meo','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7977,6,NULL,'mfa','pattani malay','1248825600',NULL,'mfa','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7978,6,NULL,'mfb','bangka','1248825600',NULL,'mfb','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7979,6,NULL,'mfs','mexican sign language','1248825600',NULL,'mfs','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7980,6,NULL,'min','minangkabau','1248825600',NULL,'min','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7981,6,NULL,'mnp','min bei chinese','1248825600',NULL,'mnp','zh',NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(7982,6,NULL,'mqg','kota bangun kutai malay','1248825600',NULL,'mqg','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7983,6,NULL,'mre','martha''s vineyard sign language','1248825600',NULL,'mre','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7984,6,NULL,'msd','yucatec maya sign language','1248825600',NULL,'msd','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7985,6,NULL,'msi','sabah malay','1248825600',NULL,'msi','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7986,6,NULL,'msr','mongolian sign language','1248825600',NULL,'msr','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7987,6,NULL,'mui','musi','1248825600',NULL,'mui','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(7988,6,NULL,'mzc','madagascar sign language','1248825600',NULL,'mzc','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7989,6,NULL,'mzg','monastic sign language','1248825600',NULL,'mzg','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7990,6,NULL,'mzy','mozambican sign language','1248825600',NULL,'mzy','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7991,6,NULL,'nan','min nan chinese','1248825600',NULL,'nan','zh',NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(7992,6,NULL,'nbs','namibian sign language','1248825600',NULL,'nbs','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7993,6,NULL,'ncs','nicaraguan sign language','1248825600',NULL,'ncs','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7994,6,NULL,'nsi','nigerian sign language','1248825600',NULL,'nsi','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7995,6,NULL,'nsl','norwegian sign language','1248825600',NULL,'nsl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7996,6,NULL,'nsp','nepalese sign language','1248825600',NULL,'nsp','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7997,6,NULL,'nsr','maritime sign language','1248825600',NULL,'nsr','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7998,6,NULL,'nzs','new zealand sign language','1248825600',NULL,'nzs','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(7999,6,NULL,'okl','old kentish sign language','1248825600',NULL,'okl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8000,6,NULL,'orn','orang kanaq','1248825600',NULL,'orn','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(8001,6,NULL,'ors','orang seletar','1248825600',NULL,'ors','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(8002,6,NULL,'pel','pekal','1248825600',NULL,'pel','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(8003,6,NULL,'pga','sudanese creole arabic','1248825600',NULL,'pga','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(8004,6,NULL,'pks','pakistan sign language','1248825600',NULL,'pks','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8005,6,NULL,'prl','peruvian sign language','1248825600',NULL,'prl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8006,6,NULL,'prz','providencia sign language','1248825600',NULL,'prz','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8007,6,NULL,'psc','persian sign language','1248825600',NULL,'psc','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8008,6,NULL,'psd','plains indian sign language','1248825600',NULL,'psd','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8009,6,NULL,'pse','central malay','1248825600',NULL,'pse','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(8010,6,NULL,'psg','penang sign language','1248825600',NULL,'psg','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8011,6,NULL,'psl','puerto rican sign language','1248825600',NULL,'psl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8012,6,NULL,'pso','polish sign language','1248825600',NULL,'pso','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8013,6,NULL,'psp','philippine sign language','1248825600',NULL,'psp','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8014,6,NULL,'psr','portuguese sign language','1248825600',NULL,'psr','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8015,6,NULL,'pys','lengua de señas del paraguay','1268265600',NULL,'pys','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8015,6,NULL,'pys','paraguayan sign language','1268265600',NULL,'pys','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8016,6,NULL,'rms','romanian sign language','1248825600',NULL,'rms','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8017,6,NULL,'rsi','rennellese sign language','1248825600',NULL,'rsi','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8018,6,NULL,'rsl','russian sign language','1248825600',NULL,'rsl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8019,6,NULL,'sdl','saudi arabian sign language','1248825600',NULL,'sdl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8020,6,NULL,'sfb','french belgian sign language','1248825600',NULL,'sfb','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8020,6,NULL,'sfb','langue des signes de belgique francophone','1248825600',NULL,'sfb','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8021,6,NULL,'sfs','south african sign language','1248825600',NULL,'sfs','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8022,6,NULL,'sgg','swiss-german sign language','1248825600',NULL,'sgg','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8023,6,NULL,'sgx','sierra leone sign language','1248825600',NULL,'sgx','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8024,6,NULL,'shu','chadian arabic','1248825600',NULL,'shu','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(8025,6,NULL,'slf','swiss-italian sign language','1248825600',NULL,'slf','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8026,6,NULL,'sls','singapore sign language','1248825600',NULL,'sls','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8027,6,NULL,'sqs','sri lankan sign language','1248825600',NULL,'sqs','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8028,6,NULL,'ssh','shihhi arabic','1248825600',NULL,'ssh','ar',NULL,'ar',NULL,NULL); +INSERT INTO "iana_records" VALUES(8029,6,NULL,'ssp','spanish sign language','1248825600',NULL,'ssp','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8030,6,NULL,'ssr','swiss-french sign language','1248825600',NULL,'ssr','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8031,6,NULL,'svk','slovakian sign language','1248825600',NULL,'svk','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8032,6,NULL,'swc','congo swahili','1248825600',NULL,'swc','sw',NULL,'sw',NULL,NULL); +INSERT INTO "iana_records" VALUES(8033,6,NULL,'swh','kiswahili','1248825600',NULL,'swh','sw',NULL,'sw',NULL,NULL); +INSERT INTO "iana_records" VALUES(8033,6,NULL,'swh','swahili (individual language)','1248825600',NULL,'swh','sw',NULL,'sw',NULL,NULL); +INSERT INTO "iana_records" VALUES(8034,6,NULL,'swl','swedish sign language','1248825600',NULL,'swl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8035,6,NULL,'syy','al-sayyid bedouin sign language','1248825600',NULL,'syy','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8036,6,NULL,'tmw','temuan','1248825600',NULL,'tmw','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(8037,6,NULL,'tse','tunisian sign language','1248825600',NULL,'tse','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8038,6,NULL,'tsm','turkish sign language','1248825600',NULL,'tsm','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8038,6,NULL,'tsm','türk İşaret dili','1248825600',NULL,'tsm','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8039,6,NULL,'tsq','thai sign language','1248825600',NULL,'tsq','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8040,6,NULL,'tss','taiwan sign language','1248825600',NULL,'tss','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8041,6,NULL,'tsy','tebul sign language','1248825600',NULL,'tsy','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8042,6,NULL,'tza','tanzanian sign language','1248825600',NULL,'tza','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8043,6,NULL,'ugn','ugandan sign language','1248825600',NULL,'ugn','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8044,6,NULL,'ugy','uruguayan sign language','1248825600',NULL,'ugy','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8045,6,NULL,'ukl','ukrainian sign language','1248825600',NULL,'ukl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8046,6,NULL,'uks','kaapor sign language','1248825600',NULL,'uks','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8046,6,NULL,'uks','urubú-kaapor sign language','1248825600',NULL,'uks','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8047,6,NULL,'urk','urak lawoi''','1248825600',NULL,'urk','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(8048,6,NULL,'uzn','northern uzbek','1248825600',NULL,'uzn','uz',NULL,'uz',NULL,NULL); +INSERT INTO "iana_records" VALUES(8049,6,NULL,'uzs','southern uzbek','1248825600',NULL,'uzs','uz',NULL,'uz',NULL,NULL); +INSERT INTO "iana_records" VALUES(8050,6,NULL,'vgt','flemish sign language','1248825600',NULL,'vgt','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8050,6,NULL,'vgt','vlaamse gebarentaal','1248825600',NULL,'vgt','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8051,6,NULL,'vkk','kaur','1248825600',NULL,'vkk','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(8052,6,NULL,'vkt','tenggarong kutai malay','1248825600',NULL,'vkt','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(8053,6,NULL,'vsi','moldova sign language','1248825600',NULL,'vsi','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8054,6,NULL,'vsl','venezuelan sign language','1248825600',NULL,'vsl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8055,6,NULL,'vsv','llengua de signes valenciana','1248825600',NULL,'vsv','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8055,6,NULL,'vsv','valencian sign language','1248825600',NULL,'vsv','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8056,6,NULL,'wuu','wu chinese','1248825600',NULL,'wuu','zh',NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(8057,6,NULL,'xki','kenyan sign language','1248825600',NULL,'xki','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8058,6,NULL,'xml','malaysian sign language','1248825600',NULL,'xml','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8059,6,NULL,'xmm','manado malay','1248825600',NULL,'xmm','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(8060,6,NULL,'xms','moroccan sign language','1248825600',NULL,'xms','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8061,6,NULL,'yds','yiddish sign language','1248825600',NULL,'yds','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8062,6,NULL,'ysl','yugoslavian sign language','1248825600',NULL,'ysl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8063,6,NULL,'yue','yue chinese','1248825600',NULL,'yue','zh',NULL,'zh',NULL,NULL); +INSERT INTO "iana_records" VALUES(8064,6,NULL,'zib','zimbabwe sign language','1248825600',NULL,'zib','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8065,6,NULL,'zlm','malay (individual language)','1248825600',NULL,'zlm','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(8066,6,NULL,'zmi','negeri sembilan malay','1248825600',NULL,'zmi','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(8067,6,NULL,'zsl','zambian sign language','1248825600',NULL,'zsl','sgn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8068,1,NULL,'zsm','standard malay','1248825600',NULL,'zsm','ms',NULL,'ms',NULL,NULL); +INSERT INTO "iana_records" VALUES(8069,1,NULL,'arab','arabic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8070,1,NULL,'armi','imperial aramaic','1196812800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8071,1,NULL,'armn','armenian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8072,1,NULL,'avst','avestan','1185580800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8073,1,NULL,'bali','balinese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8074,1,NULL,'bamu','bamum','1248912000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8075,1,NULL,'bass','bassa vah','1270857600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8076,1,NULL,'batk','batak','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8077,1,NULL,'beng','bengali','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8078,1,NULL,'blis','blissymbols','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8079,1,NULL,'bopo','bopomofo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8080,1,NULL,'brah','brahmi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8081,1,NULL,'brai','braille','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8082,1,NULL,'bugi','buginese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8083,1,NULL,'buhd','buhid','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8084,1,NULL,'cakm','chakma','1196812800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8085,1,NULL,'cans','unified canadian aboriginal syllabics','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8086,1,NULL,'cari','carian','1153440000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8087,1,NULL,'cham','cham','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8088,1,NULL,'cher','cherokee','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8089,1,NULL,'cirt','cirth','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8090,1,NULL,'copt','coptic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8091,1,NULL,'cprt','cypriot','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8092,1,NULL,'cyrl','cyrillic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8093,1,NULL,'cyrs','cyrillic (old church slavonic variant)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8094,1,NULL,'deva','devanagari','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8094,1,NULL,'deva','nagari','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8095,1,NULL,'dsrt','deseret','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8095,1,NULL,'dsrt','mormon','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8096,1,NULL,'egyd','egyptian demotic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8097,1,NULL,'egyh','egyptian hieratic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8098,1,NULL,'egyp','egyptian hieroglyphs','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8099,1,NULL,'ethi','ethiopic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8099,1,NULL,'ethi','ge''ez','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8099,1,NULL,'ethi','geʻez','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8100,1,NULL,'geok','khutsuri (asomtavruli and nuskhuri)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8101,1,NULL,'geor','georgian (mkhedruli)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8102,1,NULL,'glag','glagolitic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8103,1,NULL,'goth','gothic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8104,1,NULL,'gran','grantha','1260316800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8105,1,NULL,'grek','greek','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8106,1,NULL,'gujr','gujarati','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8107,1,NULL,'guru','gurmukhi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8108,1,NULL,'hang','hangeul','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8108,1,NULL,'hang','hangul','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8108,1,NULL,'hang','hangŭl','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8109,1,NULL,'hani','han','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8109,1,NULL,'hani','hanja','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8109,1,NULL,'hani','hanzi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8109,1,NULL,'hani','kanji','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8110,1,NULL,'hano','hanunoo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8110,1,NULL,'hano','hanunóo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8111,1,NULL,'hans','han (simplified variant)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8112,1,NULL,'hant','han (traditional variant)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8113,1,NULL,'hebr','hebrew','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8114,1,NULL,'hira','hiragana','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8115,1,NULL,'hmng','pahawh hmong','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8116,1,NULL,'hrkt','(alias for hiragana + katakana)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8117,1,NULL,'hung','old hungarian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8118,1,NULL,'inds','harappan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8118,1,NULL,'inds','indus','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8119,1,NULL,'ital','old italic (etruscan, oscan, etc.)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8120,1,NULL,'java','javanese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8121,1,NULL,'jpan','japanese (alias for han + hiragana + katakana)','1153440000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8122,1,NULL,'kali','kayah li','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8123,1,NULL,'kana','katakana','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8124,1,NULL,'khar','kharoshthi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8125,1,NULL,'khmr','khmer','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8126,1,NULL,'knda','kannada','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8127,1,NULL,'kore','korean (alias for hangul + han)','1183593600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8128,1,NULL,'kpel','kpelle','1270857600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8129,1,NULL,'kthi','kaithi','1196812800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8130,1,NULL,'lana','lanna','1153440000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8130,1,NULL,'lana','tai tham','1153440000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8131,1,NULL,'laoo','lao','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8132,1,NULL,'latf','latin (fraktur variant)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8133,1,NULL,'latg','latin (gaelic variant)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8134,1,NULL,'latn','latin','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8135,1,NULL,'lepc','lepcha','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8135,1,NULL,'lepc','róng','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8136,1,NULL,'limb','limbu','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8137,1,NULL,'lina','linear a','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8138,1,NULL,'linb','linear b','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8139,1,NULL,'lisu','fraser','1236902400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8139,1,NULL,'lisu','lisu','1236902400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8140,1,NULL,'loma','loma','1270857600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8141,1,NULL,'lyci','lycian','1153440000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8142,1,NULL,'lydi','lydian','1153440000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8143,1,NULL,'mand','mandaean','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8143,1,NULL,'mand','mandaic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8144,1,NULL,'mani','manichaean','1185580800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8145,1,NULL,'maya','mayan hieroglyphs','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8146,1,NULL,'mend','mende','1270857600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8147,1,NULL,'merc','meroitic cursive','1260316800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8148,1,NULL,'mero','meroitic hieroglyphs','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8149,1,NULL,'mlym','malayalam','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8150,1,NULL,'mong','mongolian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8151,1,NULL,'moon','moon','1169769600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8151,1,NULL,'moon','moon code','1169769600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8151,1,NULL,'moon','moon script','1169769600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8151,1,NULL,'moon','moon type','1169769600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8152,1,NULL,'mtei','meetei','1169769600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8152,1,NULL,'mtei','meitei mayek','1169769600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8152,1,NULL,'mtei','meithei','1169769600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8153,1,NULL,'mymr','burmese','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8153,1,NULL,'mymr','myanmar','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8154,1,NULL,'narb','ancient north arabian','1270857600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8154,1,NULL,'narb','old north arabian','1270857600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8155,1,NULL,'nbat','nabataean','1270857600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8156,1,NULL,'nkgb','''na-''khi ²ggŏ-¹baw','1236902400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8156,1,NULL,'nkgb','nakhi geba','1236902400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8156,1,NULL,'nkgb','naxi geba','1236902400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8157,1,NULL,'nkoo','n''ko','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8157,1,NULL,'nkoo','n’ko','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8158,1,NULL,'ogam','ogham','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8159,1,NULL,'olck','ol','1153440000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8159,1,NULL,'olck','ol cemet''','1153440000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8159,1,NULL,'olck','ol chiki','1153440000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8159,1,NULL,'olck','santali','1153440000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8160,1,NULL,'orkh','old turkic','1248912000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8160,1,NULL,'orkh','orkhon runic','1248912000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8161,1,NULL,'orya','oriya','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8162,1,NULL,'osma','osmanya','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8163,1,NULL,'palm','palmyrene','1270857600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8164,1,NULL,'perm','old permic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8165,1,NULL,'phag','phags-pa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8166,1,NULL,'phli','inscriptional pahlavi','1196812800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8167,1,NULL,'phlp','psalter pahlavi','1196812800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8168,1,NULL,'phlv','book pahlavi','1185580800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8169,1,NULL,'phnx','phoenician','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8170,1,NULL,'plrd','miao','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8170,1,NULL,'plrd','pollard','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8171,1,NULL,'prti','inscriptional parthian','1196812800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8172,1,NULL,'qaaa..qabx','private use','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8173,1,NULL,'rjng','kaganga','1161043200',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8173,1,NULL,'rjng','redjang','1161043200',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8173,1,NULL,'rjng','rejang','1161043200',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8174,1,NULL,'roro','rongorongo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8175,1,NULL,'runr','runic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8176,1,NULL,'samr','samaritan','1185580800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8177,1,NULL,'sara','sarati','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8178,1,NULL,'sarb','old south arabian','1248912000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8179,1,NULL,'saur','saurashtra','1153440000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8180,1,NULL,'sgnw','signwriting','1161043200',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8181,1,NULL,'shaw','shavian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8181,1,NULL,'shaw','shaw','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8182,1,NULL,'sinh','sinhala','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8183,1,NULL,'sund','sundanese','1153440000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8184,1,NULL,'sylo','syloti nagri','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8185,1,NULL,'syrc','syriac','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8186,1,NULL,'syre','syriac (estrangelo variant)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8187,1,NULL,'syrj','syriac (western variant)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8188,1,NULL,'syrn','syriac (eastern variant)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8189,1,NULL,'tagb','tagbanwa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8190,1,NULL,'tale','tai le','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8191,1,NULL,'talu','new tai lue','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8192,1,NULL,'taml','tamil','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8193,1,NULL,'tavt','tai viet','1196812800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8194,1,NULL,'telu','telugu','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8195,1,NULL,'teng','tengwar','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8196,1,NULL,'tfng','berber','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8196,1,NULL,'tfng','tifinagh','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8197,1,NULL,'tglg','alibata','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8197,1,NULL,'tglg','baybayin','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8197,1,NULL,'tglg','tagalog','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8198,1,NULL,'thaa','thaana','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8199,1,NULL,'thai','thai','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8200,1,NULL,'tibt','tibetan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8201,1,NULL,'ugar','ugaritic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8202,1,NULL,'vaii','vai','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8203,1,NULL,'visp','visible speech','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8204,1,NULL,'wara','varang kshiti','1260316800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8204,1,NULL,'wara','warang citi','1260316800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8205,1,NULL,'xpeo','old persian','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8206,1,NULL,'xsux','sumero-akkadian cuneiform','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8207,1,NULL,'yiii','yi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8208,1,NULL,'zinh','code for inherited script','1238716800',NULL,NULL,NULL,NULL,NULL,NULL,'not intended for use as a language subtag'); +INSERT INTO "iana_records" VALUES(8209,1,NULL,'zmth','mathematical notation','1196812800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8210,1,NULL,'zsym','symbols','1196812800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8211,1,NULL,'zxxx','code for unwritten documents','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8212,1,NULL,'zyyy','code for undetermined script','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8213,2,NULL,'zzzz','code for uncoded script','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8214,2,NULL,'aa','private use','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8215,2,NULL,'ac','ascension island','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8216,2,NULL,'ad','andorra','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8217,2,NULL,'ae','united arab emirates','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8218,2,NULL,'af','afghanistan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8219,2,NULL,'ag','antigua and barbuda','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8220,2,NULL,'ai','anguilla','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8221,2,NULL,'al','albania','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8222,2,NULL,'am','armenia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8223,2,NULL,'an','netherlands antilles','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8224,2,NULL,'ao','angola','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8225,2,NULL,'aq','antarctica','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8226,2,NULL,'ar','argentina','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8227,2,NULL,'as','american samoa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8228,2,NULL,'at','austria','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8229,2,NULL,'au','australia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8230,2,NULL,'aw','aruba','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8231,2,NULL,'ax','Åland islands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8232,2,NULL,'az','azerbaijan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8233,2,NULL,'ba','bosnia and herzegovina','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8234,2,NULL,'bb','barbados','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8235,2,NULL,'bd','bangladesh','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8236,2,NULL,'be','belgium','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8237,2,NULL,'bf','burkina faso','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8238,2,NULL,'bg','bulgaria','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8239,2,NULL,'bh','bahrain','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8240,2,NULL,'bi','burundi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8241,2,NULL,'bj','benin','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8242,2,NULL,'bl','saint barthélemy','1193961600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8243,2,NULL,'bm','bermuda','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8244,2,NULL,'bn','brunei darussalam','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8245,2,NULL,'bo','bolivia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8246,2,NULL,'br','brazil','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8247,2,NULL,'bs','bahamas','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8248,2,NULL,'bt','bhutan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8249,2,NULL,'bu','burma','1129420800',628819200,'mm',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8250,2,NULL,'bv','bouvet island','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8251,2,NULL,'bw','botswana','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8252,2,NULL,'by','belarus','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8253,2,NULL,'bz','belize','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8254,2,NULL,'ca','canada','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8255,2,NULL,'cc','cocos (keeling) islands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8256,2,NULL,'cd','the democratic republic of the congo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8257,2,NULL,'cf','central african republic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8258,2,NULL,'cg','congo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8259,2,NULL,'ch','switzerland','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8260,2,NULL,'ci','côte d''ivoire','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8261,2,NULL,'ck','cook islands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8262,2,NULL,'cl','chile','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8263,2,NULL,'cm','cameroon','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8264,2,NULL,'cn','china','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8265,2,NULL,'co','colombia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8266,2,NULL,'cp','clipperton island','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8267,2,NULL,'cr','costa rica','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8268,2,NULL,'cs','serbia and montenegro','1129420800',1160006400,NULL,NULL,NULL,NULL,NULL,'see rs for serbia or me for montenegro'); +INSERT INTO "iana_records" VALUES(8269,2,NULL,'cu','cuba','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8270,2,NULL,'cv','cape verde','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8271,2,NULL,'cx','christmas island','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8272,2,NULL,'cy','cyprus','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8273,2,NULL,'cz','czech republic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8274,2,NULL,'dd','german democratic republic','1129420800',657244800,'de',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8275,2,NULL,'de','germany','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8276,2,NULL,'dg','diego garcia','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8277,2,NULL,'dj','djibouti','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8278,2,NULL,'dk','denmark','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8279,2,NULL,'dm','dominica','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8280,2,NULL,'do','dominican republic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8281,2,NULL,'dz','algeria','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8282,2,NULL,'ea','ceuta, melilla','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8283,2,NULL,'ec','ecuador','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8284,2,NULL,'ee','estonia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8285,2,NULL,'eg','egypt','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8286,2,NULL,'eh','western sahara','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8287,2,NULL,'er','eritrea','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8288,2,NULL,'es','spain','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8289,2,NULL,'et','ethiopia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8290,2,NULL,'eu','european union','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8291,2,NULL,'fi','finland','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8292,2,NULL,'fj','fiji','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8293,2,NULL,'fk','falkland islands (malvinas)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8294,2,NULL,'fm','federated states of micronesia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8295,2,NULL,'fo','faroe islands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8296,2,NULL,'fr','france','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8297,2,NULL,'fx','metropolitan france','1129420800',868838400,'fr',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8298,2,NULL,'ga','gabon','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8299,2,NULL,'gb','united kingdom','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,'as of 2006-03-29 gb no longer includes the channel islands and isle of man; see gg, je, im'); +INSERT INTO "iana_records" VALUES(8300,2,NULL,'gd','grenada','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8301,2,NULL,'ge','georgia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8302,2,NULL,'gf','french guiana','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8303,2,NULL,'gg','guernsey','1143590400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8304,2,NULL,'gh','ghana','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8305,2,NULL,'gi','gibraltar','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8306,2,NULL,'gl','greenland','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8307,2,NULL,'gm','gambia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8308,2,NULL,'gn','guinea','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8309,2,NULL,'gp','guadeloupe','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8310,2,NULL,'gq','equatorial guinea','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8311,2,NULL,'gr','greece','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8312,2,NULL,'gs','south georgia and the south sandwich islands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8313,2,NULL,'gt','guatemala','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8314,2,NULL,'gu','guam','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8315,2,NULL,'gw','guinea-bissau','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8316,2,NULL,'gy','guyana','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8317,2,NULL,'hk','hong kong','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8318,2,NULL,'hm','heard island and mcdonald islands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8319,2,NULL,'hn','honduras','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8320,2,NULL,'hr','croatia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8321,2,NULL,'ht','haiti','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8322,2,NULL,'hu','hungary','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8323,2,NULL,'ic','canary islands','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8324,2,NULL,'id','indonesia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8325,2,NULL,'ie','ireland','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8326,2,NULL,'il','israel','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8327,2,NULL,'im','isle of man','1143590400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8328,2,NULL,'in','india','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8329,2,NULL,'io','british indian ocean territory','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8330,2,NULL,'iq','iraq','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8331,2,NULL,'ir','islamic republic of iran','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8332,2,NULL,'is','iceland','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8333,2,NULL,'it','italy','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8334,2,NULL,'je','jersey','1143590400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8335,2,NULL,'jm','jamaica','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8336,2,NULL,'jo','jordan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8337,2,NULL,'jp','japan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8338,2,NULL,'ke','kenya','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8339,2,NULL,'kg','kyrgyzstan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8340,2,NULL,'kh','cambodia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8341,2,NULL,'ki','kiribati','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8342,2,NULL,'km','comoros','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8343,2,NULL,'kn','saint kitts and nevis','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8344,2,NULL,'kp','democratic people''s republic of korea','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8345,2,NULL,'kr','republic of korea','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8346,2,NULL,'kw','kuwait','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8347,2,NULL,'ky','cayman islands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8348,2,NULL,'kz','kazakhstan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8349,2,NULL,'la','lao people''s democratic republic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8350,2,NULL,'lb','lebanon','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8351,2,NULL,'lc','saint lucia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8352,2,NULL,'li','liechtenstein','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8353,2,NULL,'lk','sri lanka','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8354,2,NULL,'lr','liberia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8355,2,NULL,'ls','lesotho','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8356,2,NULL,'lt','lithuania','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8357,2,NULL,'lu','luxembourg','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8358,2,NULL,'lv','latvia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8359,2,NULL,'ly','libyan arab jamahiriya','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8360,2,NULL,'ma','morocco','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8361,2,NULL,'mc','monaco','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8362,2,NULL,'md','moldova','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8363,2,NULL,'me','montenegro','1160006400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8364,2,NULL,'mf','saint martin','1193961600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8365,2,NULL,'mg','madagascar','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8366,2,NULL,'mh','marshall islands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8367,2,NULL,'mk','the former yugoslav republic of macedonia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8368,2,NULL,'ml','mali','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8369,2,NULL,'mm','myanmar','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8370,2,NULL,'mn','mongolia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8371,2,NULL,'mo','macao','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8372,2,NULL,'mp','northern mariana islands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8373,2,NULL,'mq','martinique','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8374,2,NULL,'mr','mauritania','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8375,2,NULL,'ms','montserrat','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8376,2,NULL,'mt','malta','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8377,2,NULL,'mu','mauritius','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8378,2,NULL,'mv','maldives','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8379,2,NULL,'mw','malawi','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8380,2,NULL,'mx','mexico','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8381,2,NULL,'my','malaysia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8382,2,NULL,'mz','mozambique','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8383,2,NULL,'na','namibia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8384,2,NULL,'nc','new caledonia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8385,2,NULL,'ne','niger','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8386,2,NULL,'nf','norfolk island','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8387,2,NULL,'ng','nigeria','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8388,2,NULL,'ni','nicaragua','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8389,2,NULL,'nl','netherlands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8390,2,NULL,'no','norway','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8391,2,NULL,'np','nepal','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8392,2,NULL,'nr','nauru','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8393,2,NULL,'nt','neutral zone','1129420800',742435200,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8394,2,NULL,'nu','niue','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8395,2,NULL,'nz','new zealand','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8396,2,NULL,'om','oman','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8397,2,NULL,'pa','panama','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8398,2,NULL,'pe','peru','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8399,2,NULL,'pf','french polynesia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8400,2,NULL,'pg','papua new guinea','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8401,2,NULL,'ph','philippines','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8402,2,NULL,'pk','pakistan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8403,2,NULL,'pl','poland','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8404,2,NULL,'pm','saint pierre and miquelon','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8405,2,NULL,'pn','pitcairn','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8406,2,NULL,'pr','puerto rico','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8407,2,NULL,'ps','occupied palestinian territory','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8408,2,NULL,'pt','portugal','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8409,2,NULL,'pw','palau','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8410,2,NULL,'py','paraguay','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8411,2,NULL,'qa','qatar','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8412,2,NULL,'qm..qz','private use','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8413,2,NULL,'re','réunion','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8414,2,NULL,'ro','romania','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8415,2,NULL,'rs','serbia','1160006400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8416,2,NULL,'ru','russian federation','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8417,2,NULL,'rw','rwanda','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8418,2,NULL,'sa','saudi arabia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8419,2,NULL,'sb','solomon islands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8420,2,NULL,'sc','seychelles','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8421,2,NULL,'sd','sudan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8422,2,NULL,'se','sweden','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8423,2,NULL,'sg','singapore','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8424,2,NULL,'sh','saint helena, ascension and tristan da cunha','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8425,2,NULL,'si','slovenia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8426,2,NULL,'sj','svalbard and jan mayen','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8427,2,NULL,'sk','slovakia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8428,2,NULL,'sl','sierra leone','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8429,2,NULL,'sm','san marino','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8430,2,NULL,'sn','senegal','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8431,2,NULL,'so','somalia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8432,2,NULL,'sr','suriname','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8433,2,NULL,'st','sao tome and principe','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8434,2,NULL,'su','union of soviet socialist republics','1129420800',715132800,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8435,2,NULL,'sv','el salvador','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8436,2,NULL,'sy','syrian arab republic','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8437,2,NULL,'sz','swaziland','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8438,2,NULL,'ta','tristan da cunha','1248825600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8439,2,NULL,'tc','turks and caicos islands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8440,2,NULL,'td','chad','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8441,2,NULL,'tf','french southern territories','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8442,2,NULL,'tg','togo','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8443,2,NULL,'th','thailand','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8444,2,NULL,'tj','tajikistan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8445,2,NULL,'tk','tokelau','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8446,2,NULL,'tl','timor-leste','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8447,2,NULL,'tm','turkmenistan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8448,2,NULL,'tn','tunisia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8449,2,NULL,'to','tonga','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8450,2,NULL,'tp','east timor','1129420800',1021852800,'tl',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8451,2,NULL,'tr','turkey','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8452,2,NULL,'tt','trinidad and tobago','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8453,2,NULL,'tv','tuvalu','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8454,2,NULL,'tw','taiwan, province of china','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8455,2,NULL,'tz','united republic of tanzania','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8456,2,NULL,'ua','ukraine','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8457,2,NULL,'ug','uganda','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8458,2,NULL,'um','united states minor outlying islands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8459,2,NULL,'us','united states','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8460,2,NULL,'uy','uruguay','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8461,2,NULL,'uz','uzbekistan','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8462,2,NULL,'va','holy see (vatican city state)','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8463,2,NULL,'vc','saint vincent and the grenadines','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8464,2,NULL,'ve','venezuela','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8465,2,NULL,'vg','british virgin islands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8466,2,NULL,'vi','u.s. virgin islands','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8467,2,NULL,'vn','viet nam','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8468,2,NULL,'vu','vanuatu','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8469,2,NULL,'wf','wallis and futuna','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8470,2,NULL,'ws','samoa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8471,2,NULL,'xa..xz','private use','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8472,2,NULL,'yd','democratic yemen','1129420800',650592000,'ye',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8473,2,NULL,'ye','yemen','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8474,2,NULL,'yt','mayotte','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8475,2,NULL,'yu','yugoslavia','1129420800',1058918400,NULL,NULL,NULL,NULL,NULL,'see ba, hr, me, mk, rs, or si'); +INSERT INTO "iana_records" VALUES(8476,2,NULL,'za','south africa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8477,2,NULL,'zm','zambia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8478,2,NULL,'zr','zaire','1129420800',868838400,'cd',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8479,2,NULL,'zw','zimbabwe','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8480,2,NULL,'zz','private use','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8481,2,NULL,'001','world','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8482,2,NULL,'002','africa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8483,2,NULL,'005','south america','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8484,2,NULL,'009','oceania','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8485,2,NULL,'011','western africa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8486,2,NULL,'013','central america','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8487,2,NULL,'014','eastern africa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8488,2,NULL,'015','northern africa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8489,2,NULL,'017','middle africa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8490,2,NULL,'018','southern africa','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8491,2,NULL,'019','americas','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8492,2,NULL,'021','northern america','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8493,2,NULL,'029','caribbean','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8494,2,NULL,'030','eastern asia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8495,2,NULL,'034','southern asia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8496,2,NULL,'035','south-eastern asia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8497,2,NULL,'039','southern europe','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8498,2,NULL,'053','australia and new zealand','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8499,2,NULL,'054','melanesia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8500,2,NULL,'057','micronesia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8501,2,NULL,'061','polynesia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8502,2,NULL,'142','asia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8503,2,NULL,'143','central asia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8504,2,NULL,'145','western asia','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8505,2,NULL,'150','europe','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8506,2,NULL,'151','eastern europe','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8507,2,NULL,'154','northern europe','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8508,2,NULL,'155','western europe','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8509,3,NULL,'419','latin america and the caribbean','1129420800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8510,3,NULL,'1606nict','late middle french (to 1606)','1174348800',NULL,NULL,'frm',NULL,NULL,NULL,'16th century french as in jean nicot, "thresor de la langue francoyse", 1606, but also including some french similar to that of rabelais'); +INSERT INTO "iana_records" VALUES(8511,3,NULL,'1694acad','early modern french','1174348800',NULL,NULL,'fr',NULL,NULL,NULL,'17th century french, as catalogued in the "dictionnaire de l''académie françoise", 4eme ed. 1694; frequently includes elements of middle french, as this is a transitional period'); +INSERT INTO "iana_records" VALUES(8512,3,NULL,'1901','traditional german orthography','1129420800',NULL,NULL,'de',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8513,3,NULL,'1959acad','"academic" ("governmental") variant of belarusian as codified in 1959','1222732800',NULL,NULL,'be',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8514,3,NULL,'1994','standardized resian orthography','1185580800',NULL,NULL,'sl-rozaj',NULL,NULL,NULL,'for standardized resian an orthography was published in 1994.'); +INSERT INTO "iana_records" VALUES(8514,3,NULL,'1994','standardized resian orthography','1185580800',NULL,NULL,'sl-rozaj-biske',NULL,NULL,NULL,'for standardized resian an orthography was published in 1994.'); +INSERT INTO "iana_records" VALUES(8514,3,NULL,'1994','standardized resian orthography','1185580800',NULL,NULL,'sl-rozaj-njiva',NULL,NULL,NULL,'for standardized resian an orthography was published in 1994.'); +INSERT INTO "iana_records" VALUES(8514,3,NULL,'1994','standardized resian orthography','1185580800',NULL,NULL,'sl-rozaj-osojs',NULL,NULL,NULL,'for standardized resian an orthography was published in 1994.'); +INSERT INTO "iana_records" VALUES(8514,3,NULL,'1994','standardized resian orthography','1185580800',NULL,NULL,'sl-rozaj-solba',NULL,NULL,NULL,'for standardized resian an orthography was published in 1994.'); +INSERT INTO "iana_records" VALUES(8515,3,NULL,'1996','german orthography of 1996','1129420800',NULL,NULL,'de',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8516,3,NULL,'alalc97','ala-lc romanization, 1997 edition','1260316800',NULL,NULL,NULL,NULL,NULL,NULL,'romanizations recommended by the american library association and the library of congress, in "ala-lc romanization tables: transliteration schemes for non-roman scripts" (1997), isbn 978-0-8444-0940-5.'); +INSERT INTO "iana_records" VALUES(8517,3,NULL,'aluku','aluku dialect','1252108800',NULL,NULL,'djk',NULL,NULL,NULL,'aluku dialect of the "busi nenge tongo" english-based creole continuum in eastern suriname and western french guiana'); +INSERT INTO "iana_records" VALUES(8517,3,NULL,'aluku','boni dialect','1252108800',NULL,NULL,'djk',NULL,NULL,NULL,'aluku dialect of the "busi nenge tongo" english-based creole continuum in eastern suriname and western french guiana'); +INSERT INTO "iana_records" VALUES(8518,3,NULL,'arevela','eastern armenian','1158537600',NULL,NULL,'hy',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8519,3,NULL,'arevmda','western armenian','1158537600',NULL,NULL,'hy',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8520,3,NULL,'baku1926','unified turkic latin alphabet (historical)','1176854400',NULL,NULL,'az',NULL,NULL,NULL,'denotes alphabet used in turkic republics/regions of the former ussr in late 1920s, and throughout 1930s, which aspired to represent equivalent phonemes in a unified fashion. also known as: new turkic alphabet; birlәşdirilmiş jeni tyrk Әlifbasь (birlesdirilmis jeni tyrk elifbasi); jaŋalif (janalif).'); +INSERT INTO "iana_records" VALUES(8520,3,NULL,'baku1926','unified turkic latin alphabet (historical)','1176854400',NULL,NULL,'ba',NULL,NULL,NULL,'denotes alphabet used in turkic republics/regions of the former ussr in late 1920s, and throughout 1930s, which aspired to represent equivalent phonemes in a unified fashion. also known as: new turkic alphabet; birlәşdirilmiş jeni tyrk Әlifbasь (birlesdirilmis jeni tyrk elifbasi); jaŋalif (janalif).'); +INSERT INTO "iana_records" VALUES(8520,3,NULL,'baku1926','unified turkic latin alphabet (historical)','1176854400',NULL,NULL,'crh',NULL,NULL,NULL,'denotes alphabet used in turkic republics/regions of the former ussr in late 1920s, and throughout 1930s, which aspired to represent equivalent phonemes in a unified fashion. also known as: new turkic alphabet; birlәşdirilmiş jeni tyrk Әlifbasь (birlesdirilmis jeni tyrk elifbasi); jaŋalif (janalif).'); +INSERT INTO "iana_records" VALUES(8520,3,NULL,'baku1926','unified turkic latin alphabet (historical)','1176854400',NULL,NULL,'kk',NULL,NULL,NULL,'denotes alphabet used in turkic republics/regions of the former ussr in late 1920s, and throughout 1930s, which aspired to represent equivalent phonemes in a unified fashion. also known as: new turkic alphabet; birlәşdirilmiş jeni tyrk Әlifbasь (birlesdirilmis jeni tyrk elifbasi); jaŋalif (janalif).'); +INSERT INTO "iana_records" VALUES(8520,3,NULL,'baku1926','unified turkic latin alphabet (historical)','1176854400',NULL,NULL,'krc',NULL,NULL,NULL,'denotes alphabet used in turkic republics/regions of the former ussr in late 1920s, and throughout 1930s, which aspired to represent equivalent phonemes in a unified fashion. also known as: new turkic alphabet; birlәşdirilmiş jeni tyrk Әlifbasь (birlesdirilmis jeni tyrk elifbasi); jaŋalif (janalif).'); +INSERT INTO "iana_records" VALUES(8520,3,NULL,'baku1926','unified turkic latin alphabet (historical)','1176854400',NULL,NULL,'ky',NULL,NULL,NULL,'denotes alphabet used in turkic republics/regions of the former ussr in late 1920s, and throughout 1930s, which aspired to represent equivalent phonemes in a unified fashion. also known as: new turkic alphabet; birlәşdirilmiş jeni tyrk Әlifbasь (birlesdirilmis jeni tyrk elifbasi); jaŋalif (janalif).'); +INSERT INTO "iana_records" VALUES(8520,3,NULL,'baku1926','unified turkic latin alphabet (historical)','1176854400',NULL,NULL,'sah',NULL,NULL,NULL,'denotes alphabet used in turkic republics/regions of the former ussr in late 1920s, and throughout 1930s, which aspired to represent equivalent phonemes in a unified fashion. also known as: new turkic alphabet; birlәşdirilmiş jeni tyrk Әlifbasь (birlesdirilmis jeni tyrk elifbasi); jaŋalif (janalif).'); +INSERT INTO "iana_records" VALUES(8520,3,NULL,'baku1926','unified turkic latin alphabet (historical)','1176854400',NULL,NULL,'tk',NULL,NULL,NULL,'denotes alphabet used in turkic republics/regions of the former ussr in late 1920s, and throughout 1930s, which aspired to represent equivalent phonemes in a unified fashion. also known as: new turkic alphabet; birlәşdirilmiş jeni tyrk Әlifbasь (birlesdirilmis jeni tyrk elifbasi); jaŋalif (janalif).'); +INSERT INTO "iana_records" VALUES(8520,3,NULL,'baku1926','unified turkic latin alphabet (historical)','1176854400',NULL,NULL,'tt',NULL,NULL,NULL,'denotes alphabet used in turkic republics/regions of the former ussr in late 1920s, and throughout 1930s, which aspired to represent equivalent phonemes in a unified fashion. also known as: new turkic alphabet; birlәşdirilmiş jeni tyrk Әlifbasь (birlesdirilmis jeni tyrk elifbasi); jaŋalif (janalif).'); +INSERT INTO "iana_records" VALUES(8520,3,NULL,'baku1926','unified turkic latin alphabet (historical)','1176854400',NULL,NULL,'uz',NULL,NULL,NULL,'denotes alphabet used in turkic republics/regions of the former ussr in late 1920s, and throughout 1930s, which aspired to represent equivalent phonemes in a unified fashion. also known as: new turkic alphabet; birlәşdirilmiş jeni tyrk Әlifbasь (birlesdirilmis jeni tyrk elifbasi); jaŋalif (janalif).'); +INSERT INTO "iana_records" VALUES(8521,3,NULL,'biscayan','biscayan dialect of basque','1271116800',NULL,NULL,'eu',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8522,3,NULL,'biske','the bila dialect of resian','1183593600',NULL,NULL,'sl-rozaj',NULL,NULL,NULL,'the dialect of san giorgio/bila is one of the four major local dialects of resian'); +INSERT INTO "iana_records" VALUES(8522,3,NULL,'biske','the san giorgio dialect of resian','1183593600',NULL,NULL,'sl-rozaj',NULL,NULL,NULL,'the dialect of san giorgio/bila is one of the four major local dialects of resian'); +INSERT INTO "iana_records" VALUES(8523,3,NULL,'boont','boontling','1158537600',NULL,NULL,'en',NULL,NULL,NULL,'jargon embedded in american english'); +INSERT INTO "iana_records" VALUES(8524,3,NULL,'fonipa','international phonetic alphabet','1165795200',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8525,3,NULL,'fonupa','uralic phonetic alphabet','1165795200',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8526,3,NULL,'hepburn','hepburn romanization','1254355200',NULL,NULL,'ja-latn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8527,3,NULL,'heploc','hepburn romanization, library of congress method','1254355200',1265500800,'alalc97','ja-latn-hepburn',NULL,NULL,NULL,'preferred tag is ja-latn-alalc97'); +INSERT INTO "iana_records" VALUES(8528,3,NULL,'hognorsk','norwegian in høgnorsk (high norwegian) orthography','1262390400',NULL,NULL,'nn',NULL,NULL,NULL,'norwegian following ivar aasen''s orthographical principles, including modern usage.'); +INSERT INTO "iana_records" VALUES(8529,3,NULL,'jauer','jauer dialect of romansh','1277769600',NULL,NULL,'rm',NULL,NULL,NULL,'the spoken dialect of the val müstair, which has no written standard.'); +INSERT INTO "iana_records" VALUES(8530,3,NULL,'kkcor','common cornish orthography of revived cornish','1223942400',NULL,NULL,'kw',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8531,3,NULL,'lipaw','the lipovaz dialect of resian','1186790400',NULL,NULL,'sl-rozaj',NULL,NULL,NULL,'the dialect of lipovaz/lipovec is one of the minor local dialects of resian'); +INSERT INTO "iana_records" VALUES(8531,3,NULL,'lipaw','the lipovec dialect of resian','1186790400',NULL,NULL,'sl-rozaj',NULL,NULL,NULL,'the dialect of lipovaz/lipovec is one of the minor local dialects of resian'); +INSERT INTO "iana_records" VALUES(8532,3,NULL,'monoton','monotonic greek','1165795200',NULL,NULL,'el',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8533,3,NULL,'ndyuka','aukan dialect','1252108800',NULL,NULL,'djk',NULL,NULL,NULL,'ndyuka dialect of the "busi nenge tongo" english-based creole continuum in eastern suriname and western french guiana'); +INSERT INTO "iana_records" VALUES(8533,3,NULL,'ndyuka','ndyuka dialect','1252108800',NULL,NULL,'djk',NULL,NULL,NULL,'ndyuka dialect of the "busi nenge tongo" english-based creole continuum in eastern suriname and western french guiana'); +INSERT INTO "iana_records" VALUES(8534,3,NULL,'nedis','nadiza dialect','1129420800',NULL,NULL,'sl',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8534,3,NULL,'nedis','natisone dialect','1129420800',NULL,NULL,'sl',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8535,3,NULL,'njiva','the gniva dialect of resian','1183593600',NULL,NULL,'sl-rozaj',NULL,NULL,NULL,'the dialect of gniva/njiva is one of the four major local dialects of resian'); +INSERT INTO "iana_records" VALUES(8535,3,NULL,'njiva','the njiva dialect of resian','1183593600',NULL,NULL,'sl-rozaj',NULL,NULL,NULL,'the dialect of gniva/njiva is one of the four major local dialects of resian'); +INSERT INTO "iana_records" VALUES(8536,3,NULL,'osojs','the oseacco dialect of resian','1183593600',NULL,NULL,'sl-rozaj',NULL,NULL,NULL,'the dialect of oseacco/osojane is one of the four major local dialects of resian'); +INSERT INTO "iana_records" VALUES(8536,3,NULL,'osojs','the osojane dialect of resian','1183593600',NULL,NULL,'sl-rozaj',NULL,NULL,NULL,'the dialect of oseacco/osojane is one of the four major local dialects of resian'); +INSERT INTO "iana_records" VALUES(8537,3,NULL,'pamaka','pamaka dialect','1252108800',NULL,NULL,'djk',NULL,NULL,NULL,'pamaka dialect of the "busi nenge tongo" english-based creole continuum in eastern suriname and western french guiana'); +INSERT INTO "iana_records" VALUES(8538,3,NULL,'pinyin','pinyin romanization','1223942400',NULL,NULL,'bo-latn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8538,3,NULL,'pinyin','pinyin romanization','1223942400',NULL,NULL,'zh-latn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8539,3,NULL,'polyton','polytonic greek','1165795200',NULL,NULL,'el',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8540,3,NULL,'puter','puter idiom of romansh','1277769600',NULL,NULL,'rm',NULL,NULL,NULL,'puter is one of the five traditional written standards or "idioms" of the romansh language.'); +INSERT INTO "iana_records" VALUES(8541,3,NULL,'rozaj','resian','1129420800',NULL,NULL,'sl',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8541,3,NULL,'rozaj','resianic','1129420800',NULL,NULL,'sl',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8541,3,NULL,'rozaj','rezijan','1129420800',NULL,NULL,'sl',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8542,3,NULL,'rumgr','rumantsch grischun','1277769600',NULL,NULL,'rm',NULL,NULL,NULL,'supraregional romansh written standard'); +INSERT INTO "iana_records" VALUES(8543,3,NULL,'scotland','scottish standard english','1188518400',NULL,NULL,'en',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8544,3,NULL,'scouse','scouse','1158537600',NULL,NULL,'en',NULL,NULL,NULL,'english liverpudlian dialect known as ''scouse'''); +INSERT INTO "iana_records" VALUES(8545,3,NULL,'solba','the solbica dialect of resian','1183593600',NULL,NULL,'sl-rozaj',NULL,NULL,NULL,'the dialect of stolvizza/solbica is one of the four major local dialects of resian'); +INSERT INTO "iana_records" VALUES(8545,3,NULL,'solba','the stolvizza dialect of resian','1183593600',NULL,NULL,'sl-rozaj',NULL,NULL,NULL,'the dialect of stolvizza/solbica is one of the four major local dialects of resian'); +INSERT INTO "iana_records" VALUES(8546,3,NULL,'surmiran','surmiran idiom of romansh','1277769600',NULL,NULL,'rm',NULL,NULL,NULL,'surmiran is one of the five traditional written standards or "idioms" of the romansh language.'); +INSERT INTO "iana_records" VALUES(8547,3,NULL,'sursilv','sursilvan idiom of romansh','1277769600',NULL,NULL,'rm',NULL,NULL,NULL,'sursilvan is one of the five traditional written standards or "idioms" of the romansh language.'); +INSERT INTO "iana_records" VALUES(8548,3,NULL,'sutsilv','sutsilvan idiom of romansh','1277769600',NULL,NULL,'rm',NULL,NULL,NULL,'sutsilvan is one of the five traditional written standards or "idioms" of the romansh language.'); +INSERT INTO "iana_records" VALUES(8549,3,NULL,'tarask','belarusian in taraskievica orthography','1177632000',NULL,NULL,'be',NULL,NULL,NULL,'the subtag represents branislau taraskievic''s belarusian orthography as published in "bielaruski klasycny pravapis" by juras buslakou, vincuk viacorka, zmicier sanko, and zmicier sauka (vilnia- miensk 2005).'); +INSERT INTO "iana_records" VALUES(8550,3,NULL,'uccor','unified cornish orthography of revived cornish','1223942400',NULL,NULL,'kw',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8551,3,NULL,'ucrcor','unified cornish revised orthography of revived cornish','1223942400',NULL,NULL,'kw',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8552,3,NULL,'ulster','ulster dialect of scots','1270857600',NULL,NULL,'sco',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8553,3,NULL,'valencia','valencian','1173139200',NULL,NULL,'ca',NULL,NULL,NULL,'variety spoken in the "comunidad valenciana" region of spain, where it is co-official with spanish.'); +INSERT INTO "iana_records" VALUES(8554,3,NULL,'vallader','vallader idiom of romansh','1277769600',NULL,NULL,'rm',NULL,NULL,NULL,'vallader is one of the five traditional written standards or "idioms" of the romansh language.'); +INSERT INTO "iana_records" VALUES(8555,4,NULL,'wadegile','wade-giles romanization','1222992000',NULL,NULL,'zh-latn',NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8556,4,'art-lojban',NULL,'lojban','1005436800',1062460800,'jbo',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8557,4,'cel-gaulish',NULL,'gaulish','990748800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8558,4,'en-gb-oed',NULL,'english, oxford english dictionary spelling','1057708800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8559,4,'i-ami',NULL,'amis','927590400',1248825600,'ami',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8560,4,'i-bnn',NULL,'bunun','927590400',1248825600,'bnn',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8561,4,'i-default',NULL,'default language','889488000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8562,4,'i-enochian',NULL,'enochian','1025654400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8563,4,'i-hak',NULL,'hakka','917740800',947462400,'hak',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8564,4,'i-klingon',NULL,'klingon','927676800',1077580800,'tlh',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8565,4,'i-lux',NULL,'luxembourgish','874627200',905299200,'lb',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8566,4,'i-mingo',NULL,'mingo','874627200',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8567,4,'i-navajo',NULL,'navajo','874627200',950832000,'nv',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8568,4,'i-pwn',NULL,'paiwan','927590400',1248825600,'pwn',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8569,4,'i-tao',NULL,'tao','927590400',1248825600,'tao',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8570,4,'i-tay',NULL,'tayal','927590400',1248825600,'tay',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8571,4,'i-tsu',NULL,'tsou','927590400',1248825600,'tsu',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8572,4,'no-bok',NULL,'norwegian bokmal','809136000',950832000,'nb',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8573,4,'no-nyn',NULL,'norwegian nynorsk','809136000',950832000,'nn',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8574,4,'sgn-be-fr',NULL,'belgian-french sign language','1005436800',1248825600,'sfb',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8575,4,'sgn-be-nl',NULL,'belgian-flemish sign language','1005436800',1248825600,'vgt',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8576,4,'sgn-ch-de',NULL,'swiss german sign language','1005436800',1248825600,'sgg',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8577,4,'zh-guoyu',NULL,'mandarin or standard chinese','945475200',1121385600,'cmn',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8578,4,'zh-hakka',NULL,'hakka','945475200',1248825600,'hak',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8579,4,'zh-min',NULL,'min, fuzhou, hokkien, amoy, or taiwanese','945475200',1248825600,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8580,4,'zh-min-nan',NULL,'minnan, hokkien, amoy, taiwanese, southern min, southern fujian, hoklo, southern fukien, ho-lo','985564800',1248825600,'nan',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8581,5,'zh-xiang',NULL,'xiang or hunanese','945475200',1248825600,'hsn',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8582,5,'az-arab',NULL,'azerbaijani in arabic script','1054252800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8583,5,'az-cyrl',NULL,'azerbaijani in cyrillic script','1054252800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8584,5,'az-latn',NULL,'azerbaijani in latin script','1054252800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8585,5,'be-latn',NULL,'belarusian in latin script','1104969600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8586,5,'bs-cyrl',NULL,'bosnian in cyrillic script','1108598400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8587,5,'bs-latn',NULL,'bosnian in latin script','1108598400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8588,5,'de-1901',NULL,'german, traditional orthography','995328000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8589,5,'de-1996',NULL,'german, orthography of 1996','995328000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8590,5,'de-at-1901',NULL,'german, austrian variant, traditional orthography','995328000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8591,5,'de-at-1996',NULL,'german, austrian variant, orthography of 1996','995328000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8592,5,'de-ch-1901',NULL,'german, swiss variant, traditional orthography','995328000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8593,5,'de-ch-1996',NULL,'german, swiss variant, orthography of 1996','995328000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8594,5,'de-de-1901',NULL,'german, german variant, traditional orthography','995328000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8595,5,'de-de-1996',NULL,'german, german variant, orthography of 1996','995328000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8596,5,'en-boont',NULL,'boontling','1045180800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8597,5,'en-scouse',NULL,'scouse','959212800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8598,5,'es-419',NULL,'latin american spanish','1121385600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8599,5,'iu-cans',NULL,'inuktitut in canadian aboriginal syllabic script','1108598400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8600,5,'iu-latn',NULL,'inuktitut in latin script','1108598400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8601,5,'mn-cyrl',NULL,'mongolian in cyrillic script','1108598400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8602,5,'mn-mong',NULL,'mongolian in mongolian script','1108598400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8603,5,'sgn-br',NULL,'brazilian sign language','1005436800',1248825600,'bzs',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8604,5,'sgn-co',NULL,'colombian sign language','1005436800',1248825600,'csn',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8605,5,'sgn-de',NULL,'german sign language','1005436800',1248825600,'gsg',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8606,5,'sgn-dk',NULL,'danish sign language','1005436800',1248825600,'dsl',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8607,5,'sgn-es',NULL,'spanish sign language','1005436800',1248825600,'ssp',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8608,5,'sgn-fr',NULL,'french sign language','1005436800',1248825600,'fsl',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8609,5,'sgn-gb',NULL,'british sign language','983491200',1248825600,'bfi',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8610,5,'sgn-gr',NULL,'greek sign language','1005436800',1248825600,'gss',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8611,5,'sgn-ie',NULL,'irish sign language','983491200',1248825600,'isg',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8612,5,'sgn-it',NULL,'italian sign language','1005436800',1248825600,'ise',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8613,5,'sgn-jp',NULL,'japanese sign language','1005436800',1248825600,'jsl',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8614,5,'sgn-mx',NULL,'mexican sign language','1005436800',1248825600,'mfs',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8615,5,'sgn-ni',NULL,'nicaraguan sign language','983491200',1248825600,'ncs',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8616,5,'sgn-nl',NULL,'dutch sign language','1005436800',1248825600,'dse',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8617,5,'sgn-no',NULL,'norwegian sign language','1005436800',1248825600,'nsl',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8618,5,'sgn-pt',NULL,'portuguese sign language','1005436800',1248825600,'psr',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8619,5,'sgn-se',NULL,'swedish sign language','1005436800',1248825600,'swl',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8620,5,'sgn-us',NULL,'american sign language','983491200',1248825600,'ase',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8621,5,'sgn-za',NULL,'south african sign language','1005436800',1248825600,'sfs',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8622,5,'sl-nedis',NULL,'natisone dialect, nadiza dialect','1086048000',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8623,5,'sl-rozaj',NULL,'resian, resianic, rezijan','1065657600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8624,5,'sr-cyrl',NULL,'serbian in cyrillic script','1054252800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8625,5,'sr-latn',NULL,'serbian in latin script','1054252800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8626,5,'tg-arab',NULL,'tajik in arabic script','1108598400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8627,5,'tg-cyrl',NULL,'tajik in cyrillic script','1108598400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8628,5,'uz-cyrl',NULL,'uzbek in cyrillic script','1054252800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8629,5,'uz-latn',NULL,'uzbek in latin script','1054252800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8630,5,'yi-latn',NULL,'yiddish, in latin script','1041897600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8631,5,'zh-cmn',NULL,'mandarin chinese','1121385600',1248825600,'cmn',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8632,5,'zh-cmn-hans',NULL,'mandarin chinese (simplified)','1121385600',1248825600,'cmn-hans',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8633,5,'zh-cmn-hant',NULL,'mandarin chinese (traditional)','1121385600',1248825600,'cmn-hant',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8634,5,'zh-gan',NULL,'kan or gan','945475200',1248825600,'gan',NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8635,5,'zh-hans',NULL,'simplified chinese','1054252800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8636,5,'zh-hans-cn',NULL,'prc mainland chinese in simplified script','1113350400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8637,5,'zh-hans-hk',NULL,'hong kong chinese in simplified script','1113177600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8638,5,'zh-hans-mo',NULL,'macao chinese in simplified script','1113177600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8639,5,'zh-hans-sg',NULL,'singapore chinese in simplified script','1113177600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8640,5,'zh-hans-tw',NULL,'taiwan chinese in simplified script','1113177600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8641,5,'zh-hant',NULL,'traditional chinese','1054252800',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8642,5,'zh-hant-cn',NULL,'prc mainland chinese in traditional script','1113350400',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8643,5,'zh-hant-hk',NULL,'hong kong chinese in traditional script','1113177600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8644,5,'zh-hant-mo',NULL,'macao chinese in traditional script','1113177600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8645,5,'zh-hant-sg',NULL,'singapore chinese in traditional script','1113177600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8646,5,'zh-hant-tw',NULL,'taiwan chinese in traditional script','1113177600',NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO "iana_records" VALUES(8647,5,'zh-wuu',NULL,'shanghaiese or wu','945475200',1248825600,'wuu',NULL,NULL,NULL,NULL,NULL); +COMMIT; ) diff --git a/modules/widget_dao/orm/orm_generator_wrt.h b/modules/widget_dao/orm/orm_generator_wrt.h new file mode 100644 index 0000000..09ac57e --- /dev/null +++ b/modules/widget_dao/orm/orm_generator_wrt.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ORM_GENERATOR_WRT_H +#define ORM_GENERATOR_WRT_H + +#define ORM_GENERATOR_DATABASE_NAME wrt_db_definitions +#include +#undef ORM_GENERATOR_DATABASE_NAME + +#endif diff --git a/modules/widget_dao/orm/version_db b/modules/widget_dao/orm/version_db new file mode 100644 index 0000000..7e20d8d --- /dev/null +++ b/modules/widget_dao/orm/version_db @@ -0,0 +1,5 @@ +SQL( + BEGIN TRANSACTION; + CREATE TABLE DB_CHECKSUM (version INT); + COMMIT; +) diff --git a/modules/widget_dao/orm/wrt_db b/modules/widget_dao/orm/wrt_db new file mode 100644 index 0000000..dc0f27f --- /dev/null +++ b/modules/widget_dao/orm/wrt_db @@ -0,0 +1,359 @@ +SQL( + PRAGMA foreign_keys = ON; + BEGIN TRANSACTION; +) +/*TODO: secure_by_default should be 0 by default*/ +CREATE_TABLE(GlobalProperties) + COLUMN_NOT_NULL(developer_mode, INT, DEFAULT 0) + COLUMN_NOT_NULL(parental_mode, INT, DEFAULT 0) + COLUMN(parental_allowed_age, INT, DEFAULT NULL) + COLUMN_NOT_NULL(secure_by_default, INT, DEFAULT 1) + COLUMN_NOT_NULL(home_network_data_usage, TINYINT, DEFAULT 1) + COLUMN_NOT_NULL(roaming_data_usage, TINYINT, DEFAULT 1) + COLUMN_NOT_NULL(compliance_mode, TINYINT, DEFAULT 0) + COLUMN_NOT_NULL(compliance_fake_imei, VARCHAR(256), DEFAULT '') + COLUMN_NOT_NULL(compliance_fake_meid, VARCHAR(256), DEFAULT '') +CREATE_TABLE_END() + +SQL( + INSERT INTO GlobalProperties DEFAULT VALUES; +) + +CREATE_TABLE(WidgetInfo) + COLUMN_NOT_NULL(app_id, INTEGER, PRIMARY KEY AUTOINCREMENT) + COLUMN(widget_type, INT, DEFAULT 1) + COLUMN(widget_id, VARCHAR(256), DEFAULT '') + COLUMN(widget_version, VARCHAR(256), DEFAULT '') + COLUMN(widget_width, INT, DEFAULT 0) + COLUMN(widget_height, INT, DEFAULT 0) + COLUMN(author_name, VARCHAR(256), DEFAULT '') + COLUMN(author_email, VARCHAR(256), DEFAULT '') + COLUMN(author_href, VARCHAR(256), DEFAULT '') + COLUMN(base_folder, VARCHAR(256), DEFAULT '') + COLUMN(webkit_plugins_required, TINYINT, DEFAULT 0) + COLUMN(security_domain, INT, DEFAULT 0) + COLUMN_NOT_NULL(child_protection,INT, DEFAULT 0) + COLUMN(recognized, INT, DEFAULT 0) + COLUMN(wac_signed, INT, DEFAULT 0) + COLUMN(distributor_signed, INT, DEFAULT 0) + COLUMN(min_version, VARCHAR(16), DEFAULT '1.0') + COLUMN_NOT_NULL(back_supported, TINYINT, DEFAULT 0) + COLUMN(access_network, TINYINT, DEFAULT 0) + COLUMN(defaultlocale, VARCHAR(256), DEFAULT 0) + COLUMN(pkgname, VARCHAR(256), DEFAULT 0) +CREATE_TABLE_END() + +CREATE_TABLE(WidgetCertificate) + COLUMN_NOT_NULL(app_id, INT,) + COLUMN_NOT_NULL(encoded_chain, VARCHAR(16000),) + TABLE_CONSTRAINTS( + FOREIGN KEY (app_id) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(WidgetWindowModes) + COLUMN_NOT_NULL(app_id, INT,) + COLUMN_NOT_NULL(window_mode, VARCHAR(256),) + TABLE_CONSTRAINTS( + FOREIGN KEY (app_id) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(WidgetUserAgentLocales) + COLUMN_NOT_NULL(id, INTEGER, PRIMARY KEY AUTOINCREMENT) + COLUMN_NOT_NULL(app_id, INT,) + COLUMN_NOT_NULL(language_tag, TEXT,) + TABLE_CONSTRAINTS( + FOREIGN KEY (app_id) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(LocalizedWidgetInfo) + COLUMN_NOT_NULL(app_id, INT,) + COLUMN_NOT_NULL(widget_locale, TEXT,) + COLUMN(widget_name, TEXT,) + COLUMN(widget_shortname, TEXT,) + COLUMN(widget_description, TEXT,) + COLUMN(widget_license, TEXT,) + COLUMN(widget_license_file, TEXT,) + COLUMN(widget_license_href, TEXT,) + + TABLE_CONSTRAINTS( + PRIMARY KEY (app_id, widget_locale), + FOREIGN KEY (app_id) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(WidgetExtendedInfo) + COLUMN_NOT_NULL(app_id, INTEGER, PRIMARY KEY) + COLUMN(last_update_time, BIGINT, DEFAULT 0) + COLUMN(install_time, BIGINT, DEFAULT 0) + COLUMN(option_state, INT, DEFAULT 0) + COLUMN(share_href, VARCHAR(256), DEFAULT '') + COLUMN(signature_type, INT, DEFAULT 0) + COLUMN(factory_widget, INT, DEFAULT 0) + COLUMN(updated, INT, DEFAULT 0) + COLUMN(update_policy, INT, DEFAULT 0) + COLUMN_NOT_NULL(test_widget, INT, CHECK(test_widget between 0 and 1) DEFAULT 0) + TABLE_CONSTRAINTS( + FOREIGN KEY(app_id) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(WidgetPreference) + COLUMN_NOT_NULL(app_id, INT,) + COLUMN_NOT_NULL(key_name, VARCHAR(256),) + COLUMN(key_value, VARCHAR(8000), DEFAULT '') + COLUMN(readonly, INT, DEFAULT 0) + + TABLE_CONSTRAINTS( + PRIMARY KEY(app_id, key_name), + FOREIGN KEY(app_id) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(WidgetFeature) + COLUMN_NOT_NULL(widget_feature_id, INTEGER, primary key autoincrement) + COLUMN_NOT_NULL(app_id, INT,) + COLUMN_NOT_NULL(name, VARCHAR(256),) + COLUMN_NOT_NULL(required, INT,) + TABLE_CONSTRAINTS( + FOREIGN KEY (app_id) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(FeatureParam) + COLUMN_NOT_NULL(widget_feature_id, INTEGER,) + COLUMN_NOT_NULL(name, TEXT,) + COLUMN_NOT_NULL(value, TEXT,) + + TABLE_CONSTRAINTS( + FOREIGN KEY (widget_feature_id) REFERENCES WidgetFeature (widget_feature_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(WidgetIcon) + COLUMN_NOT_NULL(icon_id, INTEGER, primary key autoincrement) + COLUMN_NOT_NULL(app_id, INT,) + COLUMN_NOT_NULL(icon_src, VARCHAR(256),) + COLUMN(icon_width, INT, DEFAULT 0) + COLUMN(icon_height, INT, DEFAULT 0) + TABLE_CONSTRAINTS( + FOREIGN KEY(app_id) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(WidgetLocalizedIcon) + COLUMN_NOT_NULL(app_id, INT,) /* TODO key duplicated for efficiency - ORM doesn't support JOIN */ + COLUMN_NOT_NULL(icon_id, INTEGER,) + COLUMN_NOT_NULL(widget_locale, TEXT,) + TABLE_CONSTRAINTS( + FOREIGN KEY(icon_id) REFERENCES WidgetIcon (icon_id) ON DELETE CASCADE, + PRIMARY KEY(icon_id, widget_locale) + ) +CREATE_TABLE_END() + +CREATE_TABLE(WidgetStartFile) + COLUMN_NOT_NULL(start_file_id, INTEGER, primary key autoincrement) + COLUMN_NOT_NULL(app_id, INT,) + COLUMN_NOT_NULL(src, VARCHAR(256),) + TABLE_CONSTRAINTS( + FOREIGN KEY(app_id) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(WidgetLocalizedStartFile) + COLUMN_NOT_NULL(app_id, INT,) /* TODO key duplicated for efficiency - ORM doesn't support JOIN */ + COLUMN_NOT_NULL(start_file_id, INTEGER,) + COLUMN_NOT_NULL(widget_locale, TEXT,) + COLUMN_NOT_NULL(type, TEXT,) + COLUMN_NOT_NULL(encoding, TEXT,) + TABLE_CONSTRAINTS( + FOREIGN KEY(start_file_id) REFERENCES WidgetStartFile (start_file_id) ON DELETE CASCADE, + PRIMARY KEY(start_file_id, widget_locale) + ) +CREATE_TABLE_END() + +CREATE_TABLE(WidgetAccessHost) + COLUMN_NOT_NULL(app_id, INT,) + COLUMN_NOT_NULL(host, VARCHAR(256),) + + TABLE_CONSTRAINTS( + PRIMARY KEY(app_id, host) + FOREIGN KEY (app_id) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE) +CREATE_TABLE_END() + +CREATE_TABLE(WidgetCertificateFingerprint) + COLUMN_NOT_NULL(app_id, INT,) + COLUMN_NOT_NULL(owner, INT,) + COLUMN_NOT_NULL(chainid, INT,) + COLUMN_NOT_NULL(type, INT,) + COLUMN(md5_fingerprint, VARCHAR(64),) + COLUMN(sha1_fingerprint, VARCHAR(64),) + COLUMN(common_name, VARCHAR(64),) + + TABLE_CONSTRAINTS( + PRIMARY KEY(app_id, chainid, owner, type) + FOREIGN KEY (app_id) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(WidgetWARPInfo) + COLUMN_NOT_NULL(app_id, INT,) + COLUMN_NOT_NULL(iri, VARCHAR(65536),) + COLUMN(subdomain_access, INT, CHECK(subdomain_access between 0 and 1)) + + TABLE_CONSTRAINTS( + PRIMARY KEY(app_id, iri) + FOREIGN KEY (app_id) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(FeaturesList) + COLUMN_NOT_NULL(FeatureUUID, INTEGER, primary key autoincrement) + COLUMN_NOT_NULL(FeatureName, TEXT, unique) + COLUMN_NOT_NULL(PluginPropertiesId, INT,) +CREATE_TABLE_END() + +CREATE_TABLE(PluginProperties) + COLUMN_NOT_NULL(PluginPropertiesId, INTEGER, primary key autoincrement) + COLUMN_NOT_NULL(InstallationState, INTEGER, DEFAULT 0) + COLUMN_NOT_NULL(PluginLibraryName, TEXT, unique) + COLUMN(PluginLibraryPath, TEXT,) + COLUMN(InstallURI, TEXT,) + COLUMN(KeyCN, TEXT,) + COLUMN(RootKeyCN, TEXT,) + COLUMN(RootKeyFingerprint, TEXT,) +CREATE_TABLE_END() + +CREATE_TABLE(PluginDependencies) + COLUMN_NOT_NULL(PluginPropertiesId, INTEGER, not null) + COLUMN_NOT_NULL(RequiredPluginPropertiesId, INTEGER, not null) +CREATE_TABLE_END() + +CREATE_TABLE(PluginImplementedObjects) + COLUMN_NOT_NULL(PluginObject, TEXT, unique) + COLUMN_NOT_NULL(PluginPropertiesId, INTEGER, not null) +CREATE_TABLE_END() + +CREATE_TABLE(PluginRequiredObjects) + COLUMN_NOT_NULL(PluginPropertiesId, INTEGER, not null) + COLUMN_NOT_NULL(PluginObject, TEXT, not null) +CREATE_TABLE_END() + +CREATE_TABLE(DeviceCapabilities) + COLUMN_NOT_NULL(DeviceCapID, INTEGER, primary key autoincrement) + COLUMN_NOT_NULL(DeviceCapName, TEXT, unique) + COLUMN(DeviceCapDefaultValue, INT,) +CREATE_TABLE_END() + +CREATE_TABLE(FeatureDeviceCapProxy) + COLUMN_NOT_NULL(FeatureUUID, INT, not null) + COLUMN_NOT_NULL(DeviceCapID, INT, not null) + + TABLE_CONSTRAINTS(PRIMARY KEY(FeatureUUID,DeviceCapID)) +CREATE_TABLE_END() + +CREATE_TABLE(PowderLevels) + COLUMN_NOT_NULL(app_id, INT, ) + COLUMN_NOT_NULL(id, INTEGER, primary key autoincrement) + COLUMN_NOT_NULL(category, TEXT, ) + COLUMN_NOT_NULL(level, INTEGER, ) + TABLE_CONSTRAINTS( + FOREIGN KEY (app_id) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(PowderLevelContexts) + COLUMN_NOT_NULL(levelId, INTEGER, ) + COLUMN_NOT_NULL(context, TEXT, ) + + TABLE_CONSTRAINTS( + FOREIGN KEY (levelId) REFERENCES PowderLevels (id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(ChildProtectionBlacklist) + COLUMN_NOT_NULL(url, TEXT, unique) +CREATE_TABLE_END() + +CREATE_TABLE(PowderRules) + COLUMN_NOT_NULL(id, INTEGER, primary key autoincrement) + COLUMN_NOT_NULL(category, TEXT, ) + COLUMN_NOT_NULL(level, INTEGER, ) + COLUMN(context, TEXT, ) + TABLE_CONSTRAINTS(unique(category,context)) +CREATE_TABLE_END() + +CREATE_TABLE(DefferedWidgetPackageInstallation) + COLUMN_NOT_NULL(path, TEXT, unique) +CREATE_TABLE_END() + +CREATE_TABLE(OCSPResponseStorage) + COLUMN_NOT_NULL(cert_chain, TEXT, primary key) + COLUMN(end_entity_check, INT,) + COLUMN(ocsp_status, INT,) + COLUMN(next_update_time, BIGINT,) +CREATE_TABLE_END() + +CREATE_TABLE(CRLResponseStorage) + COLUMN_NOT_NULL(distribution_point,TEXT, primary key) + COLUMN_NOT_NULL(crl_body, TEXT,) + COLUMN(next_update_time, BIGINT,) +CREATE_TABLE_END() + +CREATE_TABLE(AutoSaveIdPasswd) + COLUMN_NOT_NULL(address, VARCHAR(256), unique) + COLUMN_NOT_NULL(userId, VARCHAR(128),) + COLUMN_NOT_NULL(passwd, VARCHAR(128),) +CREATE_TABLE_END() + +CREATE_TABLE(SettginsList) + COLUMN_NOT_NULL(appId, INT,) + COLUMN_NOT_NULL(settingName, TEXT, ) + COLUMN_NOT_NULL(settingValue, TEXT, ) + TABLE_CONSTRAINTS( + FOREIGN KEY (appId) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(ApplicationServiceInfo) + COLUMN_NOT_NULL(app_id, INT,) + COLUMN_NOT_NULL(src, TEXT,) + COLUMN_NOT_NULL(operation, TEXT,) + COLUMN_NOT_NULL(scheme, TEXT,) + COLUMN_NOT_NULL(mime, TEXT,) + + TABLE_CONSTRAINTS( + PRIMARY KEY(app_id, operation, scheme, mime) + FOREIGN KEY(app_id) REFERENCES WidgetInfo (app_id) ON DELETE CASCADE + ) +CREATE_TABLE_END() + +CREATE_TABLE(WidgetWhiteURIList) + COLUMN_NOT_NULL(uri, VARCHAR(65536), primary key) + COLUMN_NOT_NULL(subdomain_access, INT, CHECK(subdomain_access between 0 and 1)) +CREATE_TABLE_END() + +SQL( + INSERT INTO WidgetWhiteURIList VALUES("http://samsung.com", 1); + INSERT INTO WidgetWhiteURIList VALUES("http://orange.fr", 1); + INSERT INTO WidgetWhiteURIList VALUES("http://orange.co.uk", 1); +) + +/*TODO: It will be removed when user agent is fixed. User agent MUST be configurable in development...*/ +CREATE_TABLE(UserAgents) + COLUMN_NOT_NULL(key_name, VARCHAR(256),) + COLUMN(key_value, VARCHAR(8000), DEFAULT '') + + TABLE_CONSTRAINTS(PRIMARY KEY(key_name)) +CREATE_TABLE_END() + +SQL( + INSERT INTO UserAgents VALUES("Tizen", "Mozilla/5.0 (Linux; U; Tizen 1.0; en-us) AppleWebKit/534.46 (KHTML, like Gecko) Mobile Tizen Browser/1.0"); + INSERT INTO UserAgents VALUES("Chrome 16", "Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20110706 Firefox/5.0"); +) + +SQL( + COMMIT; +) diff --git a/modules/widget_dao/orm/wrt_db_definitions b/modules/widget_dao/orm/wrt_db_definitions new file mode 100644 index 0000000..1e2200e --- /dev/null +++ b/modules/widget_dao/orm/wrt_db_definitions @@ -0,0 +1,7 @@ +DATABASE_START(wrt) + +#include "wrt_db" +#include "iana_db" +#include "version_db" + +DATABASE_END() diff --git a/modules/widget_dao/orm/wrt_db_sql_generator.h b/modules/widget_dao/orm/wrt_db_sql_generator.h new file mode 100644 index 0000000..a4d7da8 --- /dev/null +++ b/modules/widget_dao/orm/wrt_db_sql_generator.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file wrt_db_sql_generator.h + * @author Bartosz Janiak (b.janiak@samsung.com) + * @version 1.0 + * @brief Macro definitions for generating the SQL input file from database definition. + */ + +//Do not include this file directly! It is used only for SQL code generation. + +#include + +#include "wrt_db_definitions" diff --git a/packaging/dpl.spec b/packaging/dpl.spec new file mode 100644 index 0000000..0b043d7 --- /dev/null +++ b/packaging/dpl.spec @@ -0,0 +1,79 @@ +Name: dpl +Summary: Design patterns library +Version: 0.1.57 +Release: 1 +Group: System/Libraries +License: TBD +Source0: %{name}-%{version}.tar.bz2 +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig +BuildRequires: cmake +BuildRequires: pkgconfig(ecore) +BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(heynoti) +BuildRequires: pkgconfig(appcore-efl) +BuildRequires: pkgconfig(libssl) +BuildRequires: pkgconfig(sqlite3) +BuildRequires: pkgconfig(db-util) +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(zlib) +BuildRequires: pkgconfig(libpcrecpp) +BuildRequires: pkgconfig(icu-uc) + +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +%description +Design patterns library is a collection of useful C++ utilities to easily develop window applications. + +%package efl +Summary: Design patterns library - EFL based +Group: System/Libraries + +%description efl +Design patterns library is a collection of useful C++ utilities to easily develop window applications. + +%package efl-devel +Summary: Design patterns library - EFL based developer files +Group: Development/Libraries +Requires: %{name}-efl = %{version}-%{release} + +%description efl-devel +Design patterns library is a collection of useful C++ utilities to easily develop window applications. The most important part of library is full MVC support. It also supports event-based architecture, adds wrappers for many packages and provides many basic C++ utilities as RAII objects, singletons, and many other. + +%prep +%setup -q -n %{name}-%{version} + +%build + +cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} \ + -DDPL_LOG=ON \ + -DDISABLE_GTK=ON + +# Call make instruction with smp support +make %{?jobs:-j%jobs} + +# >> build post +# << build post +%install +rm -rf %{buildroot} +%make_install + +%clean +rm -rf %{buildroot} + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + + +%files efl +%defattr(-,root,root,-) +%{_libdir}/lib*-efl.so* +# << files + +%files efl-devel +%defattr(-,root,root,-) +%{_includedir}/dpl-efl/* +%{_libdir}/pkgconfig/*-efl.pc + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..8453ccb --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +SET(DPL_TEST_INCLUDE_DIR + ${DPL_CORE_INCLUDE_DIR} + ${DPL_EVENT_INCLUDE_DIR} + ${DPL_DBUS_INCLUDE_DIR} + ${DPL_DB_INCLUDE_DIR} + ${DPL_RPC_INCLUDE_DIR} + ${DPL_SOCKET_INCLUDE_DIR} + ${DPL_TEST_ENGINE_INCLUDE_DIR} + ${DPL_LOG_INCLUDE_DIR} +) + +ADD_SUBDIRECTORY(core) +ADD_SUBDIRECTORY(dbus) +ADD_SUBDIRECTORY(db) +ADD_SUBDIRECTORY(event) +ADD_SUBDIRECTORY(ace) +ADD_SUBDIRECTORY(vcore) +ADD_SUBDIRECTORY(localization) diff --git a/tests/ace/AttributeSetter.cpp b/tests/ace/AttributeSetter.cpp new file mode 100644 index 0000000..18a31f0 --- /dev/null +++ b/tests/ace/AttributeSetter.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "AttributeSetter.h" + +#include +IMPLEMENT_SINGLETON(AttributeSetter) diff --git a/tests/ace/AttributeSetter.h b/tests/ace/AttributeSetter.h new file mode 100644 index 0000000..20962ae --- /dev/null +++ b/tests/ace/AttributeSetter.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file AttributeSetter.h + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 0.1 + * @brief Stub for attribute_facade.h + */ +#ifndef _TEST_ACE_TESTS_ATTRIBUTE_SETTER_ +#define _TEST_ACE_TESTS_ATTRIBUTE_SETTER_ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +class AttributeSetter { +public: + typedef std::list ValueList; + typedef std::map AttributeMap; + typedef std::map CollectionHandlerMap; + + typedef std::list Evil; + + void get(const Request &request, + const Evil* evil) + { + int handle = request.getWidgetHandle(); + + CollectionHandlerMap::iterator widgetContextIt = + m_collectionMap.find(handle); + + if (widgetContextIt == m_collectionMap.end()) { + LogError("WidgetContext is empty"); + return; + } + + FOREACH(it, *evil) + { + std::string attrName = *(it->first); + + if (attrName == "device-cap") { + Request::DeviceCapabilitySet devCapSet = + request.getDeviceCapabilitySet(); + std::copy(devCapSet.begin(), + devCapSet.end(), + std::back_inserter(*(it->second))); + continue; + } + + AttributeMap::iterator attrIt = + widgetContextIt->second.find(attrName); + + if (attrIt == widgetContextIt->second.end()) { + LogError("No attribute: " << attrName << + " in context nr: " << handle); + const ATTRIBUTE &a = *it; + const_cast(a).second = NULL; + continue; + } + + ValueList valueList = attrIt->second; + + std::copy(valueList.begin(), + valueList.end(), + std::back_inserter(*(it->second))); + } + } + + void addValue(int handler, + const std::string &attributeName, + const std::string &attributeValue) + { + m_collectionMap[handler][attributeName].push_back(attributeValue); + } + + std::string getValue(int handler, const std::string &attributeName) + { + std::list lst = m_collectionMap[handler][attributeName]; + if (lst.empty()) + return "fake-device-name-that-should-not-match-with-anything"; + return lst.front(); + } + + void addValue(int handler, + const char *attributeName, + const char *attributeValue) + { + std::string aN = attributeName; + std::string aV = attributeValue; + addValue(handler, aN, aV); + } + + void clear() { + m_collectionMap.clear(); + } +protected: + AttributeSetter(){} + ~AttributeSetter(){} +private: + CollectionHandlerMap m_collectionMap; +}; + +typedef DPL::Singleton AttributeSetterSingleton; + +#endif // _TEST_ACE_TESTS_ATTRIBUTE_SETTER_ diff --git a/tests/ace/CMakeLists.txt b/tests/ace/CMakeLists.txt new file mode 100644 index 0000000..b65f6bf --- /dev/null +++ b/tests/ace/CMakeLists.txt @@ -0,0 +1,75 @@ +# +#Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) +# @version 0.1 +# @brief +# +INCLUDE(FindPkgConfig) +SET(TARGET_ACE_TEST "dpl-tests-ace") + +PKG_CHECK_MODULES(ACE_TEST_DEP + libxml-2.0 + gobject-2.0 + REQUIRED + ) + +# Set DPL tests sources +SET(ACE_TESTS_SOURCES + ${PROJECT_SOURCE_DIR}/tests/ace/ace_tests.cpp + ${PROJECT_SOURCE_DIR}/tests/ace/loop_control.cpp + ${PROJECT_SOURCE_DIR}/tests/ace/TestSuite01.cpp + ${PROJECT_SOURCE_DIR}/tests/ace/TestSuite02.cpp + ${PROJECT_SOURCE_DIR}/tests/ace/TestSuite03.cpp + ${PROJECT_SOURCE_DIR}/tests/ace/TestSuite04.cpp + ${PROJECT_SOURCE_DIR}/tests/ace/TestSuite05.cpp + ${PROJECT_SOURCE_DIR}/tests/ace/TestSuite06.cpp + ${PROJECT_SOURCE_DIR}/tests/ace/TestSuite07.cpp + ${PROJECT_SOURCE_DIR}/tests/ace/Interfaces.cpp + ${PROJECT_SOURCE_DIR}/tests/ace/PEPSingleton.cpp + ${PROJECT_SOURCE_DIR}/tests/ace/AttributeSetter.cpp + ) + +INCLUDE_DIRECTORIES( + ${SYS_EFL_INCLUDE_DIRS} + ${DPL_TEST_INCLUDE_DIR} + ${PROJECT_SOURCE_DIR}/modules/ace/include + ${PROJECT_SOURCE_DIR}/tests/ace + ${ACE_TEST_DEP_INCLUDE_DIRS} + ) + +ADD_EXECUTABLE(${TARGET_ACE_TEST} ${ACE_TESTS_SOURCES}) + +TARGET_LINK_LIBRARIES(${TARGET_ACE_TEST} + ${SYS_EFL_LIBRARIES} + ${TARGET_DPL_EFL} + ${TARGET_DPL_TEST_ENGINE_EFL} + ${TARGET_ACE_LIB} + ${ACE_TEST_DEP_LIBRARIES} + ) + +INSTALL(TARGETS ${TARGET_ACE_TEST} + DESTINATION bin + PERMISSIONS OWNER_READ + OWNER_WRITE + OWNER_EXECUTE + GROUP_READ + GROUP_EXECUTE + WORLD_READ + WORLD_EXECUTE + ) + +ADD_SUBDIRECTORY(test-configuration) diff --git a/tests/ace/Interfaces.cpp b/tests/ace/Interfaces.cpp new file mode 100644 index 0000000..78ed1d0 --- /dev/null +++ b/tests/ace/Interfaces.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file Interfaces.cpp + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief Stubs used by PolicyInformationPoint.h + */ +#include "Interfaces.h" + +#include + +#include "AttributeSetter.h" + +int WebRuntimeImp::getAttributesValues( + const Request &request, + std::list *attribute) +{ + LogError("Running stub"); + AttributeSetterSingleton::Instance().get(request, attribute); + return 0; +} + +int ResourceInformationImp::getAttributesValues( + const Request &request, + std::list *attribute) +{ + LogError("Running stub"); + AttributeSetterSingleton::Instance().get(request, attribute); + return 0; +} + +int OperationSystemImp::getAttributesValues( + const Request &request, + std::list *attribute) +{ + LogError("Running stub"); + AttributeSetterSingleton::Instance().get(request, attribute); + return 0; +} + +int FunctionParamImpl::getAttributesValues(const Request & /* request*/, + std::list *attributes) +{ + LogError("Running stub"); + FOREACH(iter, *attributes) + { + std::string attributeName = *(iter->first); + + ParamMap::const_iterator i; + std::pair jj = + paramMap.equal_range(attributeName); + + for (i = jj.first; i != jj.second; ++i) { + iter->second->push_back(i->second); + LogDebug("Attribute: " << attributeName << " Value: " << + i->second); + } + } + return 0; +} + diff --git a/tests/ace/Interfaces.h b/tests/ace/Interfaces.h new file mode 100644 index 0000000..6d769e5 --- /dev/null +++ b/tests/ace/Interfaces.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file Interfaces.h + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief Stubs used by PolicyInformationPoint.h + */ +#ifndef _TEST_ACE_TESTS_INTERFACE_H_ +#define _TEST_ACE_TESTS_INTERFACE_H_ + +#include +#include +#include +#include + +class Request; + +class WebRuntimeImp : public IWebRuntime { +public: + int getAttributesValues( + const Request &request, + std::list *attribute); + std::string getSessionId(const Request &) { return std::string(); } +}; + +class ResourceInformationImp : public IResourceInformation { +public: + int getAttributesValues( + const Request &request, + std::list *attribute); +}; + +class OperationSystemImp : public IOperationSystem { +public: + int getAttributesValues( + const Request &request, + std::list *attribute); +}; + +class FunctionParamImpl : public IFunctionParam +{ +public: + virtual int getAttributesValues(const Request & /* request*/, + std::list *attributes); + void addAttribute(const std::string &key, + const std::string &value) + { + paramMap.insert(make_pair(key, value)); + } + virtual ~FunctionParamImpl() {} +private: + typedef std::multimap ParamMap; + ParamMap paramMap; +}; + +#endif // _TEST_ACE_TESTS_INTERFACE_H_ diff --git a/tests/ace/PEPSingleton.cpp b/tests/ace/PEPSingleton.cpp new file mode 100644 index 0000000..9551700 --- /dev/null +++ b/tests/ace/PEPSingleton.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file Interfaces.h + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief Instance of PEP used in tests. + */ +#include "PEPSingleton.h" + +#include +IMPLEMENT_SINGLETON(PolicyEnforcementPoint) + diff --git a/tests/ace/PEPSingleton.h b/tests/ace/PEPSingleton.h new file mode 100644 index 0000000..1dfb912 --- /dev/null +++ b/tests/ace/PEPSingleton.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file Interfaces.h + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief Instantion of PEP used in tests. + */ +#ifndef _TEST_ACE_TEST_PEPSINGLETON_H_ +#define _TEST_ACE_TEST_PEPSINGLETON_H_ + +#include +#include + +typedef DPL::Singleton PEPSingleton; + +#endif // _TEST_ACE_TEST_PEPSINGLETON_H_ + diff --git a/tests/ace/TestSuite01.cpp b/tests/ace/TestSuite01.cpp new file mode 100644 index 0000000..1d169ae --- /dev/null +++ b/tests/ace/TestSuite01.cpp @@ -0,0 +1,986 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file TestSuite01.cpp + * @author unknown + * @version 1.0 + * @brief Test cases for PolicyEvaluator class. + */ +#include + +#include +#include + +#include +#include +#include +#include + +#include "AttributeSetter.h" +#include "Interfaces.h" +#include "PEPSingleton.h" + +#define GENERAL_TEST_POLICY "/usr/etc/ace/general-test.xml" +#define GENERAL_TEST_POLICY_UNDETERMIND "/usr/etc/ace/undefined-test.xml" +#define GENERAL_TEST_POLICY_GSETTINGS "/usr/etc/ace/policy-test-gsettings.xml" +#define GENERAL_TEST_POLICY_TEST "/usr/etc/ace/policy-test.xml" + +#define CLEANENV(d) \ + do{ \ + if (PEPSingleton::Instance().getPdp()->getCurrentPolicy() != (d)) { \ + PEPSingleton::Instance().getPdp()->updatePolicy(d); \ + } \ + AttributeSetterSingleton::Instance().clear(); \ + AceDB::AceDAO::clearWidgetDevCapSettings(); \ + AceDB::AceDAO::clearDevCapSettings(); \ + }while(0) + +#define QU(nr, subid, resid) \ + do{ \ + AttributeSetterSingleton::Instance().addValue((nr), "id", (subid)); \ + AttributeSetterSingleton::Instance().addValue((nr), "resource-id", (resid));\ + }while(0) + +#define QA(nr, attrname, attrvalue) \ + AttributeSetterSingleton::Instance().addValue((nr), (attrname), (attrvalue)) + +#define QC(nr, expect) \ + do{ \ + Request request((nr), WidgetExecutionPhase_Unknown); \ + std::string a = \ + AttributeSetterSingleton::Instance().getValue((nr),"resource-id"); \ + request.addDeviceCapability(a); \ + PolicyResult result = PEPSingleton::Instance().check(request); \ + RUNNER_ASSERT(result == (expect)); \ + }while(0) + +#define QCP(nr, param, expect) \ + do{ \ + Request request((nr), WidgetExecutionPhase_Unknown, (param)); \ + std::string a = \ + AttributeSetterSingleton::Instance().getValue((nr),"resource-id"); \ + request.addDeviceCapability(a); \ + PolicyResult result = PEPSingleton::Instance().check(request); \ + RUNNER_ASSERT(result == (expect)); \ + }while(0) + +#define TESTSUITE01(n) \ +RUNNER_TEST(ts01_general_tests_ ## n) + +TESTSUITE01(01){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "subject", "resource"); + QA(1, "version", "3"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(02){ + CLEANENV(GENERAL_TEST_POLICY); + QU(2, "subject2", "resource"); + QA(2, "version", "1"); + QC(2, PolicyEffect::DENY); +} + +TESTSUITE01(03){ + CLEANENV(GENERAL_TEST_POLICY); + QU(3, "subject3", "resource2"); + QC(3, PolicyEffect::DENY); +} + +TESTSUITE01(04){ + CLEANENV(GENERAL_TEST_POLICY); + QU(4, "subject4", "resource4"); + QA(4, "version", "4"); + QC(4, PolicyEffect::PERMIT); +} + +TESTSUITE01(05){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(5, "subject5", "resource5"); + QA(5, "version", "5"); + QC(5, PolicyEffect::PERMIT); + + QU(6, "subject5", "resource5"); + QA(6, "version", "1"); + QC(6, PolicyDecision::NOT_APPLICABLE); + + QU(7, "subject6", "resource6"); + QA(7, "version", "1"); + QC(7, PolicyEffect::PERMIT); +} + +TESTSUITE01(06){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(6, "subject7", "resource7"); + QA(6, "version", "7"); + QA(6, "author", "author"); + QC(6, PolicyDecision::NOT_APPLICABLE); + + QU(7, "subject7", "resource7"); + QA(7, "version", "7"); + QA(7, "author", "author2"); + QC(7, PolicyEffect::PERMIT); +} + +TESTSUITE01(07){ + CLEANENV(GENERAL_TEST_POLICY); + QU(3, "subject7", "resource7"); + QA(3, "version", "1"); + QA(3, "author", "author3"); + QC(3, PolicyEffect::DENY); +} + +TESTSUITE01(08){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(8, "s8a", "r8"); + QA(8, "r8v1", "1"); + QA(8, "r8v2", "2"); + QC(8, PolicyEffect::PERMIT); + + QU(9, "s8b", "r8"); + QA(9, "r8v1", "1"); + QA(9, "r8v2", "2"); + QC(9, PolicyEffect::DENY); + + QU(19, "s8c", "r8"); + QA(19, "r8v1", "1"); + QA(19, "r8v2", "2"); + QC(19, PolicyDecision::NOT_APPLICABLE); +} + +TESTSUITE01(09){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(1, "s9a", "r9"); + QA(1, "r9a", "http://onet.pl:90/test.html"); + QA(1, "r9b", "http://onet.pl:80/test.html"); + QA(1, "r9c", "http://onet.pl:80"); + QA(1, "r9d", "http://onet.pl:80"); + QA(1, "r9e", "http://onet.pl:80/test.html"); + QA(1, "r9g", "http://onet.pl:80/test.html"); + QC(1, PolicyEffect::PERMIT); + + QU(2, "s9b", "r9"); + QA(2, "r9a", "http://onet.pl:90/test.html"); + QA(2, "r9b", "http://onet.pl:80/test.html"); + QA(2, "r9c", "http://onet.pl:80"); + QA(2, "r9d", "http://onet.pl:80"); + QA(2, "r9e", "http://onet.pl:80/test.html"); + QA(2, "r9g", "http://onet.pl:80/test.html"); + QC(2, PolicyEffect::DENY); + + QU(3, "s9c", "r9"); + QA(3, "r9a", "http://onet.pl:90/test.html"); + QA(3, "r9b", "http://onet.pl:80/test.html"); + QA(3, "r9c", "http://onet.pl:80"); + QA(3, "r9d", "http://onet.pl:80"); + QA(3, "r9e", "http://onet.pl:80/test.html"); + QA(3, "r9g", "http://onet.pl:80/test.html"); + QC(3, PolicyEffect::PERMIT); + + QU(4, "s9d", "r9"); + QA(4, "r9a", "http://onet.pl:90/test.html"); + QA(4, "r9b", "http://onet.pl:80/test.html"); + QA(4, "r9c", "http://onet.pl:80"); + QA(4, "r9d", "http://onet.pl:80"); + QA(4, "r9e", "http://onet.pl:80/test.html"); + QA(4, "r9g", "http://onet.pl:80/test.html"); + QC(4, PolicyEffect::DENY); + + QU(5, "s9e", "r9"); + QA(5, "r9a", "http://onet.pl:90/test.html"); + QA(5, "r9b", "http://onet.pl:80/test.html"); + QA(5, "r9c", "http://onet.pl:80"); + QA(5, "r9d", "http://onet.pl:80"); + QA(5, "r9e", "http://onet.pl:80/test.html"); + QA(5, "r9g", "http://onet.pl:80/test.html"); + QC(5, PolicyEffect::DENY); + + QU(6, "s9f", "r9"); + QA(6, "r9a", "http://onet.pl:90/test.html"); + QA(6, "r9b", "http://onet.pl:80/test.html"); + QA(6, "r9c", "http://onet.pl:80"); + QA(6, "r9d", "http://onet.pl:80"); + QA(6, "r9e", "http://onet.pl:80/test.html"); + QA(6, "r9g", "http://onet.pl:80/test.html"); + QC(6, PolicyEffect::PERMIT); + + QU(7, "s9g", "r9"); + QA(7, "r9a", "http://onet.pl:90/test.html"); + QA(7, "r9b", "http://onet.pl:80/test.html"); + QA(7, "r9c", "http://onet.pl:80"); + QA(7, "r9d", "http://onet.pl:80"); + QA(7, "r9e", "http://onet.pl:80/test.html"); + QA(7, "r9g", "http://onet.pl:80/test.html"); + QC(7, PolicyEffect::DENY); + + QU(8, "s9h", "r9"); + QA(8, "r9a", "http://onet.pl:90/test.html"); + QA(8, "r9b", "http://onet.pl:80/test.html"); + QA(8, "r9c", "http://onet.pl:80"); + QA(8, "r9d", "http://onet.pl:80"); + QA(8, "r9e", "http://onet.pl:80/test.html"); + QA(8, "r9g", "http://onet.pl:80/test.html"); + QC(8, PolicyEffect::PERMIT); +} + +TESTSUITE01(10){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(1, "s10a", "r10"); + QA(1, "r10a", "15ba"); + QA(1, "r10c", "15a1"); + QC(1, PolicyEffect::PERMIT); + + QU(2, "s10b", "r10"); + QA(2, "r10a", "15ba"); + QA(2, "r10c", "15a1"); + QC(2, PolicyEffect::DENY); + + QU(3, "s10c", "r10"); + QA(3, "r10a", "15ba"); + QA(3, "r10c", "15a1"); + QC(3, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(11){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(1, "s15", "device:pim.contacts"); + QA(1, "uri", "//buu.com.pl"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(12){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(1, "s16", "device:pim.contacts"); + QA(1, "uri", "//v.com.pl"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(13){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(1, "s17a", "resource4"); + QA(1, "version", "4"); + QC(1, PolicyEffect::PERMIT); + + QU(2, "s17b", "resource4"); + QA(2, "version", "4"); + QC(2, PolicyEffect::DENY); +} + +TESTSUITE01(14){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(1, "s18a", "device:pim.contacts"); + QA(1, "uri", "buu.com.pl"); + QC(1, PolicyEffect::PERMIT); + + QU(2, "s18b", "device:pim.contacts"); + QA(2, "uri", "buu.com.pl"); + QC(2, PolicyEffect::PERMIT); + + QU(3, "s18c", "device:pim.contacts"); + QA(3, "uri", "buu.com.pl"); + QC(3, PolicyEffect::PERMIT); +} + +TESTSUITE01(15){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(1, "s19.1", "resource4"); + QA(1, "key-root-trust", "voperator"); + QC(1, PolicyEffect::DENY); + + QU(2, "s19.2", "resource4"); + QA(2, "key-root-trust", "voperator"); + QC(2, PolicyEffect::PERMIT); +} + +TESTSUITE01(16){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(1, "s20.1", "resource4"); + QA(1, "signer-id", "v7zha89%49x£$"); + QC(1, PolicyEffect::DENY); + + QU(2, "s20.2", "resource4"); + QA(2, "signer-id", "v7zha89%49x£$"); + QC(2, PolicyEffect::PERMIT); +} + +TESTSUITE01(17){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(1, "s21", "undetermined"); + QC(1, PolicyDecision::NOT_APPLICABLE); + + + QU(2, "s21a", "undetermined"); + QC(2, PolicyEffect::PERMIT); +} + +TESTSUITE01(18){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s23", "device:pim.contacts"); + QA(1, "version", "undetermined"); + QC(1, PolicyDecision::NOT_APPLICABLE); +} + +TESTSUITE01(19){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s24", "device:pim"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(20){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s25.1", "device:pim.contacts"); + QA(1, "version", "5"); + QA(1, "roaming", "off"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(21){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s25.2", "device:pim.contacts"); + QA(1, "version", "undetermined"); + QC(1, PolicyDecision::NOT_APPLICABLE); +} + +TESTSUITE01(22){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + QU(1, "s25.3", "device:pim.contacts"); +// undetermined QA(1, "version", ""); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(23){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + QU(1, "s25.4", "device:pim.contacts"); + //undetermined + //QA(1, "version", "", true); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(24){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + QU(1, "s26.1", "device:pim.contacts"); + QA(1, "version", "5"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(25){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + QU(1, "s26.2", "device:pim.contacts"); + //undetermined + //QA(1, "version", "", true); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(26){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + QU(1, "s26.3", "device:pim.contacts"); + //undetermined + //QA(1, "version", "", true); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(27){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + QU(1, "s26.4", "device:pim.contacts"); + //undetermined + //QA(1, "version", "undetermined", true); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(28){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + QU(1, "s27.1", "device:pim.contacts"); + //undetermined + //QA(1, "version", "undetermined", true); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(29){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + QU(1, "s27.2", "device:pim.contacts"); + //undetermined + //QA(1, "version", "undetermined", true); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(30){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + QU(1, "s27.3", "device:pim.contacts"); + //undetermined + //QA(1, "version", "undetermined", true); + QC(1, PolicyDecision::NOT_APPLICABLE); +} + +TESTSUITE01(31){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + QU(1, "s27.4", "device:pim.contacts"); + //undetermined + //QA(1, "version", "undetermined", true); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(32){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + QU(1, "s28", "device:pim.contacts"); + //undetermined + //QA(1, "version", "undetermined", true); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(33){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + QU(1, "s29", "device:pim.contacts"); + //undetermined + //QA(1, "version", "undetermined", true); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(34){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + QU(1, "s30.1", "device:pim.contacts"); + //undetermined + //QA(1, "version", "undetermined", true); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(35){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + QU(1, "s30.2", "device:pim.contacts"); + //undetermined + //QA(1, "version", "undetermined", true); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(36){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + QU(1, "s30.3", "device:pim.contacts"); + //undetermined + //QA(1, "version", "undetermined", true); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(37){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + QU(1, "s30.4", "device:pim.contacts"); + //undetermined + //QA(1, "version", "undetermined", true); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(38){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + QU(1, "s31.1", "device:pim.contacts"); + //undetermined + //QA(1, "version", "undetermined", true); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(39){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + //undetermined + //QA(1, "version", "undetermined", true); + QU(1, "s31.2.1", "device:pim.contacts"); + QC(1, PolicyEffect::DENY); +} + +TESTSUITE01(40){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + //undetermined + //QA(1, "version", "undetermined", true); + QU(1, "s31.2.2", "device:pim.contacts"); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(41){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + //undetermined + //QA(1, "version", "undetermined", true); + QU(1, "s31.3", "device:pim.contacts"); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(42){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + //undetermined + //QA(1, "version", "undetermined", true); + QU(1, "s32.1", "device:pim.contacts"); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(43){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + //undetermined + //QA(1, "version", "undetermined", true); + QU(1, "s32.2.1", "device:pim.contacts"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(44){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + //undetermined + //QA(1, "version", "undetermined", true); + QU(1, "s32.2.2", "device:pim.contacts"); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(45){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + //undetermined + //QA(1, "version", "undetermined", true); + QU(1, "s32.3", "device:pim.contacts"); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(46){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + //undetermined + //QA(1, "version", "undetermined", true); + QU(1, "s33.1", "device:pim.contacts"); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(47){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + //undetermined + //QA(1, "version", "undetermined", true); + QU(1, "s33.2", "device:pim.contacts"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(48){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + //undetermined + //QA(1, "version", "undetermined", true); + QU(1, "s33.3", "device:pim.contacts"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(49){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //Test undetermined, we have to refine this tests to test something + //undetermined + //QA(1, "version", "undetermined", true); + QU(1, "s34.1", "device:pim.contacts"); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(50){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //undetermined + //QA(1, "version", "undetermined", true); + QU(1, "s34.2", "device:pim.contacts"); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(51){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //undetermined + //QA(1, "version", "undetermined", true); + QU(1, "s35.1", "device:pim.contacts"); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(52){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + //undetermined + //QA(1, "version", "undetermined", true); + QU(1, "s35.2", "device:pim.contacts"); + QC(1, PolicyResult::UNDETERMINED); +} + +TESTSUITE01(53){ + CLEANENV(GENERAL_TEST_POLICY_UNDETERMIND); + QU(1, "s36", "device:pim.contacts"); + //undetermined + //QA(1, "version", "undetermined", true); + QC(1, PolicyDecision::NOT_APPLICABLE); +} + +TESTSUITE01(54){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s37", "device:pim.contacts.read"); + QC(1, PolicyEffect::PROMPT_ONESHOT); +} + +TESTSUITE01(55){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(1, "s38", "device:pim.contacts.read"); + QC(1, PolicyEffect::PROMPT_SESSION); + + QU(2, "s38.4", "device:pim.contacts.read"); + QC(2, PolicyEffect::PROMPT_SESSION); +} + +TESTSUITE01(56){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(1, "s39", "device:pim.contacts.read"); + QC(1, PolicyEffect::PROMPT_BLANKET); + + QU(2, "s39.4", "device:pim.contacts.read"); + QC(2, PolicyEffect::PROMPT_BLANKET); +} + +TESTSUITE01(57){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s40", "r40"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(58){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s41", "r41"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(59){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s42.1", "r42.1"); + QC(1, PolicyEffect::DENY); +} + +TESTSUITE01(60){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s42.2", "r42.2"); + QC(1, PolicyEffect::DENY); + +} + +TESTSUITE01(61){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s43.1", "r43.1"); + QC(1, PolicyEffect::DENY); +} + +TESTSUITE01(62){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s43.2", "r43.2"); + QC(1, PolicyEffect::DENY); +} + +TESTSUITE01(63){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s44.1", "r44.1"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(64){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s44.2", "r44.2"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(65){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s45.1", "r45.1"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(66){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s45.2", "r45.2"); + QC(1, PolicyEffect::DENY); +} + +TESTSUITE01(67){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s46.1", "r46.1"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(68){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s46.2", "r46.2"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(69){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s47.1", "r47.1"); + QC(1, PolicyEffect::DENY); +} + +TESTSUITE01(70){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s47.2", "r47.2"); + QC(1, PolicyEffect::DENY); +} + +TESTSUITE01(71){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "s48", "device:pim.contacts"); + QA(1, "uri", "http://www.test.pl:80"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(72){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "BF00", "BFR00"); + QC(1, PolicyEffect::DENY); +} + +TESTSUITE01(73){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "BF01", "BFR01"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(74){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "BF02", "BFR02"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(75){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "BF03", "BFR03"); + QC(1, PolicyEffect::DENY); +} + +TESTSUITE01(76){ + CLEANENV(GENERAL_TEST_POLICY); + QU(1, "BF04", "BFR04"); + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(77){ + CLEANENV(GENERAL_TEST_POLICY); + FunctionParamImpl functionParam; + functionParam.addAttribute("param:recipients","+44091234"); + QU(1, "paramTestSubject", "resource"); + QA(1, "dev-cap","messaging"); + QCP(1, &functionParam, PolicyEffect::DENY); +} + +TESTSUITE01(78){ + CLEANENV(GENERAL_TEST_POLICY); + FunctionParamImpl functionParam; + functionParam.addAttribute("param:recipients","+44081234"); + QU(1, "paramTestSubject", "resource"); + QA(1, "dev-cap","messaging"); + QCP(1, &functionParam, PolicyEffect::PERMIT); +} + +TESTSUITE01(79){ + CLEANENV(GENERAL_TEST_POLICY); + FunctionParamImpl functionParam; + functionParam.addAttribute("param:recipients","+4812345656"); + QU(1, "paramTestSubject", "resource"); + QA(1, "dev-cap","messaging"); + QCP(1, &functionParam, PolicyEffect::PERMIT); +} + +TESTSUITE01(80){ + CLEANENV(GENERAL_TEST_POLICY); + FunctionParamImpl functionParam; + functionParam.addAttribute("param:quality","low"); + QU(1, "paramTestSubject", "resource"); + QA(1, "dev-cap","messaging"); + QCP(1, &functionParam, PolicyDecision::NOT_APPLICABLE); +} + +TESTSUITE01(81){ + CLEANENV(GENERAL_TEST_POLICY); + FunctionParamImpl functionParam; + functionParam.addAttribute("param:quality", "high"); + QU(1, "paramTestSubject", "resource"); + QA(1, "dev-cap","camera"); + QCP(1, &functionParam, PolicyEffect::PERMIT); +} + +TESTSUITE01(82){ + CLEANENV(GENERAL_TEST_POLICY); + + QU(1, "s61a", "r61a"); + QC(1, PolicyEffect::DENY); + + FunctionParamImpl functionParam; + functionParam.addAttribute("param:name", "type"); + QU(2, "s61b", "r61b"); + QCP(2, &functionParam, PolicyEffect::PERMIT); + + QU(3, "s61c", "r61c"); + QC(3, PolicyEffect::DENY); + + FunctionParamImpl functionParam4; + functionParam4.addAttribute("param:name", "type"); + functionParam4.addAttribute("param:name", "port"); + QU(4, "s61d", "r61d"); + QCP(4, &functionParam4, PolicyEffect::PERMIT); +} + +TESTSUITE01(83){ + CLEANENV(GENERAL_TEST_POLICY_TEST); + + PermissionList sList; + sList.push_back(PermissionTriple(1, "b1", Preference::PREFERENCE_PERMIT)); + sList.push_back(PermissionTriple(2, "bb2", Preference::PREFERENCE_DENY)); + sList.push_back(PermissionTriple(3, "d3", Preference::PREFERENCE_DEFAULT)); + SettingsLogic::setWidgetDevCapSettings(sList); + + QU(1, "a1", "b1"); + QC(1, PolicyEffect::DENY); + + QU(2, "aa2", "bb2"); + QC(2, PolicyEffect::DENY); + + QU(3, "c3", "d3"); + QC(3, PolicyEffect::PERMIT); +} + +TESTSUITE01(84){ + CLEANENV(GENERAL_TEST_POLICY_TEST); + PermissionList sList; + sList.push_back(PermissionTriple(3, "d3", Preference::PREFERENCE_ONE_SHOT_PROMPT)); + SettingsLogic::setWidgetDevCapSettings(sList); + QU(3, "c3", "d3"); + QC(3, PolicyEffect::PROMPT_ONESHOT); +} + +TESTSUITE01(85){ + CLEANENV(GENERAL_TEST_POLICY_TEST); + + PermissionList sList; + sList.push_back(PermissionTriple(1, "bnp", Preference::PREFERENCE_DEFAULT)); + sList.push_back(PermissionTriple(2, "bbnp", Preference::PREFERENCE_DENY)); + sList.push_back(PermissionTriple(3, "dnp", Preference::PREFERENCE_PERMIT)); + + SettingsLogic::setWidgetDevCapSettings(sList); + + QU(1, "anp", "bnp"); + QC(1, PolicyDecision::NOT_APPLICABLE); + + QU(2, "aanp", "bbnp"); + QC(2, PolicyEffect::DENY); + + QU(3, "cnp", "dnp"); + QC(3, PolicyEffect::PERMIT); +} + +TESTSUITE01(86){ + CLEANENV(GENERAL_TEST_POLICY_TEST); + + PermissionList sList; + sList.push_back(PermissionTriple(1, "b1", Preference::PREFERENCE_DEFAULT)); + sList.push_back(PermissionTriple(2, "bb", Preference::PREFERENCE_DENY)); + sList.push_back(PermissionTriple(3, "d3", Preference::PREFERENCE_PERMIT)); + sList.push_back(PermissionTriple(4, "b1", Preference::PREFERENCE_PERMIT)); + sList.push_back(PermissionTriple(5, "bb2", Preference::PREFERENCE_DENY)); + sList.push_back(PermissionTriple(6, "d3", Preference::PREFERENCE_DEFAULT)); + + SettingsLogic::setWidgetDevCapSettings(sList); + + QU(1, "a1", "b1"); + QC(1, PolicyEffect::DENY); + + QU(2, "aa", "bb"); + QC(2, PolicyEffect::DENY); + + QU(3, "c3", "d3"); + QC(3, PolicyEffect::PERMIT); + + QU(5, "aa2", "bb2"); + QC(5, PolicyEffect::DENY); +} + +TESTSUITE01(87){ + CLEANENV(GENERAL_TEST_POLICY_TEST); + + QU(1, "c3", "d3"); + QC(1, PolicyEffect::PERMIT); + + QU(2, "aa2", "bb2"); + QC(2, PolicyEffect::PERMIT); + + SettingsLogic::updateDevCapSetting("d3", Preference::PREFERENCE_DENY); + SettingsLogic::updateDevCapSetting("bb2", Preference::PREFERENCE_DEFAULT); + + QC(1, PolicyEffect::DENY); + + QC(2, PolicyEffect::PERMIT); + + SettingsLogic::updateDevCapSetting("d3", Preference::PREFERENCE_PERMIT); + + QC(1, PolicyEffect::PERMIT); +} + +TESTSUITE01(88){ + CLEANENV(GENERAL_TEST_POLICY_GSETTINGS); + std::string d3("d3"); + + QU(1, "c3", "d3"); + QC(1, PolicyEffect::PERMIT); + + QU(2, "aa2", "bb2"); + QC(2, PolicyEffect::PERMIT); + + QU(3, "a1", "d3"); + QC(3, PolicyEffect::DENY); + + SettingsLogic::updateDevCapSetting(d3, Preference::PREFERENCE_DEFAULT); + + QC(1, PolicyEffect::PERMIT); + + QC(2, PolicyEffect::PERMIT); + + QC(3, PolicyEffect::DENY); + + SettingsLogic::updateDevCapSetting(d3, Preference::PREFERENCE_DENY); + + QC(1, PolicyEffect::DENY); + + QC(3, PolicyEffect::DENY); + + SettingsLogic::updateDevCapSetting(d3, Preference::PREFERENCE_PERMIT); + + QC(1, PolicyEffect::PERMIT); + + QC(3, PolicyEffect::DENY); +} + diff --git a/tests/ace/TestSuite02.cpp b/tests/ace/TestSuite02.cpp new file mode 100644 index 0000000..66d79f5 --- /dev/null +++ b/tests/ace/TestSuite02.cpp @@ -0,0 +1,913 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file TestSuite02.cpp + * @author unknown + * @version 1.0 + * @brief Test cases for Attribute class. + */ +#include + +#include +#include + +#include +#include + +#include + +#include "PEPSingleton.h" + +#define POLICY_ATTR_EXAMPLE "/usr/etc/ace/attr_policy-example.xml" +#define POLICY_ATTR_EXAMPLE1 "/usr/etc/ace/attr_policy-example1.xml" +#define POLICY_ATTR_EXAMPLE2 "/usr/etc/ace/attr_policy-example2.xml" +#define POLICY_ATTR_EXAMPLE3 "/usr/etc/ace/attr_policy-example3.xml" +#define POLICY_ATTR_EXAMPLE4 "/usr/etc/ace/attr_policy-example4.xml" +#define POLICY_ATTR_EXAMPLE5 "/usr/etc/ace/attr_policy-example5.xml" +#define POLICY_ATTR_EXAMPLE6 "/usr/etc/ace/attr_policy-example6.xml" +#define POLICY_ATTR_EXAMPLE7 "/usr/etc/ace/attr_policy-example7.xml" +#define POLICY_ATTR_EXAMPLE8 "/usr/etc/ace/attr_policy-example8.xml" + +#define CLEANENV(d) \ + do{ \ + if (PEPSingleton::Instance().getPdp()->getCurrentPolicy() != (d)) { \ + PEPSingleton::Instance().getPdp()->updatePolicy(d); \ + } \ + }while(0) + +#define PEPTR PEPSingleton::Instance().getPdp() + +#define TESTSUITE02(n) \ +RUNNER_TEST(ts02_extract_attributes_tests_ ## n) + +using namespace AceDB; + +bool AttrEqual(const AttributeSet &actual, AttributeSet * expected) +{ + bool match = false; + for (AttributeSet::const_iterator ita = actual.begin(); + ita != actual.end(); + ++ita) + { + match = false; + for (AttributeSet::const_iterator ite = expected->begin(); + ite != expected->end(); + ++ite) + { + if ((*(*ita)->getName() == *(*ite)->getName()) && + ((*ita)->getType() == (*ite)->getType())) + { + match = true; + } + } + if (!match) + { + LogError("Not found " << + *(*ita)->getName() << + " " << static_cast((*ita)->getType())); + return false; + } + } + return true; +} + +TESTSUITE02(01){ + CLEANENV(POLICY_ATTR_EXAMPLE); + + AttributeSet attrSet; + std::string n1("name"); + BaseAttributePtr a1(new Attribute(&n1, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a1); + + + std::string n2("resource-id"); + BaseAttributePtr a2(new Attribute(&n2, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a2); + + PEPTR->extractAttributesTest(); + + bool result = AttrEqual(attrSet, PEPTR->getAttributeSet()); + RUNNER_ASSERT(result); +} + +TESTSUITE02(02){ + CLEANENV(POLICY_ATTR_EXAMPLE1); + + AttributeSet attrSet; + + std::string n3("uri.host"); + BaseAttributePtr a3(new Attribute(&n3, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a3); + + std::string n4("key-root-trust"); + BaseAttributePtr a4(new Attribute(&n4, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a4); + + std::string n5("id"); + BaseAttributePtr a5(new Attribute(&n5, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a5); + + std::string n6("signer-id"); + BaseAttributePtr a6(new Attribute(&n6, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a6); + + std::string n7("version"); + BaseAttributePtr a7(new Attribute(&n7, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a7); + + std::string n8("r8v2"); + BaseAttributePtr a8(new Attribute(&n8, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a8); + + std::string n9("author"); + BaseAttributePtr a9(new Attribute(&n9, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a9); + + std::string n10("r9a.scheme"); + BaseAttributePtr a10(new Attribute(&n10, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a10); + + std::string n11("r9b.authority"); + BaseAttributePtr a11(new Attribute(&n11, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a11); + + std::string n12("r9c.scheme-authority"); + BaseAttributePtr a12(new Attribute(&n12, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a12); + + PEPTR->extractAttributesTest(); + + bool result = AttrEqual(attrSet, PEPTR->getAttributeSet()); + RUNNER_ASSERT(result); +} + +TESTSUITE02(03){ + CLEANENV(POLICY_ATTR_EXAMPLE2); + + AttributeSet attrSet; + + std::string n1("id"); + BaseAttributePtr a1(new Attribute(&n1, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a1); + + std::string n3("version"); + BaseAttributePtr a3(new Attribute(&n3, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a3); + + std::string n2("resource-id"); + BaseAttributePtr a2(new Attribute(&n2, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a2); + + std::string n4("author"); + BaseAttributePtr a4(new Attribute(&n4, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a4); + + PEPTR->extractAttributesTest(); + + bool result = AttrEqual(attrSet, PEPTR->getAttributeSet()); + RUNNER_ASSERT(result); +} + +TESTSUITE02(04){ + CLEANENV(POLICY_ATTR_EXAMPLE3); + + AttributeSet attrSet; + + std::string n1("id"); + BaseAttributePtr a1(new Attribute(&n1, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a1); + + std::string n3("version"); + BaseAttributePtr a3(new Attribute(&n3, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a3); + + std::string n2("resource-id"); + BaseAttributePtr a2(new Attribute(&n2, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a2); + + std::string n4("author"); + BaseAttributePtr a4(new Attribute(&n4, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a4); + + PEPTR->extractAttributesTest(); + + bool result = AttrEqual(attrSet, PEPTR->getAttributeSet()); + RUNNER_ASSERT(result); +} + +TESTSUITE02(05){ + CLEANENV(POLICY_ATTR_EXAMPLE4); + + AttributeSet attrSet; + + std::string n1("id"); + BaseAttributePtr a1(new Attribute(&n1, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a1); + + std::string n3("version"); + BaseAttributePtr a3(new Attribute(&n3, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a3); + + + std::string n2("resource-id"); + BaseAttributePtr a2(new Attribute(&n2, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a2); + + std::string n4("author"); + BaseAttributePtr a4(new Attribute(&n4, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a4); + + std::string n5("author"); + BaseAttributePtr a5(new Attribute(&n5, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a5); + + std::string n6("version"); + BaseAttributePtr a6(new Attribute(&n6, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a6); + + std::string n7("ver"); + BaseAttributePtr a7(new Attribute(&n7, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a7); + + PEPTR->extractAttributesTest(); + + bool result = AttrEqual(attrSet, PEPTR->getAttributeSet()); + RUNNER_ASSERT(result); +} + +TESTSUITE02(06){ + CLEANENV(POLICY_ATTR_EXAMPLE5); + + AttributeSet attrSet; + + std::string n3("uri.host"); + BaseAttributePtr a3(new Attribute(&n3, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a3); + + std::string n4("key-root-trust"); + BaseAttributePtr a4(new Attribute(&n4, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a4); + + std::string n5("id"); + BaseAttributePtr a5(new Attribute(&n5, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a5); + + std::string n6("signer-id"); + BaseAttributePtr a6(new Attribute(&n6, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a6); + + std::string n7("version"); + BaseAttributePtr a7(new Attribute(&n7, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a7); + + std::string n13("name"); + BaseAttributePtr a13(new Attribute(&n13, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a13); + + std::string n14("resource-id"); + BaseAttributePtr a14(new Attribute(&n14, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a14); + + std::string n15("r8v2"); + BaseAttributePtr a15(new Attribute(&n15, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a15); + + std::string n16("author"); + BaseAttributePtr a16(new Attribute(&n16, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a16); + + std::string n17("r9a.scheme"); + BaseAttributePtr a17(new Attribute(&n17, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a17); + + std::string n18("r9b.authority"); + BaseAttributePtr a18(new Attribute(&n18, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a18); + + std::string n19("r9c.scheme-authority"); + BaseAttributePtr a19(new Attribute(&n19, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a19); + + std::string n20("resource-id"); + BaseAttributePtr a20(new Attribute(&n20, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a20); + + std::string n8("r8v2"); + BaseAttributePtr a8(new Attribute(&n8, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a8); + + std::string n9("author"); + BaseAttributePtr a9(new Attribute(&n9, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a9); + + std::string n10("r9a.scheme"); + BaseAttributePtr a10(new Attribute(&n10, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a10); + + std::string n11("r9b.authority"); + BaseAttributePtr a11(new Attribute(&n11, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a11); + + std::string n12("r9c.scheme-authority"); + BaseAttributePtr a12(new Attribute(&n12, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a12); + + std::string n21("resource-id"); + BaseAttributePtr a21(new Attribute(&n21, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a21); + + std::string n22("r8v2"); + BaseAttributePtr a22(new Attribute(&n22, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a22); + + std::string n23("author"); + BaseAttributePtr a23(new Attribute(&n23, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a23); + + std::string n24("r9a.scheme"); + BaseAttributePtr a24(new Attribute(&n24, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a24); + + std::string n25("r9b.authority"); + BaseAttributePtr a25(new Attribute(&n25, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a25); + + std::string n26("r9c.scheme-authority"); + BaseAttributePtr a26(new Attribute(&n26, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a26); + + PEPTR->extractAttributesTest(); + + bool result = AttrEqual(attrSet, PEPTR->getAttributeSet()); + RUNNER_ASSERT(result); +} + +TESTSUITE02(07){ + CLEANENV(POLICY_ATTR_EXAMPLE6); + + AttributeSet attrSet; + + std::string n3("s-uri.host"); + BaseAttributePtr a3(new Attribute(&n3, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a3); + + std::string n4("s-key-root-trust"); + BaseAttributePtr a4(new Attribute(&n4, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a4); + + std::string n5("s-id"); + BaseAttributePtr a5(new Attribute(&n5, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a5); + + std::string n6("s-signer-id"); + BaseAttributePtr a6(new Attribute(&n6, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a6); + + std::string n7("s-version"); + BaseAttributePtr a7(new Attribute(&n7, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a7); + + std::string n13("s-name"); + BaseAttributePtr a13(new Attribute(&n13, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a13); + + std::string n14("s-resource-id"); + BaseAttributePtr a14(new Attribute(&n14, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a14); + + std::string n15("s-r8v2"); + BaseAttributePtr a15(new Attribute(&n15, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a15); + + std::string n16("s-author"); + BaseAttributePtr a16(new Attribute(&n16, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a16); + + std::string n17("s-r9a.scheme"); + BaseAttributePtr a17(new Attribute(&n17, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a17); + + std::string n18("s-r9b.authority"); + BaseAttributePtr a18(new Attribute(&n18, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a18); + + std::string n19("s-r9c.scheme-authority"); + BaseAttributePtr a19(new Attribute(&n19, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a19); + + std::string n20("r-resource-id"); + BaseAttributePtr a20(new Attribute(&n20, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a20); + + std::string n8("r-r8v2"); + BaseAttributePtr a8(new Attribute(&n8, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a8); + + std::string n9("r-author"); + BaseAttributePtr a9(new Attribute(&n9, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a9); + + std::string n10("r-r9a.scheme"); + BaseAttributePtr a10(new Attribute(&n10, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a10); + + std::string n11("r-r9b.authority"); + BaseAttributePtr a11(new Attribute(&n11, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a11); + + std::string n12("r-r9c.scheme-authority"); + BaseAttributePtr a12(new Attribute(&n12, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a12); + + std::string n21("e-resource-id"); + BaseAttributePtr a21(new Attribute(&n21, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a21); + + std::string n22("e-r8v2"); + BaseAttributePtr a22(new Attribute(&n22, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a22); + + std::string n23("e-author"); + BaseAttributePtr a23(new Attribute(&n23, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a23); + + std::string n24("e-r9a.scheme"); + BaseAttributePtr a24(new Attribute(&n24, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a24); + + std::string n25("e-r9b.authority"); + BaseAttributePtr a25(new Attribute(&n25, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a25); + + std::string n26("e-r9c.scheme-authority"); + BaseAttributePtr a26(new Attribute(&n26, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a26); + + PEPTR->extractAttributesTest(); + + bool result = AttrEqual(attrSet, PEPTR->getAttributeSet()); + RUNNER_ASSERT(result); +} + +TESTSUITE02(08){ + CLEANENV(POLICY_ATTR_EXAMPLE2); + + AttributeSet attrSet; + + std::string n1("id"); + BaseAttributePtr a1(new Attribute(&n1, + Attribute::Match::Glob, + Attribute::Type::Subject)); + attrSet.insert(a1); + + std::string n3("version"); + BaseAttributePtr a3(new Attribute(&n3, + Attribute::Match::Glob, + Attribute::Type::Subject)); + attrSet.insert(a3); + + + std::string n2("resource-id"); + BaseAttributePtr a2(new Attribute(&n2, + Attribute::Match::Glob, + Attribute::Type::Resource)); + attrSet.insert(a2); + + std::string n4("author"); + BaseAttributePtr a4(new Attribute(&n4, + Attribute::Match::Glob, + Attribute::Type::Resource)); + attrSet.insert(a4); + + PEPTR->extractAttributesTest(); + + bool result = AttrEqual(attrSet, PEPTR->getAttributeSet()); + RUNNER_ASSERT(result); +} + +TESTSUITE02(09){ + CLEANENV(POLICY_ATTR_EXAMPLE2); + + AttributeSet attrSet; + + std::string n1("id"); + BaseAttributePtr a1(new Attribute(&n1, + Attribute::Match::Regexp, + Attribute::Type::Subject)); + attrSet.insert(a1); + + std::string n3("version"); + BaseAttributePtr a3(new Attribute(&n3, + Attribute::Match::Regexp, + Attribute::Type::Subject)); + attrSet.insert(a3); + + + std::string n2("resource-id"); + BaseAttributePtr a2(new Attribute(&n2, + Attribute::Match::Regexp, + Attribute::Type::Resource)); + attrSet.insert(a2); + + std::string n4("author"); + BaseAttributePtr a4(new Attribute(&n4, + Attribute::Match::Regexp, + Attribute::Type::Resource)); + attrSet.insert(a4); + + PEPTR->extractAttributesTest(); + + bool result = AttrEqual(attrSet, PEPTR->getAttributeSet()); + RUNNER_ASSERT(result); +} + +TESTSUITE02(10){ + CLEANENV(POLICY_ATTR_EXAMPLE2); + + AttributeSet attrSet; + + std::string n1("id"); + BaseAttributePtr a1(new Attribute(&n1, + Attribute::Match::Regexp, + Attribute::Type::Subject)); + attrSet.insert(a1); + + std::string n3("version"); + BaseAttributePtr a3(new Attribute(&n3, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a3); + + + std::string n2("resource-id"); + BaseAttributePtr a2(new Attribute(&n2, + Attribute::Match::Glob, + Attribute::Type::Resource)); + attrSet.insert(a2); + + std::string n4("author"); + BaseAttributePtr a4(new Attribute(&n4, + Attribute::Match::Regexp, + Attribute::Type::Resource)); + attrSet.insert(a4); + + PEPTR->extractAttributesTest(); + + bool result = AttrEqual(attrSet, PEPTR->getAttributeSet()); + RUNNER_ASSERT(result); +} + +TESTSUITE02(11){ + CLEANENV(POLICY_ATTR_EXAMPLE7); + + AttributeSet attrSet; + + std::string n3("uri.host"); + BaseAttributePtr a3(new Attribute(&n3, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a3); + + std::string n4("key-root-trust"); + BaseAttributePtr a4(new Attribute(&n4, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a4); + + std::string n5("id"); + BaseAttributePtr a5(new Attribute(&n5, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a5); + + std::string n6("signer-id"); + BaseAttributePtr a6(new Attribute(&n6, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a6); + + std::string n7("version"); + BaseAttributePtr a7(new Attribute(&n7, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a7); + + std::string n13("name"); + BaseAttributePtr a13(new Attribute(&n13, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a13); + + std::string n14("resource-id"); + BaseAttributePtr a14(new Attribute(&n14, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a14); + + std::string n15("r8v2"); + BaseAttributePtr a15(new Attribute(&n15, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a15); + + std::string n16("author"); + BaseAttributePtr a16(new Attribute(&n16, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a16); + + std::string n17("r9a.scheme"); + BaseAttributePtr a17(new Attribute(&n17, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a17); + + std::string n18("r9b.authority"); + BaseAttributePtr a18(new Attribute(&n18, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a18); + + std::string n19("r9c.scheme-authority"); + BaseAttributePtr a19(new Attribute(&n19, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a19); + + std::string n20("resource-id"); + BaseAttributePtr a20(new Attribute(&n20, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a20); + + std::string n8("r8v2"); + BaseAttributePtr a8(new Attribute(&n8, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a8); + + std::string n9("author"); + BaseAttributePtr a9(new Attribute(&n9, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a9); + + std::string n10("r9a.scheme"); + BaseAttributePtr a10(new Attribute(&n10, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a10); + + std::string n11("r9b.authority"); + BaseAttributePtr a11(new Attribute(&n11, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a11); + + std::string n12("r9c.scheme-authority"); + BaseAttributePtr a12(new Attribute(&n12, + Attribute::Match::Equal, + Attribute::Type::Resource)); + attrSet.insert(a12); + + std::string n21("resource-id"); + BaseAttributePtr a21(new Attribute(&n21, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a21); + + std::string n22("r8v2"); + BaseAttributePtr a22(new Attribute(&n22, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a22); + + std::string n23("author"); + BaseAttributePtr a23(new Attribute(&n23, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a23); + + std::string n24("r9a.scheme"); + BaseAttributePtr a24(new Attribute(&n24, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a24); + + std::string n25("r9b.authority"); + BaseAttributePtr a25(new Attribute(&n25, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a25); + + std::string n26("r9c.scheme-authority"); + BaseAttributePtr a26(new Attribute(&n26, + Attribute::Match::Equal, + Attribute::Type::Environment)); + attrSet.insert(a26); + + PEPTR->extractAttributesTest(); + + bool result = AttrEqual(attrSet, PEPTR->getAttributeSet()); + RUNNER_ASSERT(result); +} + +TESTSUITE02(12){ + CLEANENV(POLICY_ATTR_EXAMPLE8); + + AttributeSet attrSet; + + std::string n1("id"); + BaseAttributePtr a1(new Attribute(&n1, + Attribute::Match::Regexp, + Attribute::Type::Subject)); + attrSet.insert(a1); + + std::string n3("version"); + BaseAttributePtr a3(new Attribute(&n3, + Attribute::Match::Equal, + Attribute::Type::Subject)); + attrSet.insert(a3); + + + std::string n2("resource-id"); + BaseAttributePtr a2(new Attribute(&n2, + Attribute::Match::Glob, + Attribute::Type::Resource)); + attrSet.insert(a2); + + std::string n4("author"); + BaseAttributePtr a4(new Attribute(&n4, + Attribute::Match::Regexp, + Attribute::Type::Resource)); + attrSet.insert(a4); + + PEPTR->extractAttributesTest(); + + bool result = AttrEqual(attrSet, PEPTR->getAttributeSet()); + RUNNER_ASSERT(result); +} + diff --git a/tests/ace/TestSuite03.cpp b/tests/ace/TestSuite03.cpp new file mode 100644 index 0000000..53c86ca --- /dev/null +++ b/tests/ace/TestSuite03.cpp @@ -0,0 +1,533 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file TestSuite03.cpp + * @author Piotr Fatyga (p.fatyga@samsung.com) + * @version 0.1 + * @brief Test cases for Condition class. + */ + +#include + +#include +#include + +#include +#include +#include + +#define TESTSUITE03(n) \ +RUNNER_TEST(ts03_condtitions_tests_ ## n) + +using namespace AceDB; + +BaseAttributePtr createAttribute( + const char *name, + const char * value, + const Attribute::Type type = Attribute::Type::Subject) +{ + std::string aName(name); + BaseAttributePtr attr(new Attribute(&aName, Attribute::Match::Equal, type)); + if(value != NULL){ + std::string aValue(value); + DPL::StaticPointerCast(attr)->addValue(&aValue); + } + return attr; +} + +BaseAttributePtr createAttribute( + const char *name, + const char *value, + const char *value2, + const Attribute::Type type = Attribute::Type::Subject) +{ + std::string aName(name); + BaseAttributePtr attr(new Attribute(&aName, Attribute::Match::Equal, type)); + if(value != NULL && value2 != NULL ){ + std::string aValue(value), aValue2(value2); + DPL::StaticPointerCast(attr)->addValue(&aValue); + DPL::StaticPointerCast(attr)->addValue(&aValue2); + } + return attr; +} + + +bool MatchResultEqual( + Attribute::MatchResult actual, + Attribute::MatchResult expected) +{ + return actual == expected; +} + +bool evaluateResult( + Condition con, + AttributeSet & attrs, + Attribute::MatchResult expectedResult) +{ + Attribute::MatchResult matchResult = con.evaluateCondition(&attrs); + return MatchResultEqual(matchResult ,expectedResult); +} + +TESTSUITE03(01){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1"); + BaseAttributePtr a2 = createAttribute("r8v2","var2"); + BaseAttributePtr a3 = createAttribute("author","va3"); + + con.addAttribute(*DPL::StaticPointerCast(a1)); + con.addAttribute(*DPL::StaticPointerCast(a2)); + con.addAttribute(*DPL::StaticPointerCast(a3)); + + attrSet.insert(a1); + attrSet.insert(a2); + attrSet.insert(a3); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRTrue)); +} + +TESTSUITE03(02){ + Condition con(Condition::AND); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1"); + BaseAttributePtr a2 = createAttribute("r8v2","var2"); + BaseAttributePtr a3 = createAttribute("author","va3"); + + con.addAttribute(*DPL::StaticPointerCast(a1)); + con.addAttribute(*DPL::StaticPointerCast(a2)); + con.addAttribute(*DPL::StaticPointerCast(a3)); + + attrSet.insert(a1); + attrSet.insert(a2); + attrSet.insert(a3); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRTrue)); +} + +TESTSUITE03(03){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1"); + BaseAttributePtr a2 = createAttribute("r8v2","var2"); + BaseAttributePtr a3 = createAttribute("author","va3"); + BaseAttributePtr a4 = createAttribute("version","wrongValue"); + + con.addAttribute(*DPL::StaticPointerCast(a1)); + con.addAttribute(*DPL::StaticPointerCast(a2)); + con.addAttribute(*DPL::StaticPointerCast(a3)); + + attrSet.insert(a2); + attrSet.insert(a3); + attrSet.insert(a4); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRTrue)); +} + +TESTSUITE03(04){ + Condition con(Condition::AND); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1"); + BaseAttributePtr a2 = createAttribute("r8v2","var2"); + BaseAttributePtr a3 = createAttribute("author","va3"); + BaseAttributePtr a4 = createAttribute("version","bad"); + BaseAttributePtr a5 = createAttribute("version","var1", Attribute::Type::Resource); + + con.addAttribute(*DPL::StaticPointerCast(a1)); + con.addAttribute(*DPL::StaticPointerCast(a2)); + con.addAttribute(*DPL::StaticPointerCast(a3)); + + attrSet.insert(a2); + attrSet.insert(a3); + attrSet.insert(a4); + attrSet.insert(a5); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRFalse)); +} + +TESTSUITE03(05){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1"); + BaseAttributePtr a2 = createAttribute("r8v2","var2"); + BaseAttributePtr a3 = createAttribute("author","va3"); + + BaseAttributePtr a4 = createAttribute("version","var1"); + BaseAttributePtr a5 = createAttribute("r8v2","var2"); + BaseAttributePtr a6 = createAttribute("author","va3"); + + con.addAttribute(*DPL::StaticPointerCast(a1)); + con.addAttribute(*DPL::StaticPointerCast(a2)); + con.addAttribute(*DPL::StaticPointerCast(a3)); + + attrSet.insert(a4); + attrSet.insert(a5); + attrSet.insert(a6); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRTrue)); +} + +TESTSUITE03(06){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1"); + BaseAttributePtr a2 = createAttribute("r8v2","var2"); + BaseAttributePtr a3 = createAttribute("author","va3"); + + BaseAttributePtr a4 = createAttribute("version","var2"); + BaseAttributePtr a5 = createAttribute("r8v2","var3"); + BaseAttributePtr a6 = createAttribute("author","va4"); + + con.addAttribute(*DPL::StaticPointerCast(a1)); + con.addAttribute(*DPL::StaticPointerCast(a2)); + con.addAttribute(*DPL::StaticPointerCast(a3)); + + attrSet.insert(a4); + attrSet.insert(a5); + attrSet.insert(a6); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRFalse)); +} + +TESTSUITE03(07){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1"); + BaseAttributePtr a2 = createAttribute("r8v2","var2"); + BaseAttributePtr a3 = createAttribute("author","va3"); + + BaseAttributePtr a4 = createAttribute("version","var1", Attribute::Type::Resource ); + BaseAttributePtr a5 = createAttribute("r8v2","var2", Attribute::Type::Resource); + BaseAttributePtr a6 = createAttribute("author","va3", Attribute::Type::Resource); + + BaseAttributePtr a7 = createAttribute("version","var2" ); + BaseAttributePtr a8 = createAttribute("r8v2","var3"); + BaseAttributePtr a9 = createAttribute("author","va4"); + + con.addAttribute(*DPL::StaticPointerCast(a1)); + con.addAttribute(*DPL::StaticPointerCast(a2)); + con.addAttribute(*DPL::StaticPointerCast(a3)); + + attrSet.insert(a4); + attrSet.insert(a5); + attrSet.insert(a6); + attrSet.insert(a7); + attrSet.insert(a8); + attrSet.insert(a9); + + //Despite that there are attribute a4,a5,a6 that matches the value of attributes + //a1,a2,a3 the type of the attributes is different + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRFalse)); +} + +//Test if the same attributes (but other instances) passes the test + +TESTSUITE03(08){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1"); + BaseAttributePtr a2 = createAttribute("r8v2","var2"); + BaseAttributePtr a3 = createAttribute("author","va3"); + + BaseAttributePtr a4 = createAttribute("version","var1"); + BaseAttributePtr a5 = createAttribute("r8v2","var2", Attribute::Type::Resource); + BaseAttributePtr a6 = createAttribute("author","va3", Attribute::Type::Resource); + BaseAttributePtr a7 = createAttribute("r8v2","aaa"); + BaseAttributePtr a8 = createAttribute("author","aaa"); + + con.addAttribute(*DPL::StaticPointerCast(a1)); + con.addAttribute(*DPL::StaticPointerCast(a2)); + con.addAttribute(*DPL::StaticPointerCast(a3)); + + attrSet.insert(a4); + attrSet.insert(a5); + attrSet.insert(a6); + attrSet.insert(a7); + attrSet.insert(a8); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRTrue)); +} + +TESTSUITE03(09){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version", NULL); //empty bag + con.addAttribute(*DPL::StaticPointerCast(a1)); + attrSet.insert(a1); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRFalse)); +} + +TESTSUITE03(10){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1","var2"); + + BaseAttributePtr a4 = createAttribute("version","var1"); + + con.addAttribute(*DPL::StaticPointerCast(a1)); + attrSet.insert(a4); + //It's enough that one value from string bag matches + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRTrue)); +} + +TESTSUITE03(11){ + Condition con(Condition::AND); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1","var2"); + BaseAttributePtr a2 = createAttribute("version","var3","var1"); + + BaseAttributePtr a4 = createAttribute("version","var1"); + + con.addAttribute(*DPL::StaticPointerCast(a1)); + con.addAttribute(*DPL::StaticPointerCast(a2)); + attrSet.insert(a4); + //It's enough that one value from string bag matches + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRTrue)); +} + + +TESTSUITE03(12){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1"); + BaseAttributePtr a2 = createAttribute("r8v2","var2"); + BaseAttributePtr a3 = createAttribute("author","va3"); + + BaseAttributePtr a4 = createAttribute("version","var1"); + BaseAttributePtr a5 = createAttribute("r8v2","var2", Attribute::Type::Resource); + BaseAttributePtr a6 = createAttribute("author","va3", Attribute::Type::Resource); + + con.addAttribute(*DPL::StaticPointerCast(a1)); + con.addAttribute(*DPL::StaticPointerCast(a2)); + con.addAttribute(*DPL::StaticPointerCast(a3)); + + attrSet.insert(a4); + attrSet.insert(a5); + attrSet.insert(a6); + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRTrue)); +} + +TESTSUITE03(13){ + Condition con(Condition::AND); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version",NULL); + a1->setUndetermind(true); + BaseAttributePtr a4 = createAttribute("version","var1"); + con.addAttribute(*DPL::StaticPointerCast(a4)); + attrSet.insert(a1); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRUndetermined)); +} + +TESTSUITE03(14){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version",NULL); + a1->setUndetermind(true); + BaseAttributePtr a2 = createAttribute("author","good"); + BaseAttributePtr a4 = createAttribute("version","var1"); + con.addAttribute(*DPL::StaticPointerCast(a4)); + con.addAttribute(*DPL::StaticPointerCast(a2)); + attrSet.insert(a1); + attrSet.insert(a2); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRTrue)); +} + +TESTSUITE03(15){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version",NULL,NULL); + a1->setUndetermind(true); + BaseAttributePtr a4 = createAttribute("version","var1"); + con.addAttribute(*DPL::StaticPointerCast(a4)); + attrSet.insert(a1); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRUndetermined)); +} + +TESTSUITE03(16){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1","aaa"); + a1->setUndetermind(true); + BaseAttributePtr a4 = createAttribute("version","var1"); + con.addAttribute(*DPL::StaticPointerCast(a4)); + attrSet.insert(a1); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRUndetermined)); +} + +TESTSUITE03(17){ + Condition con(Condition::AND); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1"); + BaseAttributePtr a2 = createAttribute("version1","var1"); + BaseAttributePtr a3 = createAttribute("version2","var1"); + BaseAttributePtr a4 = createAttribute("version3","var1"); + BaseAttributePtr a5 = createAttribute("version1","var2"); + + Condition con2(Condition::AND); + con2.addAttribute(*DPL::StaticPointerCast(a2)); + Condition con3(Condition::OR); + con3.addAttribute(*DPL::StaticPointerCast(a3)); + con3.addAttribute(*DPL::StaticPointerCast(a4)); + + con.addCondition(con2); + con.addCondition(con3); + + con.addAttribute(*DPL::StaticPointerCast(a4)); + attrSet.insert(a1); + attrSet.insert(a3); + attrSet.insert(a4); + attrSet.insert(a5); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRFalse)); +} + +TESTSUITE03(18){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1"); + BaseAttributePtr a2 = createAttribute("version1","var1"); + BaseAttributePtr a3 = createAttribute("version2","var1"); + BaseAttributePtr a4 = createAttribute("version3","var1"); + BaseAttributePtr a5 = createAttribute("version1","var2"); + + Condition con2(Condition::AND); + con2.addAttribute(*DPL::StaticPointerCast(a2)); + con2.addAttribute(*DPL::StaticPointerCast(a5)); + Condition con3(Condition::AND); + con3.addAttribute(*DPL::StaticPointerCast(a3)); + con3.addAttribute(*DPL::StaticPointerCast(a4)); + Condition con4(Condition::AND); + con4.addAttribute(*DPL::StaticPointerCast(a2)); + + con3.addCondition(con4); + con.addCondition(con2); + con.addCondition(con3); + + attrSet.insert(a1); + attrSet.insert(a3); + attrSet.insert(a4); + attrSet.insert(a5); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRFalse)); +} + +TESTSUITE03(19){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1"); + BaseAttributePtr a2 = createAttribute("version1","var1"); + BaseAttributePtr a3 = createAttribute("version2","var1"); + BaseAttributePtr a4 = createAttribute("version3","var1"); + BaseAttributePtr a5 = createAttribute("version1","var2"); + + Condition con2(Condition::AND); + con2.addAttribute(*DPL::StaticPointerCast(a2)); + con2.addAttribute(*DPL::StaticPointerCast(a5)); + Condition con3(Condition::OR); + con3.addAttribute(*DPL::StaticPointerCast(a3)); + con3.addAttribute(*DPL::StaticPointerCast(a4)); + + con.addCondition(con2); + con.addCondition(con3); + + attrSet.insert(a1); + attrSet.insert(a3); + attrSet.insert(a4); + attrSet.insert(a5); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRTrue)); +} + +TESTSUITE03(20){ + Condition con(Condition::OR); + AttributeSet attrSet; + + BaseAttributePtr a1 = createAttribute("version","var1"); + BaseAttributePtr a2 = createAttribute("version1","var1"); + BaseAttributePtr a3 = createAttribute("version2","var1"); + BaseAttributePtr a4 = createAttribute("version3","var1"); + + BaseAttributePtr a5 = createAttribute("version1","var2"); + BaseAttributePtr a6 = createAttribute("version2","var2"); + BaseAttributePtr a7 = createAttribute("version3","var2"); + BaseAttributePtr a8 = createAttribute("version4","var2"); + + Condition con2(Condition::OR); + Condition con3(Condition::OR); + Condition con4(Condition::OR); + Condition con5(Condition::OR); + + con2.addAttribute(*DPL::StaticPointerCast(a2)); + con3.addAttribute(*DPL::StaticPointerCast(a3)); + con4.addAttribute(*DPL::StaticPointerCast(a4)); + con5.addAttribute(*DPL::StaticPointerCast(a1)); + + con2.addCondition(con4); + con3.addCondition(con5); + con.addCondition(con2); + con.addCondition(con3); + + attrSet.insert(a1); + attrSet.insert(a5); + attrSet.insert(a6); + attrSet.insert(a7); + attrSet.insert(a8); + + RUNNER_ASSERT( + evaluateResult(con,attrSet,Attribute::MatchResult::MRTrue)); +} diff --git a/tests/ace/TestSuite04.cpp b/tests/ace/TestSuite04.cpp new file mode 100644 index 0000000..4cbdf1c --- /dev/null +++ b/tests/ace/TestSuite04.cpp @@ -0,0 +1,627 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file TestSuite04.cpp + * @author unknown + * @version 1.0 + * @brief Test cases for Combiner. + */ + +#include +#include + +#include +#include + +#include +#include +#include + +#define TESTSUITE04(n) \ +RUNNER_TEST(ts04_combiner_tests_ ## n) + +bool assertEffectEqual(Effect actual, Effect expected){ + return (actual == expected); +} + +class WTF : public CombinerImpl{ +public: + // the evaluation functions should be static and public + // but they are protected methods! + Effect denyOverrides(std::list &lst){ + return CombinerImpl::denyOverrides(lst); + } + Effect permitOverrides(std::list &lst){ + return CombinerImpl::permitOverrides(lst); + } + Effect firstApplicable(std::list &lst){ + return CombinerImpl::firstApplicable(lst); + } + Effect firstMatchingTarget(std::list &lst){ + return CombinerImpl::firstMatchingTarget(lst); + } +}; + +Effect denyOverridesTest(std::list &lst) { + WTF impl; + return impl.denyOverrides(lst); +} + +Effect permitOverridesTest(std::list &lst) { + WTF impl; + return impl.permitOverrides(lst); +} + +Effect firstApplicableTest(std::list &lst) { + WTF impl; + return impl.firstApplicable(lst); +} + +Effect firstMatchingTargetTest(std::list &lst) { + WTF impl; + return impl.firstMatchingTarget(lst); +} + +TESTSUITE04(00_denyOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Deny); + effectList->push_back(Undetermined); + effectList->push_back(Permit); + effectList->push_back(Inapplicable); + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Deny)); +} + +TESTSUITE04(01_denyOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Undetermined); + effectList->push_back(Permit); + effectList->push_back(Inapplicable); + effectList->push_back(Deny); + effectList->push_back(Permit); + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Deny)); +} + +TESTSUITE04(02_denyOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Undetermined); + effectList->push_back(Permit); + effectList->push_back(Inapplicable); + effectList->push_back(Permit); + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Undetermined)); +} + +TESTSUITE04(03_denyOverrides){ + std::list * effectList = new std::list (); + + effectList->push_back(Permit); + effectList->push_back(Inapplicable); + effectList->push_back(Permit); + effectList->push_back(Undetermined); + effectList->push_back(PromptSession); + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Undetermined)); +} + +TESTSUITE04(04_denyOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Permit); + effectList->push_back(Inapplicable); + effectList->push_back(Permit); + effectList->push_back(PromptOneShot); + effectList->push_back(PromptSession); + effectList->push_back(PromptBlanket); + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, PromptOneShot)); +} + +TESTSUITE04(05_denyOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Permit); + effectList->push_back(Inapplicable); + effectList->push_back(Permit); + effectList->push_back(PromptSession); + effectList->push_back(PromptBlanket); + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, PromptSession)); +} + +TESTSUITE04(06_denyOverrides){ + std::list * effectList = new std::list (); + + effectList->push_back(Permit); + effectList->push_back(Inapplicable); + effectList->push_back(Permit); + effectList->push_back(PromptBlanket); + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, PromptBlanket)); +} + +TESTSUITE04(07_denyOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Permit); + effectList->push_back(Inapplicable); + effectList->push_back(Permit); + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Permit)); +} + +TESTSUITE04(08_denyOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Inapplicable); + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Inapplicable)); +} + +TESTSUITE04(09_denyOverrides){ + std::list * effectList = new std::list (); + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Inapplicable)); +} + +TESTSUITE04(10_denyOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Deny); + effectList->push_back(Undetermined); + effectList->push_back(Permit); + effectList->push_back(Error); + effectList->push_back(Inapplicable); + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(11_denyOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Error); + effectList->push_back(Undetermined); + effectList->push_back(Error); + effectList->push_back(Inapplicable); + effectList->push_back(Permit); + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(12_denyOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Permit); + effectList->push_back(Inapplicable); + effectList->push_back(Permit); + effectList->push_back(Error); + effectList->push_back(PromptOneShot); + effectList->push_back(PromptSession); + effectList->push_back(PromptBlanket); + + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(13_denyOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Permit); + effectList->push_back(Inapplicable); + effectList->push_back(Permit); + effectList->push_back(Error); + effectList->push_back(PromptBlanket); + effectList->push_back(PromptSession); + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(14_denyOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Error); + effectList->push_back(Inapplicable); + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(15_denyOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Inapplicable); + effectList->push_back(Error); + + Effect eff = denyOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(16_permitOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Deny); + effectList->push_back(Undetermined); + effectList->push_back(Permit); + effectList->push_back(Inapplicable); + effectList->push_back(PromptOneShot); + effectList->push_back(PromptSession); + effectList->push_back(PromptBlanket); + + Effect eff = permitOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Permit)); +} + +TESTSUITE04(17_permitOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Deny); + effectList->push_back(Inapplicable); + effectList->push_back(PromptOneShot); + effectList->push_back(PromptSession); + effectList->push_back(PromptBlanket); + effectList->push_back(Undetermined); + + Effect eff = permitOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Undetermined)); +} + +TESTSUITE04(18_permitOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Deny); + effectList->push_back(Inapplicable); + effectList->push_back(PromptOneShot); + effectList->push_back(PromptSession); + effectList->push_back(PromptBlanket); + + + Effect eff = permitOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, PromptBlanket)); +} + +TESTSUITE04(19_permitOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Deny); + effectList->push_back(PromptOneShot); + effectList->push_back(PromptSession); + effectList->push_back(Inapplicable); + + Effect eff = permitOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, PromptSession)); +} + +TESTSUITE04(20_permitOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Deny); + effectList->push_back(Inapplicable); + effectList->push_back(PromptOneShot); + + Effect eff = permitOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, PromptOneShot)); +} + +TESTSUITE04(21_permitOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Deny); + effectList->push_back(Inapplicable); + + Effect eff = permitOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Deny)); +} + +TESTSUITE04(22_permitOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Inapplicable); + + Effect eff = permitOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Inapplicable)); +} + +TESTSUITE04(23_permitOverrides){ + std::list * effectList = new std::list (); + + Effect eff = permitOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Inapplicable)); +} + +TESTSUITE04(24_permitOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Deny); + effectList->push_back(Undetermined); + effectList->push_back(Permit); + effectList->push_back(Inapplicable); + effectList->push_back(Error); + effectList->push_back(PromptSession); + effectList->push_back(PromptOneShot); + effectList->push_back(PromptBlanket); + + Effect eff = permitOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(25_permitOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Deny); + effectList->push_back(Inapplicable); + effectList->push_back(PromptOneShot); + effectList->push_back(PromptSession); + effectList->push_back(PromptBlanket); + effectList->push_back(Undetermined); + effectList->push_back(Error); + + Effect eff = permitOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(26_permitOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Deny); + effectList->push_back(Error); + effectList->push_back(Inapplicable); + effectList->push_back(PromptOneShot); + + Effect eff = permitOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(27_permitOverrides){ + std::list * effectList = new std::list (); + effectList->push_back(Error); + Effect eff = permitOverridesTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(28_firstApplicable){ + std::list * effectList = new std::list (); + effectList->push_back(Deny); + effectList->push_back(Inapplicable); + effectList->push_back(PromptOneShot); + effectList->push_back(PromptSession); + effectList->push_back(PromptBlanket); + effectList->push_back(Undetermined); + + Effect eff = firstApplicableTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Deny)); +} + +TESTSUITE04(29_firstApplicable){ + std::list * effectList = new std::list (); + effectList->push_back(Undetermined); + effectList->push_back(Deny); + effectList->push_back(PromptOneShot); + effectList->push_back(Inapplicable); + effectList->push_back(PromptOneShot); + effectList->push_back(PromptSession); + effectList->push_back(PromptBlanket); + effectList->push_back(Undetermined); + + Effect eff = firstApplicableTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Undetermined)); +} + +TESTSUITE04(30_firstApplicable){ + std::list * effectList = new std::list (); + effectList->push_back(Inapplicable); + effectList->push_back(Deny); + effectList->push_back(PromptOneShot); + effectList->push_back(Inapplicable); + effectList->push_back(PromptOneShot); + effectList->push_back(PromptSession); + effectList->push_back(PromptBlanket); + effectList->push_back(Undetermined); + + Effect eff = firstApplicableTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Deny)); +} + +TESTSUITE04(31_firstApplicable){ + std::list * effectList = new std::list (); + effectList->push_back(Inapplicable); + effectList->push_back(Undetermined); + effectList->push_back(PromptOneShot); + effectList->push_back(Inapplicable); + effectList->push_back(PromptOneShot); + effectList->push_back(PromptSession); + effectList->push_back(PromptBlanket); + effectList->push_back(Undetermined); + + Effect eff = firstApplicableTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Undetermined)); +} + +TESTSUITE04(32_firstApplicable){ + std::list * effectList = new std::list (); + effectList->push_back(Inapplicable); + effectList->push_back(Inapplicable); + effectList->push_back(Inapplicable); + + Effect eff = firstApplicableTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Inapplicable)); +} + +TESTSUITE04(33_firstApplicable){ + std::list * effectList = new std::list (); + effectList->push_back(Inapplicable); + effectList->push_back(Inapplicable); + effectList->push_back(Inapplicable); + effectList->push_back(Permit); + + Effect eff = firstApplicableTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Permit)); +} + +TESTSUITE04(34_firstApplicable){ + std::list * effectList = new std::list (); + + Effect eff = firstApplicableTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Inapplicable)); +} + +TESTSUITE04(35_firstApplicable){ + std::list * effectList = new std::list (); + effectList->push_back(Deny); + effectList->push_back(Inapplicable); + effectList->push_back(PromptOneShot); + effectList->push_back(Error); + effectList->push_back(PromptSession); + effectList->push_back(PromptBlanket); + effectList->push_back(Undetermined); + + Effect eff = firstApplicableTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(36_firstApplicable){ + std::list * effectList = new std::list (); + effectList->push_back(Inapplicable); + effectList->push_back(Inapplicable); + effectList->push_back(Inapplicable); + effectList->push_back(Error); + effectList->push_back(Permit); + + Effect eff = firstApplicableTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(37_firstApplicable){ + std::list * effectList = new std::list (); + effectList->push_back(Error); + + Effect eff = firstApplicableTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(38_firstMatching){ + std::list * effectList = new std::list (); + effectList->push_back(Inapplicable); + effectList->push_back(Deny); + effectList->push_back(PromptOneShot); + effectList->push_back(Inapplicable); + + Effect eff = firstMatchingTargetTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Inapplicable)); +} + +TESTSUITE04(39_firstMatching){ + std::list * effectList = new std::list (); + effectList->push_back(Undetermined); + effectList->push_back(Deny); + effectList->push_back(PromptOneShot); + effectList->push_back(Inapplicable); + + Effect eff = firstMatchingTargetTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Undetermined)); +} + +TESTSUITE04(39a_firstMatching){ + std::list * effectList = new std::list (); + effectList->push_back(Deny); + effectList->push_back(Inapplicable); + + Effect eff = firstMatchingTargetTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Deny)); +} + +TESTSUITE04(39b_firstMatching){ + std::list * effectList = new std::list (); + effectList->push_back(Permit); + + Effect eff = firstMatchingTargetTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Permit)); +} + +TESTSUITE04(40_firstMatching){ + std::list * effectList = new std::list (); + effectList->push_back(Undetermined); + effectList->push_back(Undetermined); + effectList->push_back(Undetermined); + + Effect eff = firstMatchingTargetTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Undetermined)); +} + +TESTSUITE04(41_firstMatching){ + std::list * effectList = new std::list (); + + Effect eff = firstMatchingTargetTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Inapplicable)); +} + +TESTSUITE04(42_firstMatching){ + std::list * effectList = new std::list (); + effectList->push_back(Inapplicable); + + Effect eff = firstMatchingTargetTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Inapplicable)); +} + +TESTSUITE04(43_firstMatching){ + + std::list * effectList = new std::list (); + effectList->push_back(Undetermined); + effectList->push_back(Undetermined); + effectList->push_back(Undetermined); + effectList->push_back(Deny); + effectList->push_back(Inapplicable); + effectList->push_back(Undetermined); + effectList->push_back(Deny); + effectList->push_back(PromptOneShot); + effectList->push_back(Inapplicable); + + Effect eff = firstMatchingTargetTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Undetermined)); +} + +TESTSUITE04(44_firstMatching){ + + std::list * effectList = new std::list (); + effectList->push_back(Inapplicable); + effectList->push_back(Deny); + effectList->push_back(PromptOneShot); + effectList->push_back(Error); + effectList->push_back(Inapplicable); + + Effect eff = firstMatchingTargetTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(45_firstMatching){ + + std::list * effectList = new std::list (); + effectList->push_back(Undetermined); + effectList->push_back(Undetermined); + effectList->push_back(Error); + effectList->push_back(Undetermined); + effectList->push_back(Deny); + effectList->push_back(Inapplicable); + effectList->push_back(Undetermined); + effectList->push_back(Deny); + effectList->push_back(PromptOneShot); + effectList->push_back(Inapplicable); + + Effect eff = firstMatchingTargetTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} + +TESTSUITE04(46_firstMatching){ + + std::list * effectList = new std::list (); + effectList->push_back(Error); + + Effect eff = firstMatchingTargetTest(*effectList); + RUNNER_ASSERT(assertEffectEqual(eff, Error)); +} diff --git a/tests/ace/TestSuite05.cpp b/tests/ace/TestSuite05.cpp new file mode 100644 index 0000000..57eb061 --- /dev/null +++ b/tests/ace/TestSuite05.cpp @@ -0,0 +1,620 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file TestSuite05.cpp + * @author unknown + * @version 1.0 + * @brief Test cases for Attribute class. + */ + +#include +#include +#include +#include + +#include + +#include +#include + +#define TESTSUITE05(n) \ +RUNNER_TEST(ts05_attr_tests_ ## n) + +class AT : public Attribute { +public: + AT(std::string nm) + : Attribute(nm) + {} + + static std::string* uriAuthorityStatic(const std::string *input) { + AT at("Micky"); + return at.uriAuthority(input); + } + + static std::string* uriHostStatic(const std::string *input) { + AT at("Pluto"); + return at.uriHost(input); + } + + static std::string* uriSchemeStatic(const std::string *input) { + AT at("Donald"); + return at.uriScheme(input); + } + + static std::string* uriSchemeAuthorityStatic(const std::string *input) { + AT at("Winnie the Pooh"); + return at.uriSchemeAuthority(input); + } + + static std::string* uriPathStatic(const std::string *input) { + AT at("Hannibal"); + return at.uriPath(input); + } + + static bool markTest(){ + bool result = true; + for(int i =0; i<128; ++i){ + if( i == '-' || i == '_' || i == '.'|| i == '!'|| i == '~' || i == '*' || i == '\'' || i == ')' || i == '(' ){ + if (!mark[i]){ + result = false; + break; + } + } + else{ + if(mark[i]){ + result =false; + break; + } + } + } + return result; + } + + static bool digitTest(){ + bool result = true; + for(int i =0; i<128; ++i){ + if( i > 47 && i < 58 ){ + if (!digit[i]){ + result = false; + break; + } + } + else{ + if(digit[i]){ + result =false; + break; + } + } + } + return result; + } + + static bool alphaTest(){ + bool result = true; + for(int i =0; i<128; ++i){ + if( ( i>64 && i<91 ) || ( i>96 && i<123 ) ) { + if (!alpha[i]){ + result = false; + break; + } + } + else{ + if(alpha[i]){ + result =false; + break; + } + } + } + return result; + } + + static bool isEscapedStatic(const char esc[3]) { + AT at("Swinka"); + return at.isEscaped(esc); + } +}; + +bool assertEqual(const std::string * actual, const char * intended) { + if (actual == NULL || intended == NULL) { + if (intended == NULL && actual == NULL) { + return true; + } + else { + return false; + } + } + + std::string temp(intended); + + if (temp == *actual) { + return true; + } + else { + return false; + } +} + +bool assertTrue(bool condition){ + return condition; +} + +bool assertFalse(bool condition){ + return !condition; +} + +TESTSUITE05(01_uriAuthority){ + std::string * outcome = NULL; + std::string query("http://www.wp.pl"); + outcome = AT::uriAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "www.wp.pl")); + delete outcome; +} + +TESTSUITE05(02_uriAuthority){ + std::string * outcome = NULL; + std::string query("http://authority?path/asdf"); + outcome = AT::uriAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "authority")); + delete outcome; +} + +TESTSUITE05(03_uriAuthority){ + std::string * outcome = NULL; + std::string query("abcd"); //This should be interpreted as schema + outcome = AT::uriAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "")); + delete outcome; +} + +TESTSUITE05(04_uriAuthority){ + std::string * outcome = NULL; + std::string query("http://authority/asdf"); + outcome = AT::uriAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "authority")); + delete outcome; +} + +TESTSUITE05(05_uriAuthority){ + std::string * outcome = NULL; + std::string query("http://user@host:20?ds"); + outcome = AT::uriAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "user@host:20")); + delete outcome; +} + +TESTSUITE05(06_uriAuthority){ + std::string * outcome = NULL; + std::string query("http://hostname:23"); + outcome = AT::uriAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "hostname:23")); + delete outcome; +} + +TESTSUITE05(07_uriAuthority){ + std::string * outcome = NULL; + std::string query("http://user@host:port"); + outcome = AT::uriAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "user@host:port")); + delete outcome; +} + +TESTSUITE05(08_uriAuthority){ + std::string * outcome = NULL; + std::string query("http://1user@host:port"); //This is a VALID URI + outcome = AT::uriAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "1user@host:port")); + delete outcome; +} + +TESTSUITE05(09_uriAuthority){ + std::string * outcome = NULL; + std::string query("http://abc%30"); //This is not a valid uri + outcome = AT::uriAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "abc%30")); + delete outcome; +} + +TESTSUITE05(10_uriAuthority){ + std::string * outcome = NULL; + std::string query("http:///asd"); //This is not a valid uri + outcome = AT::uriAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "")); + delete outcome; +} + +TESTSUITE05(11_uriAuthority){ + std::string * outcome = NULL; + std::string query("http://?asd"); //This is not a valid uri + outcome = AT::uriAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "")); + delete outcome; +} + +TESTSUITE05(12_uriAuthority){ + std::string * outcome = NULL; + std::string query("http://%34%56%67%ab"); + outcome = AT::uriAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "%34%56%67%ab")); + delete outcome; +} + +TESTSUITE05(13_uriAuthority){ + std::string * outcome = NULL; + std::string query("http://<>"); //This is not a valid uri + outcome = AT::uriAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, NULL)); + delete outcome; +} + +TESTSUITE05(14_uriAuthority){ + std::string * outcome = NULL; + std::string query("http://\\/"); //This is not a valid uri + outcome = AT::uriAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, NULL)); + delete outcome; +} + +TESTSUITE05(15_uriHost){ + std::string * outcome = NULL; + std::string query("http://user@host:23"); + outcome = AT::uriHostStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "host")); + delete outcome; +} + +TESTSUITE05(16_uriHost){ + std::string * outcome = NULL; + std::string query("http://user@host:name"); //This is not a valid uri + outcome = AT::uriHostStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "")); + delete outcome; +} + +TESTSUITE05(17_uriHost){ + std::string * outcome = NULL; + std::string query("http::::"); //This is a valid uri + outcome = AT::uriHostStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "")); + delete outcome; +} + +TESTSUITE05(18_uriHost){ + std::string * outcome = NULL; + std::string query("..%%%."); //This is not a valid uri + outcome = AT::uriHostStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, NULL)); + delete outcome; +} + +TESTSUITE05(19_uriHost){ + std::string * outcome = NULL; + std::string query("ftp://abds.eu/fda"); + outcome = AT::uriHostStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "abds.eu")); + delete outcome; +} + +TESTSUITE05(20_uriHost){ + std::string * outcome = NULL; + std::string query("abs%14ccc"); + //This is a valid uri because it's interpreted as a path not a host + outcome = AT::uriHostStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "")); + delete outcome; +} + +TESTSUITE05(21_uriHost){ + std::string * outcome = NULL; + std::string query("http://abc@123.2.23.213:982"); + outcome = AT::uriHostStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "123.2.23.213")); + delete outcome; +} + +TESTSUITE05(22_uriHost){ + std::string * outcome = NULL; + std::string query("http://abc@1233.2.23.213:982"); + //Hostname is invalid, but uri is valid + outcome = AT::uriHostStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "")); + delete outcome; +} + +TESTSUITE05(23_uriHost){ + std::string * outcome = NULL; + std::string query("http://ab%23c@host"); //Valid escaped characters + outcome = AT::uriHostStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "host")); + delete outcome; +} + +TESTSUITE05(24_uriHost){ + std::string * outcome = NULL; + std::string query("http://ab%23c@host%34"); //Invalid escaped characters in hostname + outcome = AT::uriHostStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "")); + delete outcome; +} + +TESTSUITE05(25_uriHost){ + std::string * outcome = NULL; + std::string query("http://ab%GGc@host"); //Wrong character % + outcome = AT::uriHostStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, NULL)); + delete outcome; +} + +TESTSUITE05(26_uriHost){ + std::string * outcome = NULL; + std::string query("http://www.example.pl"); + outcome = AT::uriHostStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "www.example.pl")); + delete outcome; +} + +TESTSUITE05(27_uriScheme){ + std::string * outcome = NULL; + std::string query("http://host"); + outcome = AT::uriSchemeStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "http")); + delete outcome; +} + +TESTSUITE05(28_uriScheme){ + std::string * outcome = NULL; + //Wrong character '1' in scheme , it's not an URI because two slashes are not acceptable + //in any other place than in separation between scheme and pat + std::string query("1http://host"); + outcome = AT::uriSchemeStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, NULL)); + delete outcome; +} + +TESTSUITE05(29_uriScheme){ + std::string * outcome = NULL; + std::string query("ftp+a-fdf.ads://host"); + outcome = AT::uriSchemeStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "ftp+a-fdf.ads")); + delete outcome; +} + +TESTSUITE05(30_uriScheme){ + std::string * outcome = NULL; + //Scheme cannot start with plus, it's not an URI because two slashes are not acceptable + //in any other place than in separation between scheme and path + std::string query("+++://host"); + outcome = AT::uriSchemeStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, NULL)); + delete outcome; +} + +TESTSUITE05(31_uriScheme){ + std::string * outcome = NULL; + std::string query("aaaac"); //It's a path not a scheme'a + outcome = AT::uriSchemeStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "")); + delete outcome; +} + +TESTSUITE05(32_uriScheme){ + std::string * outcome = NULL; + //no escaped characters in schema, it's not an URI because two slashes are not acceptable + //in any other place than in separation between scheme and path + std::string query("ftpa%34fdfads://host"); + outcome = AT::uriSchemeStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, NULL)); + delete outcome; +} + +TESTSUITE05(33_uriScheme){ + std::string * outcome = NULL; + //no escaped characters in schema, it's not an URI because two slashes are not acceptable + //in any other place than in separation between scheme and path + std::string query("meaninglessstring://host%34"); + outcome = AT::uriSchemeStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "meaninglessstring")); + delete outcome; +} + +TESTSUITE05(34_uriScheme){ + std::string * outcome = NULL; + std::string query("meaninglessstring2://"); + outcome = AT::uriSchemeStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "meaninglessstring2")); + delete outcome; +} + +TESTSUITE05(35_uriScheme){ + std::string * outcome = NULL; + std::string query("http://www.samsung.com/ace/bondi#5"); + outcome = AT::uriSchemeStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "http")); + delete outcome; +} + +TESTSUITE05(36_uriScheme){ + std::string * outcome = NULL; + std::string query("www.samsung.com"); + outcome = AT::uriSchemeStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "")); + delete outcome; +} + +TESTSUITE05(37_uriSchemeAuthority){ + std::string * outcome = NULL; + std::string query("http://www.samsung.com"); + outcome = AT::uriSchemeAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "http://www.samsung.com")); + delete outcome; +} + +TESTSUITE05(38_uriSchemeAuthority){ + std::string * outcome = NULL; + std::string query("ftp23://www.samsung.com/avc%23"); + outcome = AT::uriSchemeAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "ftp23://www.samsung.com")); + delete outcome; +} + +TESTSUITE05(39_uriSchemeAuthority){ + std::string * outcome = NULL; + std::string query("ftp++://anonymous@hostname:12/avc%23"); + outcome = AT::uriSchemeAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "ftp++://anonymous@hostname:12")); + delete outcome; +} + +TESTSUITE05(40_uriSchemeAuthority){ + std::string * outcome = NULL; + std::string query("32ftp://anonymous@hostname:12/avc%23"); + outcome = AT::uriSchemeAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, NULL)); + delete outcome; +} + +TESTSUITE05(41_uriSchemeAuthority){ + std::string * outcome = NULL; + std::string query("http://aaabbb?acd"); + outcome = AT::uriSchemeAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "http://aaabbb")); + delete outcome; +} + +TESTSUITE05(42_uriSchemeAuthority){ + std::string * outcome = NULL; + std::string query("http://aaabbb/acd;sdf;sdf"); + outcome = AT::uriSchemeAuthorityStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "http://aaabbb")); + delete outcome; +} + +TESTSUITE05(43_uriPath){ + std::string * outcome = NULL; + std::string query("ftp://authority//invalidpath"); + outcome = AT::uriPathStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, NULL)); + delete outcome; +} + +TESTSUITE05(44_uriPath){ + std::string * outcome = NULL; + std::string query("ftp://authority/validpath"); + outcome = AT::uriPathStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "validpath")); + delete outcome; +} + +TESTSUITE05(45_uriPath){ + std::string * outcome = NULL; + std::string query("ftp://authority/validpath;param;param"); + outcome = AT::uriPathStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "validpath;param;param")); + delete outcome; +} + +TESTSUITE05(46_uriPath){ + std::string * outcome = NULL; + std::string query("ftp://authority/validpath;param;param?query"); + outcome = AT::uriPathStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "validpath;param;param")); + delete outcome; +} + +TESTSUITE05(47_uriPath){ + std::string * outcome = NULL; + std::string query("ftp://authority/validpath;?param;param?query"); + outcome = AT::uriPathStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "validpath;")); + delete outcome; +} + +TESTSUITE05(48_uriPath){ + std::string * outcome = NULL; + std::string query("ftp://authority/validpath;param?;param?query"); + outcome = AT::uriPathStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "validpath;param")); + delete outcome; +} + +TESTSUITE05(49_uriPath){ + std::string * outcome = NULL; + std::string query("ftp://authority/valid:path?query"); + outcome = AT::uriPathStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "valid:path")); + delete outcome; +} + +TESTSUITE05(50_uriPath){ + std::string * outcome = NULL; + std::string query("ftp://authority/:::"); + outcome = AT::uriPathStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, ":::")); + delete outcome; +} + +TESTSUITE05(51_uriPath){ + std::string * outcome = NULL; + std::string query("/path1/path2?abc#fragment"); + outcome = AT::uriPathStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "path1/path2")); + delete outcome; +} + +TESTSUITE05(52_uriPath){ + std::string * outcome = NULL; + std::string query("http://www.samsung.com/normalpath/path2?query"); + outcome = AT::uriPathStatic(&query); + RUNNER_ASSERT(assertEqual(outcome, "normalpath/path2")); + delete outcome; +} + +TESTSUITE05(53_markTest){ + RUNNER_ASSERT(AT::markTest()); +} + +TESTSUITE05(54_digitTest){ + RUNNER_ASSERT(AT::digitTest()); +} + +TESTSUITE05(55_alphaTest){ + RUNNER_ASSERT(AT::alphaTest()); +} + +TESTSUITE05(56_escapedTest){ + const char * query = "%23"; + RUNNER_ASSERT(assertTrue(AT::isEscapedStatic(query))); + query = "%ab"; + RUNNER_ASSERT(assertTrue(AT::isEscapedStatic(query))); + + query = "%a"; + RUNNER_ASSERT(assertFalse(AT::isEscapedStatic(query))); + + query = "%rw"; + RUNNER_ASSERT(assertFalse(AT::isEscapedStatic(query))); + + query = NULL; + RUNNER_ASSERT(assertFalse(AT::isEscapedStatic(query))); + + query = "abc"; + RUNNER_ASSERT(assertFalse(AT::isEscapedStatic(query))); + + query = "%bc"; + RUNNER_ASSERT(assertTrue(AT::isEscapedStatic(query))); + + query = "%DF"; + RUNNER_ASSERT(assertTrue(AT::isEscapedStatic(query))); +} + diff --git a/tests/ace/TestSuite06.cpp b/tests/ace/TestSuite06.cpp new file mode 100644 index 0000000..7db0139 --- /dev/null +++ b/tests/ace/TestSuite06.cpp @@ -0,0 +1,1610 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file TestSuite06.cpp + * @author unknown + * @version 1.0 + * @brief Test cases for AceDAO implementation. + */ +#include +#include + +#include +#include + +#include +#include +#include +#include + +#define TESTSUITE06(n) \ +RUNNER_TEST(ts06_ace_dao_ ## n) + +#define CLEANENV \ + do{ \ + AceDB::AceDAO::clearWidgetDevCapSettings(); \ + AceDB::AceDAO::clearDevCapSettings(); \ + }while(0) + +using namespace AceDB; + +int operator==(const Attribute& left, const Attribute& right) +{ + return ( (*left.getName() == *right.getName())); +} + +bool UserSettingsAgreed(std::list* expected, + std::list* allRules) +{ + if (allRules->empty() && expected->empty()) + { + return true; + } + + std::list::iterator itA; + FOREACH(itE, *expected) + { + if (itE->devCap.empty()){ + return false; //some error occur + } + + for (itA = allRules->begin(); itA != allRules->end(); ++itA){ + if (itA->appId == itE->appId && + itA->devCap == itE->devCap) + { + if (itA->access == itE->access){ + break; + } + else{ + return false; //optimization + } + } + } + + if (itA == allRules->end()) + { + if (itE->access == Preference::PREFERENCE_DEFAULT) + { // this value is not set in DB + continue; + } + //if we are here -it means that expected values doesnt exist in DB + return false; + } + else + { + continue; //go to next expected attr + } + } + + //it means that all required values exist in DB + return true; +} + + +bool UserSettingNotStored(std::list* userSettings, + std::string res, + WidgetHandle handler, + Preference pref) +{ + if (userSettings->empty()) + { + return true; + } + + FOREACH(iter, *userSettings) + { + if (iter->appId == handler && iter->devCap == res){ + if (iter->access == pref){ + return false; //this value has been saved + } + else{ + continue; //value has been saved + } + } + } + + return true; +} + + + +bool ResourceSettingsAgreed( + std::list< std::pair > *expected, + std::map* allRules) +{ + std::list< std::pair >::iterator itE; + std::map::iterator itA; + + FOREACH(itE, *expected) + { + itA = allRules->find(*(itE->first)); + if (itA != allRules->end() && itA->second == itE->second) + { + continue; + } + else + { + return false; + } + } + + return true; +} + +bool ResourceSettingsEqual( + std::list< std::pair > * actual, + std::map* expected) +{ + bool flag = false; + FOREACH(iterA, *actual) + { + FOREACH(iterE, *expected) + { + if (*(iterA->first) == iterE->first && + iterA->second == iterE->second) + { + flag = true; + } + } + if (!flag) + { + return false; + } + flag = false; + } + + return true; +} + +//bool VSPAssertEqual(Verdict actual, Verdict expected){ +// return (actual == expected); +//} + + +bool AttributeEqual(AttributeSet actual, AttributeSet expected){ + return (actual == expected); +} + +bool ResourceSettingEqual(std::map* rSettings, + std::string res, + Preference pref) +{ + std::map::iterator iter = rSettings->find(res); + + if (iter != rSettings->end() && iter->second ==pref) + { + return true; + } + + return false; +} + + +bool UserSettingEqual(std::list* userSettings, + std::string res, + WidgetHandle handler, + Preference pref) +{ + if (userSettings->empty() && pref == Preference::PREFERENCE_DEFAULT) + { + return true; + } + FOREACH(iter, *userSettings) + { + if (iter->devCap.empty()) + { + return false; //some error occurred + } + if (iter->appId == handler && iter->devCap == res){ + if (iter->access == pref) + { + return true; + } + else + { + return false; + } + } + } + + return false; +} + +//Test does verdict PERMIT is saved when validity is once +TESTSUITE06(01){ + CLEANENV; + AttributeSet attributeSet; + std::string tmp("atrS"); + std::string tmpValue("Buu"); + BaseAttributePtr atr(new Attribute(&tmp, + Attribute::Match::Equal, + Attribute::Type::Subject)); + DPL::StaticPointerCast(atr)->addValue(&tmpValue); + attributeSet.insert(atr); + + AceDAO::setPolicyResult(attributeSet, PolicyEffect::PERMIT); + OptionalPolicyResult opt = AceDAO::getPolicyResult(attributeSet); + + PolicyResult effect(PolicyEffect::DENY); + if (!opt.IsNull()) { + effect = *opt; + } + + RUNNER_ASSERT(effect == PolicyEffect::PERMIT); +} + +TESTSUITE06(02){ + CLEANENV; + AttributeSet attributeSet; + std::string tmp("atrS"); + std::string tmpValue("Buu"); + BaseAttributePtr atr(new Attribute(&tmp, + Attribute::Match::Equal, + Attribute::Type::Subject)); + DPL::StaticPointerCast(atr)->addValue(&tmpValue); + attributeSet.insert(atr); + + AceDAO::setPolicyResult(attributeSet, PolicyEffect::DENY); + OptionalPolicyResult opt = AceDAO::getPolicyResult(attributeSet); + + PolicyResult effect(PolicyEffect::PERMIT); + if (!opt.IsNull()) { + effect = *opt; + } + + RUNNER_ASSERT(effect == PolicyEffect::DENY); +} + +TESTSUITE06(03){ + CLEANENV; + AttributeSet attributeSet; + std::string tmp("atrS"); + std::string tmpValue("Buu"); + BaseAttributePtr atr(new Attribute(&tmp, + Attribute::Match::Equal, + Attribute::Type::Subject)); + DPL::StaticPointerCast(atr)->addValue(&tmpValue); + attributeSet.insert(atr); + + AceDAO::setPolicyResult(attributeSet, PolicyEffect::PROMPT_ONESHOT); + OptionalPolicyResult opt = AceDAO::getPolicyResult(attributeSet); + + PolicyResult effect(PolicyEffect::DENY); + if (!opt.IsNull()) { + effect = *opt; + } + + RUNNER_ASSERT(effect == PolicyEffect::PROMPT_ONESHOT); +} + +TESTSUITE06(04){ + CLEANENV; + AttributeSet attributeSet; + std::string tmp("atrS"); + std::string tmpValue("Buu"); + BaseAttributePtr atr(new Attribute(&tmp, + Attribute::Match::Equal, + Attribute::Type::Subject)); + DPL::StaticPointerCast(atr)->addValue(&tmpValue); + attributeSet.insert(atr); + + AceDAO::setPolicyResult(attributeSet, PolicyEffect::PROMPT_SESSION); + OptionalPolicyResult opt = AceDAO::getPolicyResult(attributeSet); + + PolicyResult effect(PolicyEffect::DENY); + if (!opt.IsNull()) { + effect = *opt; + } + + RUNNER_ASSERT(effect == PolicyEffect::PROMPT_SESSION); +} + +TESTSUITE06(05){ + CLEANENV; + AttributeSet attributeSet; + std::string tmp("atrS"); + std::string tmpValue("Buu"); + BaseAttributePtr atr(new Attribute(&tmp, + Attribute::Match::Equal, + Attribute::Type::Subject)); + DPL::StaticPointerCast(atr)->addValue(&tmpValue); + attributeSet.insert(atr); + + AceDAO::setPolicyResult(attributeSet, PolicyEffect::PROMPT_BLANKET); + OptionalPolicyResult opt = AceDAO::getPolicyResult(attributeSet); + + PolicyResult effect(PolicyEffect::DENY); + if (!opt.IsNull()) { + effect = *opt; + } + + RUNNER_ASSERT(effect == PolicyEffect::PROMPT_BLANKET); +} + +TESTSUITE06(06){ + CLEANENV; + AttributeSet attributeSet; + std::string tmp("atrS"); + std::string tmpValue("Buu"); + BaseAttributePtr atr(new Attribute(&tmp, + Attribute::Match::Equal, + Attribute::Type::Subject)); + DPL::StaticPointerCast(atr)->addValue(&tmpValue); + attributeSet.insert(atr); + + AceDAO::setPolicyResult(attributeSet, PolicyDecision::NOT_APPLICABLE); + OptionalPolicyResult opt = AceDAO::getPolicyResult(attributeSet); + + PolicyResult effect(PolicyEffect::DENY); + if (!opt.IsNull()) { + effect = *opt; + } + + RUNNER_ASSERT(effect == PolicyDecision::NOT_APPLICABLE); +} + +TESTSUITE06(07){ + CLEANENV; + AttributeSet attributeSet; + std::string tmp("atrS"); + std::string tmpValue("Buu"); + BaseAttributePtr atr(new Attribute(&tmp, + Attribute::Match::Equal, + Attribute::Type::Subject)); + DPL::StaticPointerCast(atr)->addValue(&tmpValue); + attributeSet.insert(atr); + + AceDAO::setPolicyResult(attributeSet, PolicyResult::UNDETERMINED); + OptionalPolicyResult opt = AceDAO::getPolicyResult(attributeSet); + + PolicyResult effect(PolicyEffect::DENY); + if (!opt.IsNull()) { + effect = *opt; + } + + RUNNER_ASSERT(effect == PolicyResult::UNDETERMINED); +} + +TESTSUITE06(08){ + CLEANENV; + AttributeSet attributeSet; + std::string tmp("atrS2"); + std::string tmpValue("Buu2"); + BaseAttributePtr atr(new Attribute(&tmp, + Attribute::Match::Equal, + Attribute::Type::Subject)); + DPL::StaticPointerCast(atr)->addValue(&tmpValue); + attributeSet.insert(atr); + + std::string session = "jlakjhqrwebn234324987"; + std::string param = "xxx"; + + AceDAO::setPromptDecision(attributeSet, + DPL::FromUTF8String(param), + DPL::FromUTF8String(session), + PromptDecision::ALLOW_ALWAYS); + + OptionalCachedPromptDecision decision = + AceDAO::getPromptDecision(attributeSet, param); + + DPL::String session1; + PromptDecision dec = PromptDecision::DENY_FOR_SESSION; + if (!decision.IsNull()){ + dec = (*decision).decision; + if (!decision.IsNull()) { + session1 = *((*decision).session); + } + } + RUNNER_ASSERT(dec == PromptDecision::ALLOW_ALWAYS); + RUNNER_ASSERT(DPL::ToUTF8String(session1) == session); +} + +TESTSUITE06(09){ + CLEANENV; + AttributeSet attributeSet; + std::string tmp("atrS2"); + std::string tmpValue("Buu2"); + BaseAttributePtr atr(new Attribute(&tmp, + Attribute::Match::Equal, + Attribute::Type::Subject)); + DPL::StaticPointerCast(atr)->addValue(&tmpValue); + attributeSet.insert(atr); + + std::string param = "xxx"; + + AceDAO::setPromptDecision(attributeSet, + DPL::FromUTF8String(param), + DPL::OptionalString(), + PromptDecision::ALLOW_FOR_SESSION); + + OptionalCachedPromptDecision decision = + AceDAO::getPromptDecision(attributeSet, param); + + std::string session1; + PromptDecision dec = PromptDecision::DENY_FOR_SESSION; + if (!decision.IsNull()){ + dec = (*decision).decision; + RUNNER_ASSERT((*decision).session.IsNull()); + } + RUNNER_ASSERT(dec == PromptDecision::ALLOW_FOR_SESSION); +} + +///*findVerdict*/ +// +//void AceDAOTester::test3() +//{ +//// Tests does verdict is stored for validity always +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS3"); +// std::string tmpValue("Buu3"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_PERMIT; +// Validity val = Validity::ALWAYS; +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_PERMIT); +// handleResult(result, "find Verdict test 3"); +//} +///*findVerdict*/ +// +//void AceDAOTester::test4() +//{ +////tests does verdict is propertly stored (NOT saved)when session is Validity::ONCE +////and verdict is DENY +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS4"); +// std::string tmpValue("Buu4"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_DENY; +// Validity val = Validity::ONCE; +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict test 4"); +//} +///*findVerdict*/ +// +//void AceDAOTester::test5() +//{ +////tests does verdict is properly set when validity is session +////and verdict is deny +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS5"); +// std::string tmpValue("Buu5"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_DENY; +// Validity val = Validity::SESSION; +// pip.setSessionId("5"); +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// pip.setSessionId("5"); +// session = getSession(); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_DENY); +// handleResult(result, "find Verdict test 5"); +//} +///*findVerdict*/ +// +//void AceDAOTester::test6() +//{ +////tests does verdict is properly stored for DENY Validity::ALWAYS +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS6"); +// std::string tmpValue("Buu6"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_DENY; +// Validity val = Validity::ALWAYS; +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_DENY); +// handleResult(result, "find Verdict test 6"); +//} +// +// +//void AceDAOTester::test7() +//{ +////tests stored version fo INAPPLICABLE Validity::ONCE +// +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// attributeSet.insert(constructAttribute("atrS7","Buu7")); +// Verdict ver = Verdict::VERDICT_INAPPLICABLE; +// Validity val = Validity::ONCE; +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// //Verdict with 'Once' validity should not be stored, therefore +// //the outcome should be UNKNOWN +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict test 7"); +//} +// +// +//void AceDAOTester::test8() +//{ +////tests storing data for verdict INAPLICABLE and Validity::SESSION +// +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// attributeSet.insert(constructAttribute("atrS8","Buuu8")); +// Verdict ver = Verdict::VERDICT_INAPPLICABLE; +// Validity val = Validity::SESSION; +// pip.setSessionId("8"); +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// pip.setSessionId("9"); +// session = getSession(); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict test 8"); +//} +///*findVerdict*/ +// +//void AceDAOTester::test9() +//{ +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS9"); +// std::string tmpValue("Buu9"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_INAPPLICABLE; +// Validity val = Validity::ALWAYS; +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_INAPPLICABLE); +// handleResult(result, "find Verdict test 9"); +//} +///*findVerdict*/ +// +//void AceDAOTester::test10() +//{ +////tests does verdict is properly stored for verdict undetermined +////and validity Validity::ONCE - thats why verdict is UNKNOWN +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS10"); +// std::string tmpValue("Buu10"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_UNDETERMINED; +// Validity val = Validity::ONCE; +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict test 10"); +//} +///*findVerdict*/ +// +//void AceDAOTester::test11() +//{ +////tests does verdict is properly stored for verdict undetermined +////and validity Validity::SESSION - TODO Verdicts for undetermined are not stored?? +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS11"); +// std::string tmpValue("Buu11"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_UNDETERMINED; +// pip.setSessionId("8"); +// Validity val = Validity::SESSION; +// pip.setSessionId("8"); +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict test 11"); +//} +///*findVerdict*/ +// +//void AceDAOTester::test12() +//{ +////tests does verdict is properly stored for verdict undetermined +////and validity Validity::ALWAYS - TODO Verdicts for undetermined are not stored?? +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS12"); +// std::string tmpValue("Buu12"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_UNDETERMINED; +// Validity val = Validity::ALWAYS; +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict test 12"); +//} +///*findVerdict*/ +// +//void AceDAOTester::test13() +//{ +////tests does verdict is properly stored for verdict unknown +////and validity Validity::ONCE-thats why verdict is not stored +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS13"); +// std::string tmpValue("Buu13"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_UNKNOWN; +// Validity val = Validity::ONCE; +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict test 13"); +//} +///*findVerdict*/ +// +//void AceDAOTester::test14() +//{ +////tests does verdict is properly stored for verdict unknown +////and validity session- TODO why is the verdict not stored? +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS14"); +// std::string tmpValue("Buu14"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_UNKNOWN; +// Validity val = Validity::SESSION; +// pip.setSessionId("8"); +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// pip.setSessionId("8"); +// session = getSession(); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict test 14"); +//} +///*findVerdict*/ +// +//void AceDAOTester::test15() +//{ +////tests does verdict is properly stored for verdict unknown and +////validity Validity::ALWAYS - TODO should we save Unknown verdict? +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS15"); +// std::string tmpValue("Buu15"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_UNKNOWN; +// Validity val = Validity::ALWAYS; +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict test 15"); +//} +// +///*removeVerdict*/ //tests does verdict is properly removed +//void AceDAOTester::test16() +//{ +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS15"); +// std::string tmpValue("Buu15"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// +// Verdict ver = Verdict::VERDICT_PERMIT; +// Validity val = Validity::ALWAYS; +// //add verdict +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// //check has verdict been stored? +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_PERMIT); +// handleResult(result, "find Verdict test 16: partA"); +// +// //remove verdict +// VerdictLogic::removeVerdict(request, attributeSet); +// //verified removed +// verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict test 16: partB"); +//} +// +///*findVerdict*/ +//void AceDAOTester::test18() +//{ +////verdict for 3 elements on the set of attributes +// Request request("subject18","resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS60"); +// std::string tmpValue("Buu60"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// +// std::string tmp1("atrS61"); +// std::string tmpValue1("Buu61"); +// BaseAttributePtr atr1( +// new Attribute(&tmp1,Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr1)->addValue(&tmpValue1); +// attributeSet.insert(atr1); +// +// std::string tmp2("atrS62"); +// std::string tmpValue2("Buu62"); +// BaseAttributePtr atr2( +// new Attribute(&tmp2,Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr2)->addValue(&tmpValue2); +// attributeSet.insert(atr2); +// +// Verdict ver = Verdict::VERDICT_DENY; +// Validity val = Validity::ALWAYS; +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_DENY); +// handleResult(result, "find Verdict test 18"); +//} +// +///*resetDatabase*/ +//void AceDAOTester::test19() +//{ +// //tests reset DB +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS15"); +// std::string tmpValue("Buu15"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// +// Verdict ver = Verdict::VERDICT_PERMIT; +// Validity val = Validity::ALWAYS; +// //add verdict +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// //check has verdict been stored? +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_PERMIT); +// handleResult(result, "reset database test 19: partA"); +// +// AceDAO::resetDatabase(); +// +// verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "reset database test 19: partB"); +//} + + +/*setWidgetDevCapSetting*/ + +TESTSUITE06(26){ + CLEANENV; + std::string res("res20"); + WidgetHandle sub = 20; + + SettingsLogic::setWidgetDevCapSetting(res, sub, Preference::PREFERENCE_PERMIT); + std::list userSettings; + SettingsLogic::getWidgetDevCapSettings(&userSettings); + RUNNER_ASSERT( + UserSettingEqual( + &userSettings, res, sub, Preference::PREFERENCE_PERMIT)); +} + +/*setWidgetDevCapSetting*/ + +TESTSUITE06(27){ + CLEANENV; + std::string res("res21"); + WidgetHandle sub = 21; + + SettingsLogic::setWidgetDevCapSetting(res, sub, Preference::PREFERENCE_DENY); + std::list userSettings; + SettingsLogic::getWidgetDevCapSettings(&userSettings); + RUNNER_ASSERT( + UserSettingEqual( + &userSettings, res, sub, Preference::PREFERENCE_DENY)); +} + +/*setWidgetDevCapSetting*/ + +TESTSUITE06(28){ + CLEANENV; + std::string res("res22"); + WidgetHandle sub = 22; + AceDAO::clearAllSettings(); + SettingsLogic::setWidgetDevCapSetting(res, sub, Preference::PREFERENCE_DEFAULT); + std::list userSettings; + SettingsLogic::getWidgetDevCapSettings(&userSettings); + RUNNER_ASSERT( + UserSettingEqual( + &userSettings, res, sub, Preference::PREFERENCE_DEFAULT)); +} + +TESTSUITE06(29){ + CLEANENV; + std::string res("res23"); + WidgetHandle sub = 23; + + SettingsLogic::setWidgetDevCapSetting(res, sub, Preference::PREFERENCE_DEFAULT); + SettingsLogic::setWidgetDevCapSetting(res, sub, Preference::PREFERENCE_DENY); + std::list userSettings; + SettingsLogic::getWidgetDevCapSettings(&userSettings); + RUNNER_ASSERT( + UserSettingEqual( + &userSettings, res, sub, Preference::PREFERENCE_DENY)); +} + +/*setWidgetDevCapSettings*/ + +TESTSUITE06(30){ + CLEANENV; + std::string resa("res24a"); + WidgetHandle suba = 241; + std::string resb("res24b"); + WidgetHandle subb = 242; + + std::list permissionsL; + + permissionsL.push_back( + PermissionTriple(suba, resa, Preference::PREFERENCE_DENY)); + permissionsL.push_back( + PermissionTriple(subb, resb, Preference::PREFERENCE_PERMIT)); + + SettingsLogic::setWidgetDevCapSettings(permissionsL); + + std::list userSettings; + SettingsLogic::getWidgetDevCapSettings(&userSettings); + RUNNER_ASSERT( + UserSettingsAgreed(&permissionsL, &userSettings)); +} + + + +TESTSUITE06(31){ + CLEANENV; +//multi set - if value is not set in DB or has DEFAUL value it is not +//send by getWidgetDevCapSettings - optimization + + std::string resa("res25a"); + WidgetHandle suba = 251; + std::string resb("res25b"); + WidgetHandle subb = 252; + std::string resc("res25c"); + WidgetHandle subc = 253; + + std::list permissionsL; + + permissionsL.push_back( + PermissionTriple(suba, resa, Preference::PREFERENCE_DENY)); + permissionsL.push_back( + PermissionTriple(subb, resb, Preference::PREFERENCE_PERMIT)); + permissionsL.push_back( + PermissionTriple(subc, resc, Preference::PREFERENCE_DEFAULT)); + + + SettingsLogic::setWidgetDevCapSettings(permissionsL); + + std::list userSettings; + SettingsLogic::getWidgetDevCapSettings(&userSettings); + RUNNER_ASSERT(UserSettingsAgreed(&permissionsL, &userSettings)); +} + +TESTSUITE06(32){ + CLEANENV; + //empty list -- TODO what is it testing? + std::list permissionsL; + + std::list userSettings; + SettingsLogic::getWidgetDevCapSettings(&userSettings); + RUNNER_ASSERT(UserSettingsAgreed(&permissionsL, &userSettings)); +} + +TESTSUITE06(33){ + CLEANENV; + //test resource setting PERMIT + std::string res("res27"); + SettingsLogic::setDevCapSetting(res, Preference::PREFERENCE_PERMIT); + std::map resourceSettings; + SettingsLogic::getDevCapSettings(&resourceSettings); + + RUNNER_ASSERT( + ResourceSettingEqual(&resourceSettings, + res, + Preference::PREFERENCE_PERMIT)); +} + +TESTSUITE06(34){ + CLEANENV; + //test resource setting DENY + std::string res("res28"); + SettingsLogic::setDevCapSetting(res, Preference::PREFERENCE_DENY); + std::map resourceSettings; + SettingsLogic::getDevCapSettings(&resourceSettings); + + RUNNER_ASSERT( + ResourceSettingEqual(&resourceSettings, + res, + Preference::PREFERENCE_DENY)); +} + +TESTSUITE06(35){ + CLEANENV; + //test resource setting Preference::PREFERENCE_DEFAULT + + std::string res("res29"); + SettingsLogic::setDevCapSetting(res, Preference::PREFERENCE_DEFAULT); + std::map resourceSettings; + SettingsLogic::getDevCapSettings(&resourceSettings); + + RUNNER_ASSERT( + ResourceSettingEqual( + &resourceSettings, + res, + Preference::PREFERENCE_DEFAULT)); +} + +TESTSUITE06(36){ + CLEANENV; + //multi set for Resource settings + + std::string resa("res30a"); + std::string resb("res30b"); + + std::list< std::pair >* resourcesL = + new std::list< std::pair >(); + + resourcesL->push_back(make_pair(&resa, Preference::PREFERENCE_DENY)); + resourcesL->push_back(make_pair(&resb, Preference::PREFERENCE_PERMIT)); + + SettingsLogic::setAllDevCapSettings (*resourcesL); + + std::map resourceSettings; + SettingsLogic::getDevCapSettings(&resourceSettings); + RUNNER_ASSERT(ResourceSettingsAgreed(resourcesL, &resourceSettings)); + delete resourcesL; +} + + +//void AceDAOTester::test31() +//{ +// // session has changed (PERMIT). +// //What is the verdict now? +// +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS31"); +// std::string tmpValue("Buu31"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_PERMIT; +// Validity val = Validity::SESSION; +// pip.setSessionId("aaa"); +// +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// Verdict verdictA = +// VerdictLogic::findVerdict(request, session, attributeSet); +// bool resultA = VSPAssertEqual(verdictA, Verdict::VERDICT_PERMIT); +// handleResult(resultA, "Verdict session test 31 session a"); +// +// pip.setSessionId("bbb"); +// session = getSession(); +// Verdict verdictB = +// VerdictLogic::findVerdict(request, session, attributeSet); +// bool resultB = VSPAssertEqual(verdictB, Verdict::VERDICT_UNKNOWN); +// handleResult(resultB, "Verdict session test 31 session b"); +//} + + +//void AceDAOTester::test32() +//{ +// //session has changed (DENY). +// //what is the verdict? +// +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS32"); +// std::string tmpValue("Buu32"); +// BaseAttributePtr atr( +// new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_DENY; +// Validity val = Validity::SESSION; +// pip.setSessionId("aaa"); +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// Verdict verdictA = +// VerdictLogic::findVerdict(request, session, attributeSet); +// bool resultA = VSPAssertEqual(verdictA, Verdict::VERDICT_DENY); +// handleResult(resultA, "Verdict session test 32 session a"); +// +// pip.setSessionId("ccc"); +// +// session = getSession(); +// Verdict verdictB = +// VerdictLogic::findVerdict(request, session, attributeSet); +// bool resultB = VSPAssertEqual(verdictB, Verdict::VERDICT_UNKNOWN); +// handleResult(resultB, "Verdict session test 32 session b"); +//} + +//void AceDAOTester::test33(){ +// +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS33"); +// std::string tmpValue("Buu33"); +// BaseAttributePtr atr(new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_INAPPLICABLE; +// Validity val = Validity::SESSION; +// pip.setSessionId("aaa"); +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// Verdict verdictA = VerdictLogic::findVerdict(request, session, attributeSet); +// bool resultA = VSPAssertEqual(verdictA, Verdict::VERDICT_INAPPLICABLE); +// handleResult(resultA, "Verdict session test 33 session a"); +// +// pip.setSessionId("bbb"); +// +// session = getSession(); +// Verdict verdictB = VerdictLogic::findVerdict(request, session, attributeSet); +// bool resultB = VSPAssertEqual(verdictB, Verdict::VERDICT_UNKNOWN); +// handleResult(resultB, "Verdict session test 33 session b"); +//} + +//void AceDAOTester::test34(){ //session has changed (UNDETERMINED). +////what is the verdict? +// +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS34"); +// std::string tmpValue("Buu34"); +// BaseAttributePtr atr(new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_UNDETERMINED; +// Validity val = Validity::SESSION; +// pip.setSessionId("aaa"); +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// Verdict verdictA = VerdictLogic::findVerdict(request, session, attributeSet); +// bool resultA = VSPAssertEqual(verdictA, Verdict::VERDICT_UNKNOWN); +// handleResult(resultA, "Verdict session test 34 session a"); +// +// pip.setSessionId("bbb"); +// +// session = getSession(); +// Verdict verdictB = VerdictLogic::findVerdict(request, session, attributeSet); +// bool resultB = VSPAssertEqual(verdictB, Verdict::VERDICT_UNKNOWN); +// handleResult(resultB, "Verdict session test 34 session b"); +//} + +//void AceDAOTester::test35(){ //session has changed(UNKNOWN), +////what is the verdict? +// +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS35"); +// std::string tmpValue("Buu35"); +// BaseAttributePtr atr(new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_UNKNOWN; +// Validity val = Validity::SESSION; +// pip.setSessionId("aaa"); +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// Verdict verdictA = VerdictLogic::findVerdict(request, session, attributeSet); +// bool resultA = VSPAssertEqual(verdictA, Verdict::VERDICT_UNKNOWN); +// handleResult(resultA, "Verdict session test 35 session a"); +// pip.setSessionId("bbb"); +// +// session = getSession(); +// Verdict verdictB = VerdictLogic::findVerdict(request, session,attributeSet); +// bool resultB = VSPAssertEqual(verdictB, Verdict::VERDICT_UNKNOWN); +// handleResult(resultB, "Verdict session test 35 session b"); +//} + +//void AceDAOTester::test36(){ //changed verdict in the same session +// +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// std::string tmp("atrS36"); +// std::string tmpValue("Buu36"); +// BaseAttributePtr atr(new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// Verdict ver = Verdict::VERDICT_DENY; +// Validity val = Validity::SESSION; +// pip.setSessionId("aaa"); +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// Verdict verdictA = VerdictLogic::findVerdict(request, session, attributeSet); +// bool resultA = VSPAssertEqual(verdictA ,Verdict::VERDICT_DENY); +// handleResult(resultA, "Verdict session test 36 session a"); +// +// ver = Verdict::VERDICT_PERMIT; +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// Verdict verdictB = VerdictLogic::findVerdict(request, session, attributeSet); +// bool resultB = VSPAssertEqual(verdictB ,Verdict::VERDICT_PERMIT); +// handleResult(resultB, "Verdict session test 36 session b"); +//} +// +///* User settings crash tests */ +// +// +//void AceDAOTester::test37(){ //empty attribute and verdict permit always +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// +// Verdict ver = Verdict::VERDICT_PERMIT; +// Validity val = Validity::ALWAYS; +// +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict,Verdict::VERDICT_PERMIT); +// handleResult(result, "find Verdict empty attribute set test 37 "); +//} +// +//void AceDAOTester::test38(){ //empty attribute and verdict deny always +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// +// Verdict ver = Verdict::VERDICT_DENY; +// Validity val = Validity::ALWAYS; +// +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict,Verdict::VERDICT_DENY); +// handleResult(result, "find Verdict empty attribute set test 38 "); +//} +// +//void AceDAOTester::test39(){ +// +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// +// Verdict ver = Verdict::VERDICT_INAPPLICABLE; +// Validity val = Validity::ALWAYS; +// +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict,Verdict::VERDICT_INAPPLICABLE); +// handleResult(result, "find Verdict empty attribute set test 39 "); +//} +// +//void AceDAOTester::test40(){ //empty attribute and verdict unknown +// Request request("subject", "resource"); +// AttributeSet attributeSet; +// +// Verdict ver = Verdict::VERDICT_UNKNOWN; +// Validity val = Validity::ALWAYS; +// +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict,Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict empty attribute set test 40 "); +//} +// +//void AceDAOTester::test41() +//{ +// //empty string as subject and resource, +// //without attributes and verdit DENY Validity::ALWAYS +// Request request("", ""); +// //TODO is it OK to store Verdict::VERDICT for empty request? +// AttributeSet attributeSet; +// +// Verdict ver = Verdict::VERDICT_DENY; +// Validity val = Validity::ALWAYS; +// +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict,Verdict::VERDICT_DENY); +// //bool result = VSPAssertEqual(verdict,Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict empty request empty " +// "attribute set test 41 "); +//} +// +//void AceDAOTester::test42(){ +// //empty string as subject and resource, without attributes +// //and verdict is PERMIT Validity::ALWAYS +// Request request("", ""); +// +// AttributeSet attributeSet; +// std::string tmp("atrS42"); +// std::string tmpValue("Buu342"); +// BaseAttributePtr atr(new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// +// Verdict ver = Verdict::VERDICT_PERMIT; +// Validity val = Validity::ALWAYS; +// +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict,Verdict::VERDICT_PERMIT); +// handleResult(result, "find Verdict empty request test 42 "); +//} +// +//void AceDAOTester::test43(){ +// //TODO I have changed it! verdict +// //has changed and stored in DB. does current verdict is the last one? +// +// Request request("ac", "ca"); +// AttributeSet attributeSet; +// +// Verdict ver = Verdict::VERDICT_DENY; +// Verdict verp = Verdict::VERDICT_PERMIT; +// Validity val = Validity::ALWAYS; +// +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// VerdictLogic::addVerdict(request, session, attributeSet, verp, val); +// +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict,Verdict::VERDICT_PERMIT); +// handleResult(result, "find Verdict empty request empty " +// "attribute set test 43 "); +//} +// +//void AceDAOTester::test44(){ +// //empty subject and resource name - get Atributes list +// Request request("",""); +// +// AttributeSet attributeSet; +// std::string tmp("atrS44"); +// std::string tmpValue("Buu44"); +// BaseAttributePtr atr(new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// +// Verdict ver = Verdict::VERDICT_PERMIT; +// Validity val = Validity::ALWAYS; +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// AttributeSet attributeS; +// AceDAO::getAttributes(&attributeS); +// +// bool result = false; +// +// result = AttributeEqual(attributeSet,attributeS); +// +// handleResult(!result,"find Attributes empty request test 44"); +//} +// +//void AceDAOTester::test45(){ +// //empty subject and resource name - what is the verdict? - is it OK?? +// Request request("", ""); +// AttributeSet attributeSet; +// std::string tmp("atrS15"); +// std::string tmpValue("Buu15"); +// BaseAttributePtr atr(new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// +// Verdict ver = Verdict::VERDICT_PERMIT; +// Validity val = Validity::ALWAYS; +// std::string session = getSession(); +// VerdictLogic::addVerdict(request, session, attributeSet, ver, val); +// +// VerdictLogic::removeVerdict(request, attributeSet); +// +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict,Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict test 45"); +//} +// +//void AceDAOTester::test46(){ +// // TODO is it ok that for empty subject and resource name +// // the verdict is not stored +// Request requestemp("", ""); +// Request request("aa","bb"); +// +// AttributeSet attributeSet; +// std::string tmp("atrS46"); +// std::string tmpValue("Buu146"); +// BaseAttributePtr atr(new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// +// Verdict verAdd = Verdict::VERDICT_PERMIT; +// Validity val = Validity::ALWAYS; +// std::string session = getSession(); +// VerdictLogic::addVerdict(requestemp, session, attributeSet, verAdd, val); +// VerdictLogic::addVerdict(request, session, attributeSet, verAdd, val); +// +// VerdictLogic::removeVerdict(requestemp, attributeSet); +// +// Verdict verdictA = VerdictLogic::findVerdict(requestemp, session, attributeSet); +// bool resultA = VSPAssertEqual(verdictA,Verdict::VERDICT_UNKNOWN); +// handleResult(resultA,"find Verdict test 46 A "); +// +// Verdict verdictB = VerdictLogic::findVerdict(request, session, attributeSet); +// bool resultB = VSPAssertEqual(verdictB, Verdict::VERDICT_PERMIT); +// handleResult(resultB,"find Verdict test 46 B "); +// +//} + +TESTSUITE06(53){ + CLEANENV; + //Empty resource name + std::string res(""); + SettingsLogic::setDevCapSetting(res, Preference::PREFERENCE_PERMIT); + std::map resourceSettings; + SettingsLogic::getDevCapSettings(&resourceSettings); + + RUNNER_ASSERT( + ResourceSettingEqual( + &resourceSettings, + res, + Preference::PREFERENCE_PERMIT)); +} + +//void AceDAOTester::test48(){ +// SettingsLogic::setResourceSetting(NULL, PERMIT); +// std::map* resourceSettings = +// SettingsLogic::getResourceSettings(); +// +// bool result = ResourceSettingEqual(resourceSettings, NULL,PERMIT ); +// handleResult(result, "get Resource Settings empty resource test 48"); +// printf("get Resource Settings empty resource test 48 -----> " +// "Segmentation fault\n"); +//} + +TESTSUITE06(55){ + CLEANENV; + //resource settings list with empty elemen + + std::string resb(""); + + std::list< std::pair > *resourcesL = new + std::list< std::pair >(); + + resourcesL->push_back(make_pair(&resb, Preference::PREFERENCE_PERMIT)); + + SettingsLogic::setAllDevCapSettings (*resourcesL); + + std::map resourceSettings; + SettingsLogic::getDevCapSettings(&resourceSettings); + RUNNER_ASSERT(ResourceSettingsEqual(resourcesL, &resourceSettings)); + delete resourcesL; +} + +TESTSUITE06(56){ + CLEANENV; + //user settings with empty subject and resource + //if resource or subject name is empty, values are not saved in DB + std::string resb("fake-resource"); + WidgetHandle subb = 0; + + std::list permissionsL; + + permissionsL.push_back(PermissionTriple(subb, resb, Preference::PREFERENCE_PERMIT)); + + SettingsLogic::setWidgetDevCapSettings(permissionsL); + + std::list userSettings; + SettingsLogic::getWidgetDevCapSettings(&userSettings); + RUNNER_ASSERT(UserSettingsAgreed(&permissionsL, &userSettings)); +} + +TESTSUITE06(57){ + CLEANENV; + //user setting equal + // settings with at least one empty value is not set + std::string res(""); + WidgetHandle sub = 0; + + SettingsLogic::setWidgetDevCapSetting(res, sub, Preference::PREFERENCE_PERMIT); + std::list userSettings; + SettingsLogic::getWidgetDevCapSettings(&userSettings); + RUNNER_ASSERT( + UserSettingNotStored( + &userSettings, + res, + sub, + Preference::PREFERENCE_PERMIT)); +} + +TESTSUITE06(58){ + CLEANENV; + //user settings empty values and Default access + + std::string res(""); + WidgetHandle sub = 0; + + SettingsLogic::setWidgetDevCapSetting(res, sub, Preference::PREFERENCE_DEFAULT); + std::list userSettings; + SettingsLogic::getWidgetDevCapSettings(&userSettings); + RUNNER_ASSERT( + UserSettingNotStored( + &userSettings, + res, + sub, + Preference::PREFERENCE_DEFAULT)); +} + +//void AceDAOTester::test53(){ +// Request request("1 OR sv.verdict=1 --", "r53"); +// AttributeSet attributeSet; +// std::string tmp("atrS53"); +// std::string tmpValue("Buu15"); +// BaseAttributePtr atr(new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// +//// Verdict ver = Verdict::VERDICT_PERMIT; +//// Validity val = Validity::ALWAYS; +// +// std::string session = getSession(); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict test 53 sql injection"); +//} + +//void AceDAOTester::test54() +//{ +// Request request("1' OR sv.verdict=1 --", "r53"); +// AttributeSet attributeSet; +// std::string tmp("atrS54"); +// std::string tmpValue("Buu15"); +// BaseAttributePtr atr(new Attribute(&tmp, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&tmpValue); +// attributeSet.insert(atr); +// +// std::string session = getSession(); +// Verdict verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "find Verdict test 54 sql injection"); +//} +// +//void AceDAOTester::test55() +//{ +// Request request("s55","r55"); +// AttributeSet attributeSet; +// std::string session = getSession(); +// Verdict verdict = Verdict::VERDICT_PERMIT; +// Validity validity = Validity::ALWAYS; +// +// VerdictLogic::addVerdict(request, session, attributeSet, verdict, validity); +// +// verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_PERMIT); +// handleResult(result, "Test 55a - clean database"); +// +// SettingsLogic::setWidgetDevCapSetting(request.getResourceId(), +// request.getSubjectId(), +// Preference::PREFERENCE_DENY); +// +// Preference preference = SettingsLogic::findGlobalUserSettings(request); +// handleResult(preference == Preference::PREFERENCE_DENY, "Test 55b - clean database"); +// +// AceDAO::resetDatabase(); +// +// verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "Test 55c - clean database"); +// +// preference = SettingsLogic::findGlobalUserSettings(request); +// handleResult(preference == Preference::PREFERENCE_DEFAULT, +// "Test 55d - clean database"); +//} +// +//void AceDAOTester::test56(){ +// Request request("s56","r56"); +// AttributeSet attributeSet; +// std::string session = getSession(); +// Verdict verdict = Verdict::VERDICT_PERMIT; +// Validity validity = Validity::ALWAYS; +// +// std::string aName("atrS15"); +// std::string aValue1("a;"); +// std::string aValue2("b"); +// BaseAttributePtr atr(new Attribute(&aName, Attribute::Match::Equal, Attribute::Type::Subject)); +// DPL::StaticPointerCast(atr)->addValue(&aValue1); +// DPL::StaticPointerCast(atr)->addValue(&aValue2); +// attributeSet.insert(atr); +// +// VerdictLogic::addVerdict(request, session, attributeSet, verdict, validity); +// +// attributeSet.clear(); +// BaseAttributePtr atr1(new Attribute(&aName, Attribute::Match::Equal, Attribute::Type::Subject)); +// aValue1 = "a"; +// aValue2 = ";b"; +// DPL::StaticPointerCast(atr1)->addValue(&aValue1); +// DPL::StaticPointerCast(atr1)->addValue(&aValue2); +// attributeSet.insert(atr1); +// +// verdict = VerdictLogic::findVerdict(request, session, attributeSet); +// bool result = VSPAssertEqual(verdict, Verdict::VERDICT_UNKNOWN); +// handleResult(result, "Test 56 - attribute hash calculation check"); +//} + + +//BaseAttributePtr AceDAOTester::constructAttribute( +// const char * name, +// const char * value, +// Attribute::Match match, +// Attribute::Type type) +//{ +// std::string tmp(name); +// std::string tmpValue(value); +// BaseAttributePtr attr(new Attribute(&tmp, match, type)); +// DPL::StaticPointerCast(attr)->addValue(&tmpValue); +// return attr; +//} + diff --git a/tests/ace/TestSuite07.cpp b/tests/ace/TestSuite07.cpp new file mode 100644 index 0000000..b276a50 --- /dev/null +++ b/tests/ace/TestSuite07.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file TestSuite07.cpp + * @author unknown + * @version 1.0 + * @brief Test cases for ConfigurationManager + */ + + +#include +#include + +#include +#include + +#include +#include + +#include + +#define TESTSUITE07(n,path) \ +RUNNER_TEST(ts07_configuration_mng_ ## n){ \ + ConfigurationManagerTester cm; \ + cm.setUp(); \ + cm.parseTest(path); + +#define TESTSUITEEND \ + cm.cleanUp(); \ +} + +class ConfigurationManagerTester : public ConfigurationManager { +public: + int parseTest(const std::string &file) { + return parse(file); + } + + const std::string& getConfigFileTest() const { + return getConfigFile(); + } + + void prepareDir(std::string & path); + void cleanUp(); + void setUp(); + bool assertPolicyNOTinAList(const char *) const; + bool assertIsCurrentPolicy(const char * policy); + bool assertEqual(const std::string& original, const std::string & intended); + bool assertFileExists(const char * policyFile) const; + bool assertFileDoesntExist(const char * policyFile) const; + bool assertEqual(const int original,const int intended) const; + bool assertPolicyInAList(const char *)const; + + ConfigurationManagerTester() { } +}; + +TESTSUITE07(01,CONFIGURATION_MGR_TEST_CONFIG) + RUNNER_ASSERT(cm.assertIsCurrentPolicy("pms_general-test.xml") && cm.assertPolicyInAList("pms_general-test.xml")); +TESTSUITEEND + +TESTSUITE07(02,CONFIGURATION_MGR_TEST_CONFIG) + RUNNER_ASSERT(cm.assertEqual(cm.getStoragePath(), + std::string(CONFIGURATION_MGR_TEST_POLICY_STORAGE))); +TESTSUITEEND + +TESTSUITE07(03,CONFIGURATION_MGR_TEST_CONFIG) + LogInfo("Full path "<::const_iterator it = getPolicyFiles().begin(); it!=getPolicyFiles().end(); ++it){ + if( *it == expected ){ + found = true; + break; + } + } + + LogDebug("assert Policy in a list result: "<d_name); + if( fileName == "pms_general-test.xml" || fileName == "bondixml.dtd" || + fileName == "." || fileName ==".." ){ + continue; + } + std::string fullPath(CONFIGURATION_MGR_TEST_POLICY_STORAGE"/"); + fullPath.append(dirp->d_name); + if (removePolicyFile(fullPath) == CM_REMOVE_ERROR){ + LogError("Cannot clean up. Exiting"); + break; + }; + } + //Restore the backup of config file + system("cp "CONFIGURATION_MGR_TEST_PATH"backup.xml "CONFIGURATION_MGR_TEST_CONFIG); +} + +void ConfigurationManagerTester::setUp(){ + //Create backup of config file + system("cp "CONFIGURATION_MGR_TEST_CONFIG" "CONFIGURATION_MGR_TEST_PATH"backup.xml"); +} diff --git a/tests/ace/ace_tests.cpp b/tests/ace/ace_tests.cpp new file mode 100644 index 0000000..8202ac0 --- /dev/null +++ b/tests/ace/ace_tests.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file main.cpp + * @author Bartlomiej Grzelewski + * @version 1.0 + * @brief This file is the implementation file of main + */ +#include + +#include +#include + +#include + +#include + +#include "Interfaces.h" +#include "PEPSingleton.h" + + +using namespace LoopControl; + +class UnitTestThread +{ +public: + UnitTestThread() + { + // Attach databases + AceDB::AceDAO::attachToThread(); + } + + ~UnitTestThread() + { + // Detach databases + AceDB::AceDAO::detachFromThread(); + } +}; + +int main (int argc, char *argv[]) +{ + init_loop(argc, argv); + + LogInfo("Initializing PEPSingleton."); + + UnitTestThread attacher; + + PEPSingleton::Instance().initialize( + new WebRuntimeImp(), + new ResourceInformationImp(), + new OperationSystemImp()); + + LogInfo("Starting tests."); + + int status = DPL::Test::TestRunnerSingleton::Instance().ExecTestRunner(argc, argv); + + PEPSingleton::Instance().terminate(); + + quit_loop(); + return status; +} + diff --git a/tests/ace/loop_control.cpp b/tests/ace/loop_control.cpp new file mode 100644 index 0000000..f4cfecf --- /dev/null +++ b/tests/ace/loop_control.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file loop_control.cpp + * @author Jaroslaw Osmanski (j.osmanski@samsung.com) + * @version 1.0 + * @brief This is implementation of EFL version of loop control + */ + +#include + +#include + +#include +#include + +#include + +namespace LoopControl +{ +void init_loop(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + g_type_init(); + g_thread_init(NULL); + + LogInfo("Starting"); + elm_init(argc, argv); +} + +void wait_for_wrt_init() +{ + ecore_main_loop_begin(); +} + +void finish_wait_for_wrt_init() +{ + ecore_main_loop_quit(); +} + +void quit_loop() +{ + elm_shutdown(); +} + +void wrt_start_loop() +{ + ecore_main_loop_begin(); +} + +void wrt_end_loop() +{ + ecore_main_loop_quit(); +} + +void *abstract_window() +{ + return elm_win_add(NULL, "hello", ELM_WIN_BASIC); +} + +}//end of LoopControl namespace diff --git a/tests/ace/loop_control.h b/tests/ace/loop_control.h new file mode 100644 index 0000000..30aa6e8 --- /dev/null +++ b/tests/ace/loop_control.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file loop_control.cpp + * @author Jaroslaw Osmanski (j.osmanski@samsung.com) + * @version 1.0 + * @brief This file is the definitions of loop controlling utilities + */ + + +#ifndef LOOP_CONTROL_H_ +#define LOOP_CONTROL_H_ + +namespace LoopControl +{ + +void init_loop(int argc, char *argv[]); +void wait_for_wrt_init(); +void finish_wait_for_wrt_init(); +void quit_loop(); + +void wrt_start_loop(); +void wrt_end_loop(); + +void *abstract_window(); + +} + +#endif /* LOOP_CONTROL_H_ */ diff --git a/tests/ace/test-configuration/CMTest/CMakeLists.txt b/tests/ace/test-configuration/CMTest/CMakeLists.txt new file mode 100644 index 0000000..d186839 --- /dev/null +++ b/tests/ace/test-configuration/CMTest/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/CMTest/pms_config.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/CMTest/pms_general-test.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/CMTest/policyTest1.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/CMTest/policyTest2.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/CMTest/policyTest3.xml + DESTINATION /usr/etc/ace/CMTest + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ GROUP_WRITE + ) + +ADD_SUBDIRECTORY(active) + diff --git a/tests/ace/test-configuration/CMTest/active/CMakeLists.txt b/tests/ace/test-configuration/CMTest/active/CMakeLists.txt new file mode 100644 index 0000000..baefacf --- /dev/null +++ b/tests/ace/test-configuration/CMTest/active/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/CMTest/active/pms_general-test.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/CMTest/active/bondixml.dtd + DESTINATION /usr/etc/ace/CMTest/active + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ GROUP_WRITE + ) + diff --git a/tests/ace/test-configuration/CMTest/active/bondixml.dtd b/tests/ace/test-configuration/CMTest/active/bondixml.dtd new file mode 100644 index 0000000..42a6c7b --- /dev/null +++ b/tests/ace/test-configuration/CMTest/active/bondixml.dtd @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/ace/test-configuration/CMTest/active/pms_general-test.xml b/tests/ace/test-configuration/CMTest/active/pms_general-test.xml new file mode 100644 index 0000000..ac55f33 --- /dev/null +++ b/tests/ace/test-configuration/CMTest/active/pms_general-test.xml @@ -0,0 +1,2510 @@ + + + + + + + + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + asdfkjlhxvczxnbcvnjahfjhsdfklahfdas + + + +

PValue

QValue Gvalue laj? +
+ + + modulus + + + exponent + + +
+ + Subject name + SKI + +
+
+ + + + + + + + + subject + + + + + + + + + + + + + + + + resource + + + + + + + + + + + + + + + resource + + + + + + + + + + subject3 + + + + + + + + + + + resource2 + + + + + + + + + resource2 + + + + + + + + + subject4 + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + subject5 + + + + + + + resource5 + + + + + + + + + + resource6 + + + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + + s8a + + + + + + r8 + + + + + + + + r8 + + + + + + + + + + s8b + + + + + + r8 + + + + + + + + r8 + + + + + + + + + + s8c + + + + + + r8 + + + + + + + + + + + + + + s9a + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9b + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9c + + + + + + r9 + + + + + + + + + r9 + + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9d + + + + + + r9 + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9e + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9f + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9g + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9h + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s10a + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + s10b + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + s10c + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + + + + + + + s13 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + s14 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s16 + + + + + + + + + + + + + + + + + + + + + subject4 + + + + + + + + resource4 + + + + + + + + + + + subject5 + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s19.1 + + + + + + + + resource4 + + + + + + + + + + s19.2 + + + + + + + + resource4 + + + + + + + + + + + + + s20.1 + + + + + + + + resource4 + + + + + + + + + + s20.2 + + + + + + + + resource4 + + + + + + + + + + + + + + s21 + + + + + + + + + + + + + + + + + + s23 + + + + + + + + + + + + + + + + + + + + s24 + + + + + + + + + + + + + + + + + + s25.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s26.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.1 + + + + + + + + + + + + + + + + + + + + + + + + + s27.2 + + + + + + + + + + + + + + + + + + + + + + + + + s27.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + s28 + + + + + + + + + + + + + + + + + + + + + + + + + + s29 + + + + + + + + + + + + + + + + + + + + + + + + + s30.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s33.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s34.1 + + + + + + + + + + + + + + + + + + + + + + + + s34.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.2 + + + + + + + + + + + + + + + + + + + + + + + + org.tizen.widget.analogclock + + + + + + + + + + + + + + + + + s36 + + + + + + + + + + + + + + + + + + + + + + + s37 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + s38 + + + + + + device:pim.contacts.read + + + + + + + + + s38.4 + + + + + + device:pim.contacts.read + + + + + + + + + + + + s39 + + + + + + device:pim.contacts.read + + + + + + + + + s39.4 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + + + s40 + + + + + + r40 + + + + + + + r40 + + + + + + + + + + + + s41 + + + + + + r41 + + + + + r41.2 + + + + + + + + + + + + + s42.1 + + + + + + r42.1 + + + + + r42.1 + + + + + + + + + + + + + s42.2 + + + + + + r42.2 + + + + + r42.2 + + + + + + + + + + + + s43.1 + + + + + + r43.1 + + + + + + + r43.1 + + + + + + + + + + + s43.2 + + + + + + r43.2 + + + + + + + r43.2 + + + + + + + + + + + s44.1 + + + + + + r44.1 + + + + + + + r44.1 + + + + + + + + + + + s44.2 + + + + + + r44.2 + + + + + + + r44.2 + + + + + + + + + + + + + s45.1 + + + + + + r45.1 + + + + + r45.1 + + + + + + + + + + + + s45.2 + + + + + + r45.2 + + + + + r45.2 + + + + + + + + + + + + + s46.1 + + + + + + r46.1 + + + + + r46.1 + + + + + + + + + + + + s46.2 + + + + + + r46.2 + + + + + r46.2 + + + + + + + + + + + + + s47.1 + + + + + + r47.1 + + + + + r47.1 + + + + + + + + + + + s47.2 + + + + + + r47.2 + + + + + r47.2 + + + + + + + + + + + +s48 + + + + + + + + + + + + + + + + +
diff --git a/tests/ace/test-configuration/CMTest/pms_config.xml b/tests/ace/test-configuration/CMTest/pms_config.xml new file mode 100644 index 0000000..fc9b0dc --- /dev/null +++ b/tests/ace/test-configuration/CMTest/pms_config.xml @@ -0,0 +1,10 @@ + + + + /usr/etc/ace/CMTest/active + + pms_general-test.xml + + + + diff --git a/tests/ace/test-configuration/CMTest/pms_general-test.xml b/tests/ace/test-configuration/CMTest/pms_general-test.xml new file mode 100644 index 0000000..ac55f33 --- /dev/null +++ b/tests/ace/test-configuration/CMTest/pms_general-test.xml @@ -0,0 +1,2510 @@ + + + + + + + + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + asdfkjlhxvczxnbcvnjahfjhsdfklahfdas + + + +

PValue

QValue Gvalue laj? +
+ + + modulus + + + exponent + + +
+ + Subject name + SKI + +
+
+ + + + + + + + + subject + + + + + + + + + + + + + + + + resource + + + + + + + + + + + + + + + resource + + + + + + + + + + subject3 + + + + + + + + + + + resource2 + + + + + + + + + resource2 + + + + + + + + + subject4 + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + subject5 + + + + + + + resource5 + + + + + + + + + + resource6 + + + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + + s8a + + + + + + r8 + + + + + + + + r8 + + + + + + + + + + s8b + + + + + + r8 + + + + + + + + r8 + + + + + + + + + + s8c + + + + + + r8 + + + + + + + + + + + + + + s9a + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9b + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9c + + + + + + r9 + + + + + + + + + r9 + + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9d + + + + + + r9 + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9e + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9f + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9g + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9h + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s10a + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + s10b + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + s10c + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + + + + + + + s13 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + s14 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s16 + + + + + + + + + + + + + + + + + + + + + subject4 + + + + + + + + resource4 + + + + + + + + + + + subject5 + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s19.1 + + + + + + + + resource4 + + + + + + + + + + s19.2 + + + + + + + + resource4 + + + + + + + + + + + + + s20.1 + + + + + + + + resource4 + + + + + + + + + + s20.2 + + + + + + + + resource4 + + + + + + + + + + + + + + s21 + + + + + + + + + + + + + + + + + + s23 + + + + + + + + + + + + + + + + + + + + s24 + + + + + + + + + + + + + + + + + + s25.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s26.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.1 + + + + + + + + + + + + + + + + + + + + + + + + + s27.2 + + + + + + + + + + + + + + + + + + + + + + + + + s27.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + s28 + + + + + + + + + + + + + + + + + + + + + + + + + + s29 + + + + + + + + + + + + + + + + + + + + + + + + + s30.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s33.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s34.1 + + + + + + + + + + + + + + + + + + + + + + + + s34.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.2 + + + + + + + + + + + + + + + + + + + + + + + + org.tizen.widget.analogclock + + + + + + + + + + + + + + + + + s36 + + + + + + + + + + + + + + + + + + + + + + + s37 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + s38 + + + + + + device:pim.contacts.read + + + + + + + + + s38.4 + + + + + + device:pim.contacts.read + + + + + + + + + + + + s39 + + + + + + device:pim.contacts.read + + + + + + + + + s39.4 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + + + s40 + + + + + + r40 + + + + + + + r40 + + + + + + + + + + + + s41 + + + + + + r41 + + + + + r41.2 + + + + + + + + + + + + + s42.1 + + + + + + r42.1 + + + + + r42.1 + + + + + + + + + + + + + s42.2 + + + + + + r42.2 + + + + + r42.2 + + + + + + + + + + + + s43.1 + + + + + + r43.1 + + + + + + + r43.1 + + + + + + + + + + + s43.2 + + + + + + r43.2 + + + + + + + r43.2 + + + + + + + + + + + s44.1 + + + + + + r44.1 + + + + + + + r44.1 + + + + + + + + + + + s44.2 + + + + + + r44.2 + + + + + + + r44.2 + + + + + + + + + + + + + s45.1 + + + + + + r45.1 + + + + + r45.1 + + + + + + + + + + + + s45.2 + + + + + + r45.2 + + + + + r45.2 + + + + + + + + + + + + + s46.1 + + + + + + r46.1 + + + + + r46.1 + + + + + + + + + + + + s46.2 + + + + + + r46.2 + + + + + r46.2 + + + + + + + + + + + + + s47.1 + + + + + + r47.1 + + + + + r47.1 + + + + + + + + + + + s47.2 + + + + + + r47.2 + + + + + r47.2 + + + + + + + + + + + +s48 + + + + + + + + + + + + + + + + +
diff --git a/tests/ace/test-configuration/CMTest/policyTest1.xml b/tests/ace/test-configuration/CMTest/policyTest1.xml new file mode 100644 index 0000000..ac55f33 --- /dev/null +++ b/tests/ace/test-configuration/CMTest/policyTest1.xml @@ -0,0 +1,2510 @@ + + + + + + + + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + asdfkjlhxvczxnbcvnjahfjhsdfklahfdas + + + +

PValue

QValue Gvalue laj? +
+ + + modulus + + + exponent + + +
+ + Subject name + SKI + +
+
+ + + + + + + + + subject + + + + + + + + + + + + + + + + resource + + + + + + + + + + + + + + + resource + + + + + + + + + + subject3 + + + + + + + + + + + resource2 + + + + + + + + + resource2 + + + + + + + + + subject4 + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + subject5 + + + + + + + resource5 + + + + + + + + + + resource6 + + + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + + s8a + + + + + + r8 + + + + + + + + r8 + + + + + + + + + + s8b + + + + + + r8 + + + + + + + + r8 + + + + + + + + + + s8c + + + + + + r8 + + + + + + + + + + + + + + s9a + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9b + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9c + + + + + + r9 + + + + + + + + + r9 + + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9d + + + + + + r9 + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9e + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9f + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9g + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9h + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s10a + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + s10b + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + s10c + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + + + + + + + s13 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + s14 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s16 + + + + + + + + + + + + + + + + + + + + + subject4 + + + + + + + + resource4 + + + + + + + + + + + subject5 + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s19.1 + + + + + + + + resource4 + + + + + + + + + + s19.2 + + + + + + + + resource4 + + + + + + + + + + + + + s20.1 + + + + + + + + resource4 + + + + + + + + + + s20.2 + + + + + + + + resource4 + + + + + + + + + + + + + + s21 + + + + + + + + + + + + + + + + + + s23 + + + + + + + + + + + + + + + + + + + + s24 + + + + + + + + + + + + + + + + + + s25.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s26.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.1 + + + + + + + + + + + + + + + + + + + + + + + + + s27.2 + + + + + + + + + + + + + + + + + + + + + + + + + s27.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + s28 + + + + + + + + + + + + + + + + + + + + + + + + + + s29 + + + + + + + + + + + + + + + + + + + + + + + + + s30.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s33.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s34.1 + + + + + + + + + + + + + + + + + + + + + + + + s34.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.2 + + + + + + + + + + + + + + + + + + + + + + + + org.tizen.widget.analogclock + + + + + + + + + + + + + + + + + s36 + + + + + + + + + + + + + + + + + + + + + + + s37 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + s38 + + + + + + device:pim.contacts.read + + + + + + + + + s38.4 + + + + + + device:pim.contacts.read + + + + + + + + + + + + s39 + + + + + + device:pim.contacts.read + + + + + + + + + s39.4 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + + + s40 + + + + + + r40 + + + + + + + r40 + + + + + + + + + + + + s41 + + + + + + r41 + + + + + r41.2 + + + + + + + + + + + + + s42.1 + + + + + + r42.1 + + + + + r42.1 + + + + + + + + + + + + + s42.2 + + + + + + r42.2 + + + + + r42.2 + + + + + + + + + + + + s43.1 + + + + + + r43.1 + + + + + + + r43.1 + + + + + + + + + + + s43.2 + + + + + + r43.2 + + + + + + + r43.2 + + + + + + + + + + + s44.1 + + + + + + r44.1 + + + + + + + r44.1 + + + + + + + + + + + s44.2 + + + + + + r44.2 + + + + + + + r44.2 + + + + + + + + + + + + + s45.1 + + + + + + r45.1 + + + + + r45.1 + + + + + + + + + + + + s45.2 + + + + + + r45.2 + + + + + r45.2 + + + + + + + + + + + + + s46.1 + + + + + + r46.1 + + + + + r46.1 + + + + + + + + + + + + s46.2 + + + + + + r46.2 + + + + + r46.2 + + + + + + + + + + + + + s47.1 + + + + + + r47.1 + + + + + r47.1 + + + + + + + + + + + s47.2 + + + + + + r47.2 + + + + + r47.2 + + + + + + + + + + + +s48 + + + + + + + + + + + + + + + + +
diff --git a/tests/ace/test-configuration/CMTest/policyTest2.xml b/tests/ace/test-configuration/CMTest/policyTest2.xml new file mode 100644 index 0000000..ac55f33 --- /dev/null +++ b/tests/ace/test-configuration/CMTest/policyTest2.xml @@ -0,0 +1,2510 @@ + + + + + + + + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + asdfkjlhxvczxnbcvnjahfjhsdfklahfdas + + + +

PValue

QValue Gvalue laj? +
+ + + modulus + + + exponent + + +
+ + Subject name + SKI + +
+
+ + + + + + + + + subject + + + + + + + + + + + + + + + + resource + + + + + + + + + + + + + + + resource + + + + + + + + + + subject3 + + + + + + + + + + + resource2 + + + + + + + + + resource2 + + + + + + + + + subject4 + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + subject5 + + + + + + + resource5 + + + + + + + + + + resource6 + + + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + + s8a + + + + + + r8 + + + + + + + + r8 + + + + + + + + + + s8b + + + + + + r8 + + + + + + + + r8 + + + + + + + + + + s8c + + + + + + r8 + + + + + + + + + + + + + + s9a + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9b + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9c + + + + + + r9 + + + + + + + + + r9 + + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9d + + + + + + r9 + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9e + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9f + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9g + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9h + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s10a + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + s10b + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + s10c + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + + + + + + + s13 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + s14 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s16 + + + + + + + + + + + + + + + + + + + + + subject4 + + + + + + + + resource4 + + + + + + + + + + + subject5 + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s19.1 + + + + + + + + resource4 + + + + + + + + + + s19.2 + + + + + + + + resource4 + + + + + + + + + + + + + s20.1 + + + + + + + + resource4 + + + + + + + + + + s20.2 + + + + + + + + resource4 + + + + + + + + + + + + + + s21 + + + + + + + + + + + + + + + + + + s23 + + + + + + + + + + + + + + + + + + + + s24 + + + + + + + + + + + + + + + + + + s25.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s26.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.1 + + + + + + + + + + + + + + + + + + + + + + + + + s27.2 + + + + + + + + + + + + + + + + + + + + + + + + + s27.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + s28 + + + + + + + + + + + + + + + + + + + + + + + + + + s29 + + + + + + + + + + + + + + + + + + + + + + + + + s30.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s33.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s34.1 + + + + + + + + + + + + + + + + + + + + + + + + s34.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.2 + + + + + + + + + + + + + + + + + + + + + + + + org.tizen.widget.analogclock + + + + + + + + + + + + + + + + + s36 + + + + + + + + + + + + + + + + + + + + + + + s37 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + s38 + + + + + + device:pim.contacts.read + + + + + + + + + s38.4 + + + + + + device:pim.contacts.read + + + + + + + + + + + + s39 + + + + + + device:pim.contacts.read + + + + + + + + + s39.4 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + + + s40 + + + + + + r40 + + + + + + + r40 + + + + + + + + + + + + s41 + + + + + + r41 + + + + + r41.2 + + + + + + + + + + + + + s42.1 + + + + + + r42.1 + + + + + r42.1 + + + + + + + + + + + + + s42.2 + + + + + + r42.2 + + + + + r42.2 + + + + + + + + + + + + s43.1 + + + + + + r43.1 + + + + + + + r43.1 + + + + + + + + + + + s43.2 + + + + + + r43.2 + + + + + + + r43.2 + + + + + + + + + + + s44.1 + + + + + + r44.1 + + + + + + + r44.1 + + + + + + + + + + + s44.2 + + + + + + r44.2 + + + + + + + r44.2 + + + + + + + + + + + + + s45.1 + + + + + + r45.1 + + + + + r45.1 + + + + + + + + + + + + s45.2 + + + + + + r45.2 + + + + + r45.2 + + + + + + + + + + + + + s46.1 + + + + + + r46.1 + + + + + r46.1 + + + + + + + + + + + + s46.2 + + + + + + r46.2 + + + + + r46.2 + + + + + + + + + + + + + s47.1 + + + + + + r47.1 + + + + + r47.1 + + + + + + + + + + + s47.2 + + + + + + r47.2 + + + + + r47.2 + + + + + + + + + + + +s48 + + + + + + + + + + + + + + + + +
diff --git a/tests/ace/test-configuration/CMTest/policyTest3.xml b/tests/ace/test-configuration/CMTest/policyTest3.xml new file mode 100644 index 0000000..ac55f33 --- /dev/null +++ b/tests/ace/test-configuration/CMTest/policyTest3.xml @@ -0,0 +1,2510 @@ + + + + + + + + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + + dGhpcyBpcyBub3QgYSBzaWduYXR1cmUK... + + + asdfkjlhxvczxnbcvnjahfjhsdfklahfdas + + + +

PValue

QValue Gvalue laj? +
+ + + modulus + + + exponent + + +
+ + Subject name + SKI + +
+
+ + + + + + + + + subject + + + + + + + + + + + + + + + + resource + + + + + + + + + + + + + + + resource + + + + + + + + + + subject3 + + + + + + + + + + + resource2 + + + + + + + + + resource2 + + + + + + + + + subject4 + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + subject5 + + + + + + + resource5 + + + + + + + + + + resource6 + + + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + + s8a + + + + + + r8 + + + + + + + + r8 + + + + + + + + + + s8b + + + + + + r8 + + + + + + + + r8 + + + + + + + + + + s8c + + + + + + r8 + + + + + + + + + + + + + + s9a + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9b + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9c + + + + + + r9 + + + + + + + + + r9 + + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9d + + + + + + r9 + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9e + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9f + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9g + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9h + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s10a + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + s10b + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + s10c + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + + + + + + + s13 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + s14 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s16 + + + + + + + + + + + + + + + + + + + + + subject4 + + + + + + + + resource4 + + + + + + + + + + + subject5 + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s19.1 + + + + + + + + resource4 + + + + + + + + + + s19.2 + + + + + + + + resource4 + + + + + + + + + + + + + s20.1 + + + + + + + + resource4 + + + + + + + + + + s20.2 + + + + + + + + resource4 + + + + + + + + + + + + + + s21 + + + + + + + + + + + + + + + + + + s23 + + + + + + + + + + + + + + + + + + + + s24 + + + + + + + + + + + + + + + + + + s25.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s26.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.1 + + + + + + + + + + + + + + + + + + + + + + + + + s27.2 + + + + + + + + + + + + + + + + + + + + + + + + + s27.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + s28 + + + + + + + + + + + + + + + + + + + + + + + + + + s29 + + + + + + + + + + + + + + + + + + + + + + + + + s30.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s33.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s34.1 + + + + + + + + + + + + + + + + + + + + + + + + s34.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.2 + + + + + + + + + + + + + + + + + + + + + + + + org.tizen.widget.analogclock + + + + + + + + + + + + + + + + + s36 + + + + + + + + + + + + + + + + + + + + + + + s37 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + s38 + + + + + + device:pim.contacts.read + + + + + + + + + s38.4 + + + + + + device:pim.contacts.read + + + + + + + + + + + + s39 + + + + + + device:pim.contacts.read + + + + + + + + + s39.4 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + + + s40 + + + + + + r40 + + + + + + + r40 + + + + + + + + + + + + s41 + + + + + + r41 + + + + + r41.2 + + + + + + + + + + + + + s42.1 + + + + + + r42.1 + + + + + r42.1 + + + + + + + + + + + + + s42.2 + + + + + + r42.2 + + + + + r42.2 + + + + + + + + + + + + s43.1 + + + + + + r43.1 + + + + + + + r43.1 + + + + + + + + + + + s43.2 + + + + + + r43.2 + + + + + + + r43.2 + + + + + + + + + + + s44.1 + + + + + + r44.1 + + + + + + + r44.1 + + + + + + + + + + + s44.2 + + + + + + r44.2 + + + + + + + r44.2 + + + + + + + + + + + + + s45.1 + + + + + + r45.1 + + + + + r45.1 + + + + + + + + + + + + s45.2 + + + + + + r45.2 + + + + + r45.2 + + + + + + + + + + + + + s46.1 + + + + + + r46.1 + + + + + r46.1 + + + + + + + + + + + + s46.2 + + + + + + r46.2 + + + + + r46.2 + + + + + + + + + + + + + s47.1 + + + + + + r47.1 + + + + + r47.1 + + + + + + + + + + + s47.2 + + + + + + r47.2 + + + + + r47.2 + + + + + + + + + + + +s48 + + + + + + + + + + + + + + + + +
diff --git a/tests/ace/test-configuration/CMakeLists.txt b/tests/ace/test-configuration/CMakeLists.txt new file mode 100644 index 0000000..756c13e --- /dev/null +++ b/tests/ace/test-configuration/CMakeLists.txt @@ -0,0 +1,40 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/policy-example.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/policy-wac-2.0.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/attr_policy-example.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/policy-example2.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/policy-example3.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/policy_example.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/general-test.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/interceptpolicy.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/policy-test.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/policy-test-gsettings.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/attre_config.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/attr_policy-example1.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/attr_policy-example2.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/attr_policy-example3.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/attr_policy-example4.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/attr_policy-example5.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/attr_policy-example6.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/attr_policy-example7.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/attr_policy-example8.xml + ${PROJECT_SOURCE_DIR}/tests/ace/test-configuration/undefined-test.xml + DESTINATION /usr/etc/ace + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ GROUP_WRITE + ) + +ADD_SUBDIRECTORY(CMTest) diff --git a/tests/ace/test-configuration/attr_policy-example.xml b/tests/ace/test-configuration/attr_policy-example.xml new file mode 100644 index 0000000..8f0687f --- /dev/null +++ b/tests/ace/test-configuration/attr_policy-example.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + device.simcard + + + + + + + + + + + + + + + file.write + + + + + + + + + + + + + + + + pim.contact + + + + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/attr_policy-example1.xml b/tests/ace/test-configuration/attr_policy-example1.xml new file mode 100644 index 0000000..a4869c5 --- /dev/null +++ b/tests/ace/test-configuration/attr_policy-example1.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/attr_policy-example2.xml b/tests/ace/test-configuration/attr_policy-example2.xml new file mode 100644 index 0000000..e7a85be --- /dev/null +++ b/tests/ace/test-configuration/attr_policy-example2.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/attr_policy-example3.xml b/tests/ace/test-configuration/attr_policy-example3.xml new file mode 100644 index 0000000..4d15108 --- /dev/null +++ b/tests/ace/test-configuration/attr_policy-example3.xml @@ -0,0 +1,128 @@ + + + + + + subject + + + + + + + + + + + + + + + + resource + + + + + + + + + + + + + + + resource + + + + + + + + + + subject3 + + + + + + + + + + + resource2 + + + + + + + + + resource2 + + + + + + + + + subject4 + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + + subject5 + + + + + + + resource5 + + + + + + + + + + resource6 + + + + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/attr_policy-example4.xml b/tests/ace/test-configuration/attr_policy-example4.xml new file mode 100644 index 0000000..f709233 --- /dev/null +++ b/tests/ace/test-configuration/attr_policy-example4.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + resource7 + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/attr_policy-example5.xml b/tests/ace/test-configuration/attr_policy-example5.xml new file mode 100644 index 0000000..f9c837e --- /dev/null +++ b/tests/ace/test-configuration/attr_policy-example5.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + device.simcard + + + + + + + device.simcard + + + + + + + device.simcard + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/attr_policy-example6.xml b/tests/ace/test-configuration/attr_policy-example6.xml new file mode 100644 index 0000000..7a4c709 --- /dev/null +++ b/tests/ace/test-configuration/attr_policy-example6.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + device.simcard + + + + + + + device.simcard + + + + + + + device.simcard + + + + + + + + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/attr_policy-example7.xml b/tests/ace/test-configuration/attr_policy-example7.xml new file mode 100644 index 0000000..15907f9 --- /dev/null +++ b/tests/ace/test-configuration/attr_policy-example7.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + device.simcard + + + + + + + + + + + + device.simcard + + + + + + + + + + + device.simcard + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/attr_policy-example8.xml b/tests/ace/test-configuration/attr_policy-example8.xml new file mode 100644 index 0000000..c73be53 --- /dev/null +++ b/tests/ace/test-configuration/attr_policy-example8.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/attre_config.xml b/tests/ace/test-configuration/attre_config.xml new file mode 100644 index 0000000..bd52527 --- /dev/null +++ b/tests/ace/test-configuration/attre_config.xml @@ -0,0 +1,17 @@ + + + + false + /usr/etc/ace/ + + attr_policy-example.xml + + + samsung + + + nokia + + + + diff --git a/tests/ace/test-configuration/general-test.xml b/tests/ace/test-configuration/general-test.xml new file mode 100644 index 0000000..c67eaa6 --- /dev/null +++ b/tests/ace/test-configuration/general-test.xml @@ -0,0 +1,2621 @@ + + + + + + subject + + + + + + + + + + + + + + + + resource + + + + + + + + + + + + + + + resource + + + + + + + + + + subject3 + + + + + + + + + + + resource2 + + + + + + + + + resource2 + + + + + + + + + subject4 + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + subject5 + + + + + + + resource5 + + + + + + + + + + resource6 + + + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + + s8a + + + + + + r8 + + + + + + + + r8 + + + + + + + + + + s8b + + + + + + r8 + + + + + + + + r8 + + + + + + + + + + s8c + + + + + + r8 + + + + + + + + + + + + + + s9a + + + + + + r9 + + + + + + r9 + + + + + + + + + + s9b + + + + + + r9 + + + + + + r9 + + + + + + + + + + s9c + + + + + + r9 + + + + + + r9 + + + + + + r9 + + + + + + r9 + + + + + + + + + + s9d + + + + + + r9 + + + + + + r9 + + + + + + r9 + + + + + + r9 + + + + + + + + + + s9e + + + + + + r9 + + + + + + r9 + + + + + + + + + + s9f + + + + + + r9 + + + + + + r9 + + + + + + + + + + s9g + + + + + + r9 + + + + + + r9 + + + + + + + + + + s9h + + + + + + r9 + + + + + + r9 + + + + + + + + + + s10a + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + s10b + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + s10c + + + + + + r10 + + + + + + r10 + + + + + + + + + + + + + + + + s13 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + + + + + + + + + + + + + + s16 + + + + + + + + + + + + + + + + + + + + + s17a + + + + + + + + resource4 + + + + + + + + + + + s17b + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s19.1 + + + + + + + + resource4 + + + + + + + + + + s19.2 + + + + + + + + resource4 + + + + + + + + + + + + + s20.1 + + + + + + + + resource4 + + + + + + + + + + s20.2 + + + + + + + + resource4 + + + + + + + + + + + + + + s21 + + + + + + + + + + + + + + + + s21a + + + + + + + + + + + + + + + + + s23 + + + + + + + + + + + + + + + + + + + + s24 + + + + + + + + + + + + + + + + + + s25.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.3 + + + + + + + + + + + + + + + + + + + + + + + + + s25.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s26.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.1 + + + + + + + + + + + + + + + + + + + + + + + + + s27.2 + + + + + + + + + + + + + + + + + + + + + + + + + s27.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + s28 + + + + + + + + + + + + + + + + + + + + + + + + + + s29 + + + + + + + + + + + + + + + + + + + + + + + + + s30.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s33.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s34.1 + + + + + + + + + + + + + + + + + + + + + + + + s34.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.2 + + + + + + + + + + + + + + + + + + + + + + + + org.tizen.widget.analogclock + + + + + + + + + + + + + + + + + s36 + + + + + + + + + + + + + + + + + + + + + + + s37 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + s38 + + + + + + device:pim.contacts.read + + + + + + + + + s38.4 + + + + + + device:pim.contacts.read + + + + + + + + + + + + s39 + + + + + + device:pim.contacts.read + + + + + + + + + s39.4 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + + + s40 + + + + + + r40 + + + + + + + r40 + + + + + + + + + + + + s41 + + + + + + r41 + + + + + r41.2 + + + + + + + + + + + + + s42.1 + + + + + + r42.1 + + + + + r42.1 + + + + + + + + + + + + + s42.2 + + + + + + r42.2 + + + + + r42.2 + + + + + + + + + + + + s43.1 + + + + + + r43.1 + + + + + + + r43.1 + + + + + + + + + + + s43.2 + + + + + + r43.2 + + + + + + + r43.2 + + + + + + + + + + + s44.1 + + + + + + r44.1 + + + + + + + r44.1 + + + + + + + + + + + s44.2 + + + + + + r44.2 + + + + + + + r44.2 + + + + + + + + + + + + + s45.1 + + + + + + r45.1 + + + + + r45.1 + + + + + + + + + + + + s45.2 + + + + + + r45.2 + + + + + r45.2 + + + + + + + + + + + + + s46.1 + + + + + + r46.1 + + + + + r46.1 + + + + + + + + + + + + s46.2 + + + + + + r46.2 + + + + + r46.2 + + + + + + + + + + + + + s47.1 + + + + + + r47.1 + + + + + r47.1 + + + + + + + + + + + s47.2 + + + + + + r47.2 + + + + + r47.2 + + + + + + + + + + + + s48 + + + + + + + + + + + + + + + + + + + BF00 + + + + + + + + + + + + + + + + + + + + + + + + BF01 + + + + + + + + + + + + + + + + + + + + + + + + + BF02 + + + + + + + + + + + + + + + + + + + + + + + + BF03 + + + + + + + + + + + + + + + + + + + + + + + + + BF04 + + + + + + BFR04 + + + + + BFR04 + + + + + + + + + + + s61a + + + + + + r61a + type + + + + + r61a + + + + + + + + + s61b + + + + + + r61b + type + + + + + r61b + + + + + + + + + + s61c + + + + + + r61c + type + port + + + + + r61c + + + + + + + + + s61d + + + + + + r61d + type + port + + + + + r61d + + + + + + + + + + paramTestSubject + + + + + + messaging + +4409* + + + + + messaging + +4408* + + + + + messaging + +48* + + + + + camera + high + + + + + camera + low + + + + + + + + diff --git a/tests/ace/test-configuration/interceptpolicy.xml b/tests/ace/test-configuration/interceptpolicy.xml new file mode 100644 index 0000000..ab055ce --- /dev/null +++ b/tests/ace/test-configuration/interceptpolicy.xml @@ -0,0 +1,495 @@ + + + + + + + Unidentified + + + + + + + + http://jil.org/jil/api/1.1/device + + + getAvailableApplications + + + + + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.update + + + http://jil.org/jil/api/1.1/multimedia.Camera.captureImage + + + http://jil.org/jil/api/1.1/device.Device.launchApplication + + + DeviceStateInfo.requestPositionInfo + + + http://jil.org/jil/api/1.1/messaging.Messaging.sendMessage + + + + + + + http://jil.org/jil/api/1.1.1/pim.PIM.findAddressBookItems + + + http://jil.org/jil/api/1.1.1/pim.PIM.getAddressBookItem + + + http://jil.org/jil/api/1.1.1/pim.PIM.getAddressBookItemsCount + + + + + + + + Device.PositionInfo + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.getAttributeValue + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.getAvailableAttributes + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.setAttributeValue + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.open + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.play + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.pause + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.resume + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.stop + + + AudioPlayer.onStateChange + + + http://jil.org/jil/api/1.1/multimedia.Camera.onCameraCaptured + + + http://jil.org/jil/api/1.1/multimedia.Camera.setWindow + + + Device.PositionInfo + + + Device.DeviceStateInfo + + + DeviceStateInfo.onPositionRetrieved + + + http://jil.org/jil/api/1.1/messaging.Messaging.createMessage + + + Messaging.onMessageSendingFailure + + + Multimedia.getVolume + + + Multimedia.stopAll + + + Multimedia.isAudioPlaying + + + http://jil.org/jil/api/1.1.1/pim.PIM.createAddressBookItem + + + PIM.onAddressBookItemFound + + + + + + http://jil.org/jil/api/1.1/accelerometerinfo + + + http://jil.org/jil/api/1.1/addressbookitem + + + http://jil.org/jil/api/1.1.5/applicationtypes + + + http://jil.org/jil/api/1.1.2/camera + + + http://jil.org/jil/api/1.1/device + + + http://jil.org/jil/api/1.1/devicestateinfo + + + http://jil.org/jil/api/1.1.5/exception + + + http://jil.org/jil/api/1.1.5/exceptiontypes + + + http://jil.org/jil/api/1.1/message + + + http://jil.org/jil/api/1.1/messagetypes + + + http://jil.org/jil/api/1.1/messaging + + + http://jil.org/jil/api/1.1/multimedia + + + http://jil.org/jil/api/1.1.1/pim + + + http://jil.org/jil/api/1.1/positioninfo + + + http://jil.org/jil/api/1.1/widget + + + + + + + + + + + + Identified + + + + + + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.update + + + DeviceStateInfo.requestPositionInfo + + + + + + + http://jil.org/jil/api/1.1/device.Device.launchApplication + + + http://jil.org/jil/api/1.1/multimedia.Camera.captureImage + + + http://jil.org/jil/api/1.1/messaging.Messaging.sendMessage + + + http://jil.org/jil/api/1.1.1/pim.PIM.findAddressBookItems + + + http://jil.org/jil/api/1.1.1/pim.PIM.getAddressBookItem + + + http://jil.org/jil/api/1.1.1/pim.PIM.getAddressBookItemsCount + + + + + + + + http://jil.org/jil/api/1.1/device.Device.getAvailableApplications + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.getAttributeValue + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.getAvailableAttributes + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.setAttributeValue + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.open + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.play + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.pause + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.resume + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.stop + + + AudioPlayer.onStateChange + + + http://jil.org/jil/api/1.1/multimedia.Camera.onCameraCaptured + + + http://jil.org/jil/api/1.1/multimedia.Camera.setWindow + + + Device.PositionInfo + + + Device.DeviceStateInfo + + + DeviceStateInfo.onPositionRetrieved + + + http://jil.org/jil/api/1.1/messaging.Messaging.createMessage + + + Messaging.onMessageSendingFailure + + + Multimedia.getVolume + + + Multimedia.stopAll + + + Multimedia.isAudioPlaying + + + http://jil.org/jil/api/1.1.1/pim.PIM.createAddressBookItem + + + PIM.onAddressBookItemFound + + + + + + http://jil.org/jil/api/1.1/accelerometerinfo + + + http://jil.org/jil/api/1.1/addressbookitem + + + http://jil.org/jil/api/1.1.5/applicationtypes + + + http://jil.org/jil/api/1.1.2/camera + + + http://jil.org/jil/api/1.1/device + + + http://jil.org/jil/api/1.1/devicestateinfo + + + http://jil.org/jil/api/1.1.5/exception + + + http://jil.org/jil/api/1.1.5/exceptiontypes + + + http://jil.org/jil/api/1.1/message + + + http://jil.org/jil/api/1.1/messagetypes + + + http://jil.org/jil/api/1.1/messaging + + + http://jil.org/jil/api/1.1/multimedia + + + http://jil.org/jil/api/1.1.1/pim + + + http://jil.org/jil/api/1.1/positioninfo + + + http://jil.org/jil/api/1.1/widget + + + + + + + + + + + + Operator + + + + + + + + http://jil.org/jil/api/1.1/device.Device.launchApplication + + + DeviceStateInfo.requestPositionInfo + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.update + + + http://jil.org/jil/api/1.1/device.Device.getAvailableApplications + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.getAttributeValue + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.getAvailableAttributes + + + http://jil.org/jil/api/1.1/addressbookitem.AddressBookItem.setAttributeValue + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.open + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.play + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.pause + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.resume + + + http://jil.org/jil/api/1.1/multimedia.AudioPlayer.stop + + + AudioPlayer.onStateChange + + + http://jil.org/jil/api/1.1/multimedia.Camera.onCameraCaptured + + + http://jil.org/jil/api/1.1/multimedia.Camera.setWindow + + + http://jil.org/jil/api/1.1/multimedia.Camera.captureImage + + + Device.PositionInfo + + + Device.DeviceStateInfo + + + DeviceStateInfo.onPositionRetrieved + + + http://jil.org/jil/api/1.1/messaging.Messaging.createMessage + + + http://jil.org/jil/api/1.1/messaging.Messaging.sendMessage + + + Messaging.onMessageSendingFailure + + + Multimedia.getVolume + + + Multimedia.stopAll + + + Multimedia.isAudioPlaying + + + http://jil.org/jil/api/1.1.1/pim.PIM.createAddressBookItem + + + PIM.onAddressBookItemFound + + + http://jil.org/jil/api/1.1.1/pim.PIM.findAddressBookItems + + + http://jil.org/jil/api/1.1.1/pim.PIM.getAddressBookItem + + + http://jil.org/jil/api/1.1.1/pim.PIM.getAddressBookItemsCount + + + + + + http://jil.org/jil/api/1.1/accelerometerinfo + + + http://jil.org/jil/api/1.1/addressbookitem + + + http://jil.org/jil/api/1.1.5/applicationtypes + + + http://jil.org/jil/api/1.1.2/camera + + + http://jil.org/jil/api/1.1/device + + + http://jil.org/jil/api/1.1/devicestateinfo + + + http://jil.org/jil/api/1.1.5/exception + + + http://jil.org/jil/api/1.1.5/exceptiontypes + + + http://jil.org/jil/api/1.1/message + + + http://jil.org/jil/api/1.1/messagetypes + + + http://jil.org/jil/api/1.1/messaging + + + http://jil.org/jil/api/1.1/multimedia + + + http://jil.org/jil/api/1.1.1/pim + + + http://jil.org/jil/api/1.1/positioninfo + + + http://jil.org/jil/api/1.1/widget + + + + + + + diff --git a/tests/ace/test-configuration/old_policy-example.xml b/tests/ace/test-configuration/old_policy-example.xml new file mode 100644 index 0000000..d7ce388 --- /dev/null +++ b/tests/ace/test-configuration/old_policy-example.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + device.simcard + + + + + + + + + + + + + + + file.write + + + + + + + + + + + + + + + + pim.contact + + + + + + + diff --git a/tests/ace/test-configuration/policy-example.xml b/tests/ace/test-configuration/policy-example.xml new file mode 100644 index 0000000..c0e0857 --- /dev/null +++ b/tests/ace/test-configuration/policy-example.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + devicestatus + + + + + + + + + + + + + + + + devicestatus + + + + + + + + + + + + + + + http://bondi.omtp.org/api.appconfig + + + + + + + + + + + + + + + + + + + + + file.write + + + + + + + + + + + + + + + + pim.contact + + + + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/policy-example2.xml b/tests/ace/test-configuration/policy-example2.xml new file mode 100644 index 0000000..dded65c --- /dev/null +++ b/tests/ace/test-configuration/policy-example2.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + device.simcard + + + + + + + + + + + + + + + file.write + + + + + + + + + + + + + + + + pim.contact + + + + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/policy-example3.xml b/tests/ace/test-configuration/policy-example3.xml new file mode 100644 index 0000000..ef0fddc --- /dev/null +++ b/tests/ace/test-configuration/policy-example3.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + device.simcard + + + + + + + + + + + + + + + file.write + + + + + + + + + + + + + + + + pim.contact + + + + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/policy-test-gsettings.xml b/tests/ace/test-configuration/policy-test-gsettings.xml new file mode 100644 index 0000000..2a242f6 --- /dev/null +++ b/tests/ace/test-configuration/policy-test-gsettings.xml @@ -0,0 +1,41 @@ + + + + + a1 + + + + + + d3 + + + + + + + aa2 + + + + + + bb2 + + + + + + + + c3 + + + + + d3 + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/policy-test.xml b/tests/ace/test-configuration/policy-test.xml new file mode 100644 index 0000000..9094ee4 --- /dev/null +++ b/tests/ace/test-configuration/policy-test.xml @@ -0,0 +1,41 @@ + + + + + a1 + + + + + + b1 + + + + + + + aa2 + + + + + + bb2 + + + + + + + + c3 + + + + + d3 + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/policy-wac-2.0.xml b/tests/ace/test-configuration/policy-wac-2.0.xml new file mode 100644 index 0000000..cf59001 --- /dev/null +++ b/tests/ace/test-configuration/policy-wac-2.0.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/ace/test-configuration/policy_example.xml b/tests/ace/test-configuration/policy_example.xml new file mode 100644 index 0000000..333422d --- /dev/null +++ b/tests/ace/test-configuration/policy_example.xml @@ -0,0 +1,2407 @@ + + + + + + subject + + + + + + + + + + + + + + + + resource + + + + + + + + + + + + + + + resource + + + + + + + + + + subject3 + + + + + + + + + + + resource2 + + + + + + + + + resource2 + + + + + + + + + subject4 + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + subject5 + + + + + + + resource5 + + + + + + + + + + resource6 + + + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + resource7 + + + + + + + + + + + + + + + + + s8a + + + + + + r8 + + + + + + + + r8 + + + + + + + + + + s8b + + + + + + r8 + + + + + + + + r8 + + + + + + + + + + s8c + + + + + + r8 + + + + + + + + + + + + + + s9a + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9b + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9c + + + + + + r9 + + + + + + + + + r9 + + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9d + + + + + + r9 + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9e + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9f + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9g + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s9h + + + + + + r9 + + + + + + + + r9 + + + + + + + + + + s10a + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + s10b + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + s10c + + + + + + r10 + + + + + + + + r10 + + + + + + + + + + + + + + + + s13 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + s14 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s16 + + + + + + + + + + + + + + + + + + + + + subject4 + + + + + + + + resource4 + + + + + + + + + + + subject5 + + + + + + + + resource4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s19.1 + + + + + + + + resource4 + + + + + + + + + + s19.2 + + + + + + + + resource4 + + + + + + + + + + + + + s20.1 + + + + + + + + resource4 + + + + + + + + + + s20.2 + + + + + + + + resource4 + + + + + + + + + + + + + + s21 + + + + + + + + + + + + + + + + + + s23 + + + + + + + + + + + + + + + + + + + + s24 + + + + + + + + + + + + + + + + + + s25.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + s25.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s26.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.1 + + + + + + + + + + + + + + + + + + + + + + + + + s27.2 + + + + + + + + + + + + + + + + + + + + + + + + + s27.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + s28 + + + + + + + + + + + + + + + + + + + + + + + + + + s29 + + + + + + + + + + + + + + + + + + + + + + + + + s30.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s33.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s34.1 + + + + + + + + + + + + + + + + + + + + + + + + s34.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.2 + + + + + + + + + + + + + + + + + + + + + + + + org.tizen.widget.analogclock + + + + + + + + + + + + + + + + + s36 + + + + + + + + + + + + + + + + + + + + + + + s37 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + s38 + + + + + + device:pim.contacts.read + + + + + + + + + s38.4 + + + + + + device:pim.contacts.read + + + + + + + + + + + + s39 + + + + + + device:pim.contacts.read + + + + + + + + + s39.4 + + + + + + device:pim.contacts.read + + + + + + + + + + + + + + + s40 + + + + + + r40 + + + + + + + r40 + + + + + + + + + + + + s41 + + + + + + r41 + + + + + r41.2 + + + + + + + + + + + + + s42.1 + + + + + + r42.1 + + + + + r42.1 + + + + + + + + + + + + + s42.2 + + + + + + r42.2 + + + + + r42.2 + + + + + + + + + + + + s43.1 + + + + + + r43.1 + + + + + + + r43.1 + + + + + + + + + + + s43.2 + + + + + + r43.2 + + + + + + + r43.2 + + + + + + + + + + + s44.1 + + + + + + r44.1 + + + + + + + r44.1 + + + + + + + + + + + s44.2 + + + + + + r44.2 + + + + + + + r44.2 + + + + + + + + + + + + + s45.1 + + + + + + r45.1 + + + + + r45.1 + + + + + + + + + + + + s45.2 + + + + + + r45.2 + + + + + r45.2 + + + + + + + + + + + + + s46.1 + + + + + + r46.1 + + + + + r46.1 + + + + + + + + + + + + s46.2 + + + + + + r46.2 + + + + + r46.2 + + + + + + + + + + + + + s47.1 + + + + + + r47.1 + + + + + r47.1 + + + + + + + + + + + s47.2 + + + + + + r47.2 + + + + + r47.2 + + + + + + + + + + + +s48 + + + + + + + + + + + + + + + diff --git a/tests/ace/test-configuration/reproduce-abort-test.xml b/tests/ace/test-configuration/reproduce-abort-test.xml new file mode 100644 index 0000000..ee2de20 --- /dev/null +++ b/tests/ace/test-configuration/reproduce-abort-test.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/ace/test-configuration/undefined-test.xml b/tests/ace/test-configuration/undefined-test.xml new file mode 100644 index 0000000..3da1b1c --- /dev/null +++ b/tests/ace/test-configuration/undefined-test.xml @@ -0,0 +1,1075 @@ + + + + + s25.2 + + + + + + + + + + + + + + + + + + + + + + s25.3 + + + + + + + + + + + + + + + + + + + + + + + + + s25.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s26.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s26.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.1 + + + + + + + + + + + + + + + + + + + + + + + + + s27.2 + + + + + + + + + + + + + + + + + + + + + + + + + s27.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + s27.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + s28 + + + + + + + + + + + + + + + + + + + + + + + + + + s29 + + + + + + + + + + + + + + + + + + + + + + + + + s30.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s30.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s31.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s32.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s33.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s33.3 + + + + + + + + + + + + + + + + + + + + + + + + + + s34.1 + + + + + + + + + + + + + + + + + + + + + + + + s34.2 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.1 + + + + + + + + + + + + + + + + + + + + + + + + + + s35.2 + + + + + + + + + + + + + + + + + + + + + + + + org.tizen.widget.analogclock + + + + + + + + + + + + + + + + + s36 + + + + + + + + + + + + + + diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt new file mode 100644 index 0000000..c1fbd82 --- /dev/null +++ b/tests/core/CMakeLists.txt @@ -0,0 +1,90 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @version 1.0 +# @brief +# + +# +# Test files +# +# Define all DPL tests sources. +# Runner is responsible for runnint it all and +# generating proper output files +# + +SET(TARGET_NAME "dpl-tests-core") + +# Set DPL tests sources +SET(DPL_TESTS_SOURCES + ${PROJECT_SOURCE_DIR}/tests/core/main.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_address.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_binary_queue.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_foreach.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_fast_delegate.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_log_unhandled_exception.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_once.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_serialization.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_scoped_array.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_scoped_close.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_scoped_fclose.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_scoped_free.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_scoped_ptr.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_semaphore.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_shared_ptr.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_string.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_task.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_thread.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_type_list.cpp + ${PROJECT_SOURCE_DIR}/tests/core/test_zip_input.cpp +) + +INCLUDE_DIRECTORIES( + ${SYS_EFL_INCLUDE_DIRS} + ${DPL_TEST_INCLUDE_DIR} +) + +LINK_DIRECTORIES(${SYS_EFL_LIBRARY_DIRS}) + +ADD_EXECUTABLE(${TARGET_NAME} ${DPL_TESTS_SOURCES}) + +TARGET_LINK_LIBRARIES( + ${TARGET_NAME} + ${SYS_EFL_LIBRARIES} + ${TARGET_DPL_EFL} + ${TARGET_DPL_TEST_ENGINE_EFL} +) + +SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + BUILD_WITH_INSTALL_RPATH ON + INSTALL_RPATH_USE_LINK_PATH ON +) + +INSTALL(TARGETS ${TARGET_NAME} + DESTINATION bin + PERMISSIONS OWNER_READ + OWNER_WRITE + OWNER_EXECUTE + GROUP_READ + GROUP_EXECUTE + WORLD_READ + WORLD_EXECUTE +) + +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/tests/core/data/sample.zip + DESTINATION /opt/apps/wrt/wrt-commons/tests/core +) \ No newline at end of file diff --git a/tests/core/DESCRIPTION b/tests/core/DESCRIPTION new file mode 100644 index 0000000..48e5394 --- /dev/null +++ b/tests/core/DESCRIPTION @@ -0,0 +1,2 @@ +!!!options!!! stop +Test code diff --git a/tests/core/data/sample.zip b/tests/core/data/sample.zip new file mode 100644 index 0000000000000000000000000000000000000000..02417d89316a2de5eb658decfa83dc60e366c8c7 GIT binary patch literal 174 zcmWIWW@h1H0D-h9e>XlTKgobc3Fe?N{`GRn11vdjD z%L`_pLJ(1sT3iy~&B!FjjLRemkOd5kK)j?8#6mWg6=E)$nE~FcY#^14Ko|g|JwO}= E0Bw;T(f|Me literal 0 HcmV?d00001 diff --git a/tests/core/main.cpp b/tests/core/main.cpp new file mode 100644 index 0000000..42ffe3a --- /dev/null +++ b/tests/core/main.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file main.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of main + */ +#include + +int main(int argc, char *argv[]) +{ + return DPL::Test::TestRunnerSingleton::Instance().ExecTestRunner(argc, argv); +} + diff --git a/tests/core/test_address.cpp b/tests/core/test_address.cpp new file mode 100644 index 0000000..7aff2df --- /dev/null +++ b/tests/core/test_address.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_address.cpp + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of test address + */ +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +RUNNER_TEST(Address_InitialEmpty) +{ + DPL::Address address; + RUNNER_ASSERT(address.ToString() == ":0"); +} + +RUNNER_TEST(Address_InitialAddress) +{ + DPL::Address address("www.sample.com"); + RUNNER_ASSERT(address.ToString() == "www.sample.com:0"); +} + +RUNNER_TEST(Address_InitialAddressPort) +{ + DPL::Address address("www.somewhere.com", 8080); + RUNNER_ASSERT(address.ToString() == "www.somewhere.com:8080"); +} + +RUNNER_TEST(Address_Getters) +{ + DPL::Address address("www.somewhere.com", 8080); + RUNNER_ASSERT(address.GetAddress() == "www.somewhere.com"); + RUNNER_ASSERT(address.GetPort() == 8080); +} diff --git a/tests/core/test_binary_queue.cpp b/tests/core/test_binary_queue.cpp new file mode 100644 index 0000000..d955aab --- /dev/null +++ b/tests/core/test_binary_queue.cpp @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_binary_queue.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of test binary queue + */ +#include +#include +RUNNER_TEST_GROUP_INIT(DPL) + +inline std::string BinaryQueueToString(const DPL::BinaryQueue &queue) +{ + char *buffer = new char[queue.Size()]; + queue.Flatten(buffer, queue.Size()); + std::string result = std::string(buffer, buffer + queue.Size()); + delete [] buffer; + return result; +} + +RUNNER_TEST(BinaryQueue_InitialEmpty) +{ + DPL::BinaryQueue queue; + RUNNER_ASSERT(queue.Empty() == true); +} + +RUNNER_TEST(BinaryQueue_InitialSize) +{ + DPL::BinaryQueue queue; + RUNNER_ASSERT(queue.Size() == 0); +} + +RUNNER_TEST(BinaryQueue_InitialCopy) +{ + DPL::BinaryQueue queue; + DPL::BinaryQueue copy = queue; + + RUNNER_ASSERT(copy.Size() == 0); +} + +RUNNER_TEST(BinaryQueue_InitialConsumeZero) +{ + DPL::BinaryQueue queue; + queue.Consume(0); +} + +RUNNER_TEST(BinaryQueue_InitialFlattenConsumeZero) +{ + DPL::BinaryQueue queue; + queue.FlattenConsume(NULL, 0); +} + +RUNNER_TEST(BinaryQueue_InitialFlattenZero) +{ + DPL::BinaryQueue queue; + queue.Flatten(NULL, 0); +} + +RUNNER_TEST(BinaryQueue_InitialConsumeOne) +{ + DPL::BinaryQueue queue; + + Try + { + queue.Consume(1); + } + Catch (DPL::BinaryQueue::Exception::OutOfData) + { + return; + } + + RUNNER_FAIL; +} + +RUNNER_TEST(BinaryQueue_InitialFlattenConsumeOne) +{ + DPL::BinaryQueue queue; + + Try + { + char data; + queue.FlattenConsume(&data, 1); + } + Catch (DPL::BinaryQueue::Exception::OutOfData) + { + return; + } + + RUNNER_FAIL; +} + +RUNNER_TEST(BinaryQueue_InitialFlattenOne) +{ + DPL::BinaryQueue queue; + + Try + { + char data; + queue.Flatten(&data, 1); + } + Catch (DPL::BinaryQueue::Exception::OutOfData) + { + return; + } + + RUNNER_FAIL; +} + +RUNNER_TEST(BinaryQueue_ZeroCopyFrom) +{ + DPL::BinaryQueue queue; + DPL::BinaryQueue copy; + + copy.AppendCopyFrom(queue); + RUNNER_ASSERT(queue.Empty()); +} + +RUNNER_TEST(BinaryQueue_ZeroMoveFrom) +{ + DPL::BinaryQueue queue; + DPL::BinaryQueue copy; + + copy.AppendMoveFrom(queue); + RUNNER_ASSERT(queue.Empty()); +} + +RUNNER_TEST(BinaryQueue_ZeroCopyTo) +{ + DPL::BinaryQueue queue; + DPL::BinaryQueue copy; + + queue.AppendCopyTo(copy); + RUNNER_ASSERT(queue.Empty()); +} + +RUNNER_TEST(BinaryQueue_InsertSingleCharacters) +{ + DPL::BinaryQueue queue; + + queue.AppendCopy("a", 1); + queue.AppendCopy("b", 1); + queue.AppendCopy("c", 1); + queue.AppendCopy("d", 1); + + RUNNER_ASSERT(queue.Size() == 4); + RUNNER_ASSERT(BinaryQueueToString(queue) == "abcd"); +} + +RUNNER_TEST(BinaryQueue_Consume) +{ + DPL::BinaryQueue queue; + + queue.AppendCopy("abcd", 4); + queue.AppendCopy("ef", 2); + + RUNNER_ASSERT(queue.Size() == 6); + + queue.Consume(1); + RUNNER_ASSERT(queue.Size() == 5); + RUNNER_ASSERT(BinaryQueueToString(queue) == "bcdef"); + + queue.Consume(2); + RUNNER_ASSERT(queue.Size() == 3); + RUNNER_ASSERT(BinaryQueueToString(queue) == "def"); + + queue.Consume(1); + RUNNER_ASSERT(queue.Size() == 2); + RUNNER_ASSERT(BinaryQueueToString(queue) == "ef"); + + queue.Consume(2); + RUNNER_ASSERT(queue.Size() == 0); + RUNNER_ASSERT(BinaryQueueToString(queue) == ""); +} + +RUNNER_TEST(BinaryQueue_Flatten) +{ + DPL::BinaryQueue queue; + + queue.AppendCopy("abcd", 4); + queue.AppendCopy("ef", 2); + queue.AppendCopy("g", 1); + + RUNNER_ASSERT(queue.Size() == 7); + + RUNNER_ASSERT(BinaryQueueToString(queue) == "abcdefg"); +} + +RUNNER_TEST(BinaryQueue_FlattenConsume) +{ + DPL::BinaryQueue queue; + + queue.AppendCopy("abcd", 4); + queue.AppendCopy("ef", 2); + + RUNNER_ASSERT(queue.Size() == 6); + + char buffer[7] = { '\0' }; + queue.FlattenConsume(buffer, 3); + + RUNNER_ASSERT(queue.Size() == 3); + RUNNER_ASSERT(BinaryQueueToString(queue) == "def"); +} + +RUNNER_TEST(BinaryQueue_AppendCopyFrom) +{ + DPL::BinaryQueue queue; + DPL::BinaryQueue copy; + + queue.AppendCopy("abcd", 4); + queue.AppendCopy("ef", 2); + + copy.AppendCopyFrom(queue); + + RUNNER_ASSERT(queue.Size() == 6); + RUNNER_ASSERT(copy.Size() == 6); + RUNNER_ASSERT(BinaryQueueToString(queue) == "abcdef"); + RUNNER_ASSERT(BinaryQueueToString(copy) == "abcdef"); +} + +RUNNER_TEST(BinaryQueue_AppendCopyTo) +{ + DPL::BinaryQueue queue; + DPL::BinaryQueue copy; + + queue.AppendCopy("abcd", 4); + queue.AppendCopy("ef", 2); + + queue.AppendCopyTo(copy); + + RUNNER_ASSERT(queue.Size() == 6); + RUNNER_ASSERT(copy.Size() == 6); + RUNNER_ASSERT(BinaryQueueToString(queue) == "abcdef"); + RUNNER_ASSERT(BinaryQueueToString(copy) == "abcdef"); +} + +RUNNER_TEST(BinaryQueue_AppendMoveFrom) +{ + DPL::BinaryQueue queue; + DPL::BinaryQueue copy; + + queue.AppendCopy("abcd", 4); + queue.AppendCopy("ef", 2); + + copy.AppendMoveFrom(queue); + + RUNNER_ASSERT(queue.Size() == 0); + RUNNER_ASSERT(copy.Size() == 6); + RUNNER_ASSERT(BinaryQueueToString(queue) == ""); + RUNNER_ASSERT(BinaryQueueToString(copy) == "abcdef"); +} + +RUNNER_TEST(BinaryQueue_AppendMoveTo) +{ + DPL::BinaryQueue queue; + DPL::BinaryQueue copy; + + queue.AppendCopy("abcd", 4); + queue.AppendCopy("ef", 2); + + queue.AppendMoveTo(copy); + + RUNNER_ASSERT(queue.Size() == 0); + RUNNER_ASSERT(copy.Size() == 6); + RUNNER_ASSERT(BinaryQueueToString(queue) == ""); + RUNNER_ASSERT(BinaryQueueToString(copy) == "abcdef"); +} + +class Visitor + : public DPL::BinaryQueue::BucketVisitor +{ +private: + int m_index; + +public: + Visitor() + : m_index(0) + { + } + + virtual void OnVisitBucket(const void *buffer, size_t bufferSize) + { + const char *str = static_cast(buffer); + + if (m_index == 0) + { + RUNNER_ASSERT(bufferSize == 4); + RUNNER_ASSERT(str[0] == 'a'); + RUNNER_ASSERT(str[1] == 'b'); + RUNNER_ASSERT(str[2] == 'c'); + RUNNER_ASSERT(str[3] == 'd'); + } + else if (m_index == 1) + { + RUNNER_ASSERT(bufferSize == 2); + RUNNER_ASSERT(str[0] == 'e'); + RUNNER_ASSERT(str[1] == 'f'); + } + else + { + RUNNER_FAIL; + } + + ++m_index; + } +}; + +RUNNER_TEST(BinaryQueue_Visitor) +{ + DPL::BinaryQueue queue; + + queue.AppendCopy("abcd", 4); + queue.AppendCopy("ef", 2); + + Visitor visitor; + queue.VisitBuckets(&visitor); +} + +RUNNER_TEST(BinaryQueue_AbstracInputRead) +{ + DPL::BinaryQueue queue; + + queue.AppendCopy("abcd", 4); + + queue.Read(0); + + RUNNER_ASSERT(BinaryQueueToString(*queue.Read(1).get()) == "a"); + RUNNER_ASSERT(BinaryQueueToString(*queue.Read(2).get()) == "bc"); + RUNNER_ASSERT(BinaryQueueToString(*queue.Read(1).get()) == "d"); + + RUNNER_ASSERT(queue.Size() == 0); +} + +RUNNER_TEST(BinaryQueue_AbstracOutputWrite) +{ + DPL::BinaryQueue queue; + queue.AppendCopy("abcd", 4); + + DPL::BinaryQueue stream; + + stream.Write(queue, 4); + + RUNNER_ASSERT(BinaryQueueToString(*queue.Read(4).get()) == "abcd"); +} diff --git a/tests/core/test_fast_delegate.cpp b/tests/core/test_fast_delegate.cpp new file mode 100644 index 0000000..947cbe5 --- /dev/null +++ b/tests/core/test_fast_delegate.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_fast_delegate.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of fast delegate tests. + */ +#include +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +// Sample copied and adopted from +// http://www.codeproject.com/KB/cpp/FastDelegate.aspx +// +// Demonstrate the syntax for FastDelegates. +// -Don Clugston, May 2004. +// It's a really boring example, but it shows the most important cases. +// Declare some functions of varying complexity... +void SimpleStaticFunction(int num, const char *str); +void SimpleStaticFunction(int num, const char *str) +{ + LogDebug("In SimpleStaticFunction. Num=" << num << ", str =" << str); +} + +void SimpleVoidFunction(); +void SimpleVoidFunction() +{ + LogDebug("In SimpleVoidFunction with no parameters."); +} + +class CBaseClass +{ +protected: + const char *m_name; + +public: + CBaseClass(const char *name) + : m_name(name) + { + } + + virtual ~CBaseClass() + { + } + + void SimpleMemberFunction(int num, const char *str) + { + LogDebug("In SimpleMemberFunction in " << m_name << ". Num=" + << num << ", str = " << str); + } + + int SimpleMemberFunctionReturnsInt(int num, const char *str) + { + LogDebug("In SimpleMemberFunctionReturnsInt in " << m_name << ". Num=" + << num << ", str = " << str); + return -1; + } + + void ConstMemberFunction(int num, const char *str) const + { + LogDebug("In ConstMemberFunction in " << m_name << ". Num=" + << num << ", str = " << str); + } + + virtual void SimpleVirtualFunction(int num, const char *str) + { + LogDebug("In SimpleVirtualFunction in " << m_name << ". Num=" + << num << ", str = " << str); + } + + static void StaticMemberFunction(int num, const char *str) + { + LogDebug("In StaticMemberFunction Num=" + << num << ", str = " << str); + } +}; + +class COtherClass +{ + double rubbish; // to ensure this class has non-zero size. + +public: + virtual ~COtherClass() + { + } + + virtual void UnusedVirtualFunction(void) + { + } + virtual void TrickyVirtualFunction(int num, const char *str) = 0; +}; + +class VeryBigClass +{ + int letsMakeThingsComplicated[400]; +}; + +// This declaration ensures that we get a convoluted class heirarchy. +class CDerivedClass + : public VeryBigClass, + virtual public COtherClass, + virtual public CBaseClass +{ + double m_somemember[8]; + +public: + CDerivedClass() + : CBaseClass("Base of Derived") + { + m_somemember[0] = 1.2345; + } + + void SimpleDerivedFunction(int num, const char *str) + { + LogDebug("In SimpleDerivedFunction Num=" + << num << ", str = " << str); + } + + virtual void AnotherUnusedVirtualFunction(int num, const char *str) + { + LogDebug("In AnotherUnusedVirtualFunction in " << m_name << ". Num=" + << num << ", str = " << str); + } + + virtual void TrickyVirtualFunction(int num, const char *str) + { + LogDebug("In TrickyVirtualFunction in " << m_name << ". Num=" + << num << ", str = " << str); + } +}; + +RUNNER_TEST(FastDelegate_Test) +{ + // Delegates with up to 8 parameters are supported. + // Here's the case for a void function. + // We declare a delegate and attach it to SimpleVoidFunction() + DPL::FastDelegate0<> noparameterdelegate(&SimpleVoidFunction); + + // invoke the delegate - this calls SimpleVoidFunction() + noparameterdelegate(); + + LogDebug("-- Examples using two-parameter delegates (int, char *) --"); + + // By default, the return value is void. + typedef DPL::FastDelegate2 MyDelegate; + + // If you want to have a non-void return value, put it at the end. + typedef DPL::FastDelegate2 IntMyDelegate; + + + MyDelegate funclist[12]; // delegates are initialized to empty + CBaseClass a("Base A"); + CBaseClass b("Base B"); + CDerivedClass d; + CDerivedClass c; + + IntMyDelegate newdeleg; + newdeleg = DPL::MakeDelegate(&a, + &CBaseClass::SimpleMemberFunctionReturnsInt); + + // Binding a simple member function + funclist[0].bind(&a, &CBaseClass::SimpleMemberFunction); + + // You can also bind static (free) functions + funclist[1].bind(&SimpleStaticFunction); + + // and static member functions + funclist[2].bind(&CBaseClass::StaticMemberFunction); + + // and const member functions (these only need a const class pointer). + funclist[3].bind((const CBaseClass *) &a, + &CBaseClass::ConstMemberFunction); + + funclist[4].bind(&a, &CBaseClass::ConstMemberFunction); + + // and virtual member functions + funclist[5].bind(&b, &CBaseClass::SimpleVirtualFunction); + + // You can also use the = operator. For static functions, a fastdelegate + // looks identical to a simple function pointer. + funclist[6] = &CBaseClass::StaticMemberFunction; + + // The weird rule about the class of derived member function pointers + // is avoided. For MSVC, you can use &CDerivedClass::SimpleVirtualFunction + // here, but DMC will complain. Note that as well as .bind(), you can also + // use the MakeDelegate() global function. + funclist[7] = DPL::MakeDelegate(&d, &CBaseClass::SimpleVirtualFunction); + + // The worst case is an abstract virtual function of a virtually-derived + // class with at least one non-virtual base class. This is a VERY obscure + // situation, which you're unlikely to encounter in the real world. + // FastDelegate versions prior to 1.3 had problems with this case on VC6. + // Now, it works without problems on all compilers. + funclist[8].bind(&c, &CDerivedClass::TrickyVirtualFunction); + + // BUT... in such cases you should be using the base class as an + // interface, anyway. + funclist[9].bind(&c, &COtherClass::TrickyVirtualFunction); + + // Calling a function that was first declared in the derived class is + // straightforward + funclist[10] = DPL::MakeDelegate(&c, &CDerivedClass::SimpleDerivedFunction); + + // You can also bind directly using the constructor + MyDelegate dg(&b, &CBaseClass::SimpleVirtualFunction); + + const char *msg = "Looking for equal delegate"; + + for (int i = 0; i < 12; i++) + { + LogDebug(i << ":"); + + // The == and != operators are provided + // Note that they work even for inline functions. + if (funclist[i] == dg) + { + msg = "Found equal delegate"; + } + + // operator ! can be used to test for an empty delegate + // You can also use the .empty() member function. + if (!funclist[i]) + { + LogDebug("Delegate is empty"); + } + else + { + // Invocation generates optimal assembly code. + funclist[i](i, msg); + } + } +} diff --git a/tests/core/test_foreach.cpp b/tests/core/test_foreach.cpp new file mode 100644 index 0000000..f698081 --- /dev/null +++ b/tests/core/test_foreach.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_foreach.cpp + * @author Bartosz Janiak (b.janiak@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of foreach tests. + */ + +#include +#include +#include +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +static const size_t testContainerSize = 1024; + +template +void VerifyForeach(Container& container) +{ + size_t i = 0; + FOREACH(it, container) + { + RUNNER_ASSERT(*it == i); + i++; + } + RUNNER_ASSERT(i == container.size()); +} + +#define VERIFY_FOREACH(container) \ + { \ + size_t i = 0; \ + FOREACH(it, container) \ + { \ + RUNNER_ASSERT(*it == i); \ + i++; \ + } \ + } + +static size_t numberOfCallsToTemporaryList = 0; +std::list temporaryList(); +std::list temporaryList() +{ + ++numberOfCallsToTemporaryList; + std::list list; + for (size_t i = 0 ; i < testContainerSize ; i++) + { + list.push_back(i); + } + return list; +} + +static size_t numberOfCallsToTemporaryVector = 0; +std::vector temporaryVector(); +std::vector temporaryVector() +{ + ++numberOfCallsToTemporaryVector; + std::vector vector; + for (size_t i = 0 ; i < testContainerSize ; i++) + { + vector.push_back(i); + } + return vector; +} + +static size_t numberOfCallsToTemporarySet = 0; +std::set temporarySet(); +std::set temporarySet() +{ + ++numberOfCallsToTemporarySet; + std::set set; + for (size_t i = 0 ; i < testContainerSize ; i++) + { + set.insert(i); + } + return set; +} + +RUNNER_TEST(Foreach_std_containers) +{ + std::vector vector; + std::list list; + std::set set; + + for (size_t i = 0 ; i < testContainerSize ; i++) + { + vector.push_back(i); + list.push_back(i); + set.insert(i); + } + + VerifyForeach(vector); + VerifyForeach(list); + VerifyForeach(set); + + VERIFY_FOREACH(temporaryList()); + VERIFY_FOREACH(temporaryVector()); + VERIFY_FOREACH(temporarySet()); + + RUNNER_ASSERT(numberOfCallsToTemporaryList == 1); + RUNNER_ASSERT(numberOfCallsToTemporaryVector == 1); + RUNNER_ASSERT(numberOfCallsToTemporarySet == 1); +} diff --git a/tests/core/test_log_unhandled_exception.cpp b/tests/core/test_log_unhandled_exception.cpp new file mode 100644 index 0000000..0403c28 --- /dev/null +++ b/tests/core/test_log_unhandled_exception.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_log_unhandled_exception.cpp + * @author Pawel Sikorski (p.marcinkiew@samsung.com) + * @version 1.0 + * @brief + */ +#include +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +class MyException +{ +}; + +class MyDPLException +{ +public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, MyException) +}; + +class MySTDException + : public std::exception +{ +public: + virtual const char* what()const throw() { return "my std exception occurred";} +}; + +RUNNER_TEST(Log_Unknown_Exception) +{ + UNHANDLED_EXCEPTION_HANDLER_BEGIN + { +// throw MyException(); + } + UNHANDLED_EXCEPTION_HANDLER_END + RUNNER_ASSERT(true); +} + +RUNNER_TEST(Log_DPL_Exception) +{ + UNHANDLED_EXCEPTION_HANDLER_BEGIN + { +// Throw(MyDPLException::MyException); + } + UNHANDLED_EXCEPTION_HANDLER_END + RUNNER_ASSERT(true); +} + +RUNNER_TEST(Log_STD_Exception) +{ + UNHANDLED_EXCEPTION_HANDLER_BEGIN + { +// throw MySTDException(); + } + UNHANDLED_EXCEPTION_HANDLER_END + RUNNER_ASSERT(true); +} diff --git a/tests/core/test_once.cpp b/tests/core/test_once.cpp new file mode 100644 index 0000000..e65ea9b --- /dev/null +++ b/tests/core/test_once.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_once.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of once tests + */ +#include +#include +#include +#include +#include +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +namespace // anonymous +{ +gint g_counter; + +void Delegate() +{ + ++g_counter; +} +} // namespace anonymous + +RUNNER_TEST(Once_DoubleCall) +{ + g_counter = 0; + + DPL::Once once; + + once.Call(&Delegate); + once.Call(&Delegate); + + RUNNER_ASSERT_MSG(g_counter == 1, "Counter value is: " << g_counter); +} + +class MyThread + : public DPL::Thread +{ +protected: + virtual int ThreadEntry() + { + DPL::WaitForSingleHandle(m_event->GetHandle()); + m_once->Call(DPL::Once::Delegate(this, &MyThread::Call)); + return 0; + } + + void Call() + { + ++*m_atom; + } + +public: + MyThread(DPL::WaitableEvent *event, DPL::Once *once, DPL::Atomic *atom) + : m_event(event), m_once(once), m_atom(atom) + { + } + +private: + DPL::WaitableEvent *m_event; + DPL::Once *m_once; + DPL::Atomic *m_atom; +}; + +RUNNER_TEST(Once_MultiThreadCall) +{ + const size_t NUM_THREADS = 20; + typedef DPL::SharedPtr ThreadPtr; + + ThreadPtr threads[NUM_THREADS]; + DPL::WaitableEvent event; + DPL::Once once; + DPL::Atomic atom; + + for (size_t i = 0; i< NUM_THREADS; ++i) + { + (threads[i] = ThreadPtr(new MyThread(&event, &once, &atom)))->Run(); + } + + event.Signal(); + + for (size_t i = 0; i< NUM_THREADS; ++i) + threads[i]->Quit(); + + RUNNER_ASSERT_MSG(atom == 1, "Atom value is: " << atom); +} diff --git a/tests/core/test_scoped_array.cpp b/tests/core/test_scoped_array.cpp new file mode 100644 index 0000000..58b0603 --- /dev/null +++ b/tests/core/test_scoped_array.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_scoped_array.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of test scoped array + */ +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +RUNNER_TEST(ScopedArray_Zero) +{ + DPL::ScopedArray array; + + RUNNER_ASSERT(!array); + RUNNER_ASSERT(!!!array); +} + +RUNNER_TEST(ScopedArray_NonZero) +{ + DPL::ScopedArray array(new char[7]); + + RUNNER_ASSERT(array); + RUNNER_ASSERT(!!array); +} + +RUNNER_TEST(ScopedArray_Reset) +{ + DPL::ScopedArray array(new char[7]); + array.Reset(); + + RUNNER_ASSERT(!array); + + array.Reset(new char); + RUNNER_ASSERT(array); +} + +RUNNER_TEST(ScopedArray_ArrayOperator) +{ + DPL::ScopedArray array(new char[7]); + + array[1] = array[2] = 3; + + RUNNER_ASSERT(array[1] == 3); + RUNNER_ASSERT(array[2] == 3); +} diff --git a/tests/core/test_scoped_close.cpp b/tests/core/test_scoped_close.cpp new file mode 100644 index 0000000..3549fed --- /dev/null +++ b/tests/core/test_scoped_close.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_scoped_close.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of test scoped close + */ +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +// DUNNO diff --git a/tests/core/test_scoped_fclose.cpp b/tests/core/test_scoped_fclose.cpp new file mode 100644 index 0000000..dbdff95 --- /dev/null +++ b/tests/core/test_scoped_fclose.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*! + * @file test_scoped_fclose.cpp + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of test scoped fclose + */ + +#include +#include + +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +namespace +{ +FILE* MakeTmp() +{ + FILE* result = NULL; + do + { + result = tmpfile(); + } while (NULL != result && EINTR == errno); + return result; +} +}//anonymous namespace + +RUNNER_TEST(ScopedFClose_Zero) +{ + DPL::ScopedFClose file; + + RUNNER_ASSERT(!file); + RUNNER_ASSERT(!!!file); +} + +RUNNER_TEST(ScopedFClose_NonZero) +{ + DPL::ScopedFClose file(MakeTmp()); + + RUNNER_ASSERT(file); + RUNNER_ASSERT(!!file); +} + +RUNNER_TEST(ScopedFClose_Reset) +{ + DPL::ScopedFClose file(MakeTmp()); + file.Reset(); + + RUNNER_ASSERT(!file); + + file.Reset(MakeTmp()); + RUNNER_ASSERT(file); +} + diff --git a/tests/core/test_scoped_free.cpp b/tests/core/test_scoped_free.cpp new file mode 100644 index 0000000..bc41a5a --- /dev/null +++ b/tests/core/test_scoped_free.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_scoped_free.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of test scoped free + */ +#include +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +RUNNER_TEST(ScopedFree_Zero) +{ + DPL::ScopedFree free; + + RUNNER_ASSERT(!free); + RUNNER_ASSERT(!!!free); +} + +RUNNER_TEST(ScopedFree_NonZero) +{ + DPL::ScopedFree free(malloc(7)); + + RUNNER_ASSERT(free); + RUNNER_ASSERT(!!free); +} + +RUNNER_TEST(ScopedFree_Reset) +{ + DPL::ScopedFree free(malloc(7)); + free.Reset(); + + RUNNER_ASSERT(!free); + + free.Reset(malloc(8)); + RUNNER_ASSERT(free); +} diff --git a/tests/core/test_scoped_ptr.cpp b/tests/core/test_scoped_ptr.cpp new file mode 100644 index 0000000..f3a7237 --- /dev/null +++ b/tests/core/test_scoped_ptr.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_scoped_ptr.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of test scoped ptr + */ +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +RUNNER_TEST(ScopedPtr_Zero) +{ + DPL::ScopedPtr ptr; + + RUNNER_ASSERT(!ptr); + RUNNER_ASSERT(!!!ptr); +} + +RUNNER_TEST(ScopedPtr_NonZero) +{ + DPL::ScopedPtr ptr(new char(7)); + + RUNNER_ASSERT(ptr); + RUNNER_ASSERT(!!ptr); +} + +RUNNER_TEST(ScopedPtr_Reset) +{ + DPL::ScopedPtr ptr(new char(7)); + ptr.Reset(); + + RUNNER_ASSERT(!ptr); + + ptr.Reset(new char); + RUNNER_ASSERT(ptr); +} + +RUNNER_TEST(ScopedPtr_Operators) +{ + DPL::ScopedPtr ptr(new char(7)); + + RUNNER_ASSERT(*ptr == *ptr.Get()); +} diff --git a/tests/core/test_semaphore.cpp b/tests/core/test_semaphore.cpp new file mode 100644 index 0000000..854978e --- /dev/null +++ b/tests/core/test_semaphore.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_semaphore.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of semaphore tests + */ +#include +#include +#include +#include +#include +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +class SemaphoreThread + : public DPL::Thread +{ + int m_delta; + int m_times; + int *m_value; + std::string m_semaphoreName; + +public: + SemaphoreThread(int delta, + int times, + int *value, + const std::string &semaphoreName) + : m_delta(delta), + m_times(times), + m_value(value), + m_semaphoreName(semaphoreName) + { + } + +protected: + virtual int ThreadEntry() + { + DPL::Semaphore semaphore(m_semaphoreName); + + for (int i = 0; i < m_times; ++i) + { + // Take scoped semaphore lock + DPL::Semaphore::ScopedLock lock(&semaphore); + *m_value += m_delta; + } + + return 0; + } +}; + +RUNNER_TEST(Semaphore_NamedIncrementDecrement) +{ + std::string semaphoreName = + "dpl_test_semaphore_" + + DPL::lexical_cast(std::time(NULL)); + + int value = 0; + SemaphoreThread threadA(-1, 10000, &value, semaphoreName); + SemaphoreThread threadB(+1, 10000, &value, semaphoreName); + + threadA.Run(); + threadB.Run(); + + threadA.Quit(); + threadB.Quit(); + + RUNNER_ASSERT_MSG(value == 0, "Final value is: " << value); +} diff --git a/tests/core/test_serialization.cpp b/tests/core/test_serialization.cpp new file mode 100644 index 0000000..8776fc8 --- /dev/null +++ b/tests/core/test_serialization.cpp @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_address.cpp + * @author Tomasz Swierczek (t.swierczek@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of serialization tests + */ + +#include +#include +#include +#include + +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +// test stream class +class BinaryStream : public DPL::IStream { + public: + virtual void Read(size_t num, void * bytes) + { + for (unsigned i = 0; i < num; ++i) { + ((unsigned char*)bytes)[i] = data[i + readPosition]; + } + readPosition += num; + } + virtual void Write(size_t num, const void * bytes) + { + for (unsigned i = 0; i < num; ++i) { + data.push_back(((unsigned char*)bytes)[i]); + } + } + BinaryStream() + { + readPosition = 0; + } + virtual ~BinaryStream(){}; + + private: + std::vector data; + unsigned readPosition; +}; + +//test ISerializable class +class TestClass : public DPL::ISerializable { + public: + TestClass(int val, std::string str1, std::string str2) + { + a = val; + b = str1; + c.push_back(str1); + c.push_back(str2); + c.push_back(str1 + str2); + }; + TestClass(DPL::IStream& stream) + { + DPL::Deserialization::Deserialize(stream,a); + DPL::Deserialization::Deserialize(stream,b); + DPL::Deserialization::Deserialize(stream,c); + }; + virtual void Serialize(DPL::IStream& stream) const + { + DPL::Serialization::Serialize(stream,a); + DPL::Serialization::Serialize(stream,b); + DPL::Serialization::Serialize(stream,c); + } + virtual ~TestClass(){} + virtual bool operator==(const TestClass& other) + { + return (a == other.a && + b == other.b && + c.size() == other.c.size() && + c[0] == other.c[0] && + c[1] == other.c[1] && + c[2] == other.c[2]); + } + private: + int a; + std::string b; + std::vector c; +}; + +RUNNER_TEST(Serialize_primitives) +{ + int a = 1; + bool b = true; + unsigned c = 23; + BinaryStream stream; + DPL::Serialization::Serialize(stream,a); + DPL::Serialization::Serialize(stream,b); + DPL::Serialization::Serialize(stream,c); + int test_int; + DPL::Deserialization::Deserialize(stream,test_int); + RUNNER_ASSERT(test_int == a); + bool test_bool; + DPL::Deserialization::Deserialize(stream,test_bool); + RUNNER_ASSERT(test_bool == b); + unsigned test_unsigned; + DPL::Deserialization::Deserialize(stream,test_unsigned); + RUNNER_ASSERT(test_unsigned == c); +} + +RUNNER_TEST(Serialize_primitive_pointers) +{ + int a = 1; + bool b = true; + unsigned c = 23; + BinaryStream stream; + DPL::Serialization::Serialize(stream,&a); + DPL::Serialization::Serialize(stream,&b); + DPL::Serialization::Serialize(stream,&c); + int* test_int; + DPL::Deserialization::Deserialize(stream,test_int); + RUNNER_ASSERT(test_int != NULL && *test_int == a); + bool* test_bool; + DPL::Deserialization::Deserialize(stream,test_bool); + RUNNER_ASSERT(test_bool != NULL && *test_bool == b); + unsigned* test_unsigned; + DPL::Deserialization::Deserialize(stream,test_unsigned); + RUNNER_ASSERT(test_unsigned != NULL && *test_unsigned == c); + delete test_int; + delete test_bool; + delete test_unsigned; +} + +RUNNER_TEST(Serialize_strings) +{ + std::string str1 = "ALA MA KOTA"; + std::string str2 = "MULTILINE\nTEST"; + BinaryStream stream; + DPL::Serialization::Serialize(stream,str1); + DPL::Serialization::Serialize(stream,str2); + std::string test_str1; + DPL::Deserialization::Deserialize(stream,test_str1); + RUNNER_ASSERT(test_str1 == str1); + std::string test_str2; + DPL::Deserialization::Deserialize(stream,test_str2); + RUNNER_ASSERT(test_str2 == str2); +} + +RUNNER_TEST(Serialize_string_pointers) +{ + std::string str1 = "ALA MA KOTA"; + std::string str2 = "MULTILINE\nTEST"; + BinaryStream stream; + DPL::Serialization::Serialize(stream,&str1); + DPL::Serialization::Serialize(stream,&str2); + std::string* test_str1; + DPL::Deserialization::Deserialize(stream,test_str1); + RUNNER_ASSERT(test_str1 != NULL && *test_str1 == str1); + std::string* test_str2; + DPL::Deserialization::Deserialize(stream,test_str2); + RUNNER_ASSERT(test_str2 != NULL && *test_str2 == str2); + delete test_str1; + delete test_str2; +} + +RUNNER_TEST(Serialize_containers) +{ + std::vector vec; + vec.push_back(134); + vec.push_back(265); + std::list list; + list.push_back(true); + list.push_back(false); + std::pair pair; + pair.first = -23; + pair.second = 1234; + std::map map; + map.insert(std::pair(45, "ALA MA CZARNEGO KOTA")); + map.insert(std::pair(-78, "...A MOZE\nMA\nWIELE LINIJEK")); + BinaryStream stream; + DPL::Serialization::Serialize(stream,vec); + DPL::Serialization::Serialize(stream,list); + DPL::Serialization::Serialize(stream,pair); + DPL::Serialization::Serialize(stream,map); + std::vector test_vec; + DPL::Deserialization::Deserialize(stream,test_vec); + RUNNER_ASSERT(test_vec.size() == vec.size() && + test_vec[0] == vec[0] && test_vec[1] == vec[1]); + std::list test_list; + DPL::Deserialization::Deserialize(stream,test_list); + RUNNER_ASSERT(test_list.size() == list.size() && + test_list.front() == list.front() && + test_list.back() == test_list.back()); + std::pair test_pair; + DPL::Deserialization::Deserialize(stream,test_pair); + RUNNER_ASSERT(test_pair.first == pair.first && + test_pair.second == pair.second); + std::map test_map; + DPL::Deserialization::Deserialize(stream,test_map); + RUNNER_ASSERT(test_map.size() == map.size() && + test_map.at(45) == map.at(45) && + test_map.at(-78) == map.at(-78)); +} + +RUNNER_TEST(Serialize_objects) +{ + TestClass a(123,"ASDGHUADB\n\n5679b^^()*","TEST_STRING"), + b(679,"HUSPIDNSAHDPA","\nASDSADASD\naDSADASD8"); + BinaryStream stream; + DPL::Serialization::Serialize(stream,a); + DPL::Serialization::Serialize(stream,b); + TestClass test_a(0,"",""), test_b(0,"",""); + DPL::Deserialization::Deserialize(stream, test_a); + RUNNER_ASSERT(test_a == a); + DPL::Deserialization::Deserialize(stream, test_b); + RUNNER_ASSERT(test_b == b); +} + +RUNNER_TEST(Serialize_all) +{ + std::map > map; + std::vector vec; + vec.push_back(new TestClass(123,"ASDGHUADB\n\n5679b^^()*","TEST_STRING")); + vec.push_back(new TestClass(679,"HUSPIDNSAHDPA","\nASDSADASD\naDSADASD8")); + map.insert(std::pair >("KEY1",vec)); + map.insert(std::pair >("KEY2",vec)); + BinaryStream stream; + + DPL::Serialization::Serialize(stream, map); + + std::map > test_map; + DPL::Deserialization::Deserialize(stream,test_map); + RUNNER_ASSERT(map.size() == test_map.size()); + std::vector test_vec1,test_vec2; + test_vec1 = map.at("KEY1"); + test_vec2 = test_map.at("KEY1"); + RUNNER_ASSERT(test_vec1.size() == test_vec2.size()); + unsigned i; + for (i = 0; i < test_vec1.size(); ++i) + { + RUNNER_ASSERT((*test_vec1[i]) == (*test_vec2[i])); + } + test_vec1 = map.at("KEY2"); + test_vec2 = test_map.at("KEY2"); + RUNNER_ASSERT(test_vec1.size() == test_vec2.size()); + for (i = 0; i < test_vec1.size(); ++i) + { + RUNNER_ASSERT((*test_vec1[i]) == (*test_vec2[i])); + } +} + diff --git a/tests/core/test_shared_ptr.cpp b/tests/core/test_shared_ptr.cpp new file mode 100644 index 0000000..541a333 --- /dev/null +++ b/tests/core/test_shared_ptr.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_shared_ptr.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of test shared ptr + */ +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +RUNNER_TEST(SharedPtr_Zero) +{ + DPL::SharedPtr ptr; + + RUNNER_ASSERT(!ptr); + RUNNER_ASSERT(!!!ptr); + RUNNER_ASSERT(ptr == DPL::SharedPtr()); +} + +RUNNER_TEST(SharedPtr_NonZero) +{ + DPL::SharedPtr ptr(new char(7)); + + RUNNER_ASSERT(ptr); + RUNNER_ASSERT(!!ptr); + RUNNER_ASSERT(ptr != DPL::SharedPtr()); +} + +RUNNER_TEST(SharedPtr_Copy) +{ + DPL::SharedPtr ptr1(new char(7)); + DPL::SharedPtr ptr2(new char(7)); + + RUNNER_ASSERT(ptr1 != ptr2); + + ptr2 = ptr1; + + RUNNER_ASSERT(ptr1 == ptr2); +} + +RUNNER_TEST(SharedPtr_Reset) +{ + DPL::SharedPtr ptr(new char(7)); + ptr.Reset(); + + RUNNER_ASSERT(!ptr); + + ptr.Reset(new char); + RUNNER_ASSERT(ptr); +} + +RUNNER_TEST(SharedPtr_RefCounting) +{ + DPL::SharedPtr ptr1(new char(7)); + DPL::SharedPtr ptr2; + + ptr2 = ptr1; + + RUNNER_ASSERT(ptr1 == ptr2); + RUNNER_ASSERT(ptr1.GetUseCount() == ptr2.GetUseCount()); + RUNNER_ASSERT(ptr1.GetUseCount() == 2); +} + +RUNNER_TEST(SharedPtr_Operators) +{ + DPL::SharedPtr ptr(new char(7)); + + RUNNER_ASSERT(*ptr == *ptr.Get()); +} diff --git a/tests/core/test_string.cpp b/tests/core/test_string.cpp new file mode 100644 index 0000000..dea0a22 --- /dev/null +++ b/tests/core/test_string.cpp @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_string.cpp + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of string tests + */ +#include +#include +#include +#include +#include +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +unsigned char GetBaseCode(int index); +unsigned char GetBaseCode(int index) +{ + /* aaaack but it's fast and const should make it shared text page. */ + static const unsigned char pr2six[256] = + { + /* ASCII table */ + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + }; + return pr2six[index]; +} + + +/* Function adapted from APR library (http://apr.apache.org/) */ +int wbxml_base64_decode(const char *buffer, char **result); +int wbxml_base64_decode(const char *buffer, char **result) +{ + int nbytesdecoded = 0, nprbytes = 0; + const char *bufin = NULL; + char *bufout = NULL; + + if ((buffer == NULL) || (result == NULL)) + return 0; + + /* Initialize output buffer */ + *result = NULL; + + bufin = buffer; + while (GetBaseCode(*(bufin++)) <= 63) {} + + nprbytes = (bufin - buffer) - 1; + nbytesdecoded = ((nprbytes + 3) / 4) * 3; + + /* Malloc result buffer */ + if ((*result = (char*) malloc(nbytesdecoded + 1)) == NULL) + return 0; + memset(*result, nbytesdecoded + 1, 0); + + bufout = *result; + bufin = buffer; + + while (nprbytes > 4) + { + *(bufout++) = (char)(GetBaseCode(*bufin) << 2 | GetBaseCode(bufin[1]) >> 4); + *(bufout++) = (char)(GetBaseCode(bufin[1]) << 4 | GetBaseCode(bufin[2]) >> 2); + *(bufout++) = (char)(GetBaseCode(bufin[2]) << 6 | GetBaseCode(bufin[3])); + bufin += 4; + nprbytes -= 4; + } + + /* Note: (nprbytes == 1) would be an error, so just ingore that case */ + if (nprbytes > 1) + { + *(bufout++) = (char)(GetBaseCode(*bufin) << 2 | GetBaseCode(bufin[1]) >> 4); + } + if (nprbytes > 2) + { + *(bufout++) = (char)(GetBaseCode(bufin[1]) << 4 | GetBaseCode(bufin[2]) >> 2); + } + if (nprbytes > 3) + { + *(bufout++) = (char)(GetBaseCode(bufin[2]) << 6 | GetBaseCode(bufin[3])); + } + + nbytesdecoded -= (4 - nprbytes) & 3; + + return nbytesdecoded; +} + +//#define TEST_CONVERSION(in_string, out_string, buffer_type, function + +const char utf32Encoded[] = +"RDAAAI0wAABvMAAAazAAAHswAAB4MAAAaDAAAAAwAABhMAAAijAAAGwwAACLMAAAkjAAAAAwAACP\ +MAAASzAAAIgwAABfMAAAjDAAAF0wAAAAMAAAZDAAAG0wAABqMAAAiTAAAIAwAAAAMAAARjAAAJAw\ +AABuMAAASjAAAE8wAACEMAAAfjAAAAAwAABRMAAAdTAAAFMwAABIMAAAZjAAAAAwAABCMAAAVTAA\ +AE0wAACGMAAAgTAAAH8wAABXMAAAADAAAJEwAAByMAAAgjAAAFswAABZMAAACgAAANsFAADaBQAA\ +IAAAANQFAADqBQAA6AUAAOEFAADnBQAAIAAAAOAFAADkBQAA5QUAACAAAADiBQAA3AUAACAAAADS\ +BQAA1QUAANYFAADcBQAAIAAAAOcFAADYBQAA3wUAACwAAAAgAAAA6QUAANMFAADXBQAA4wUAACAA\ +AADQBQAA6gUAACAAAADmBQAA0QUAANkFAAAgAAAA3AUAAN4FAADZBQAA3QUAAAoAAACk0AAApMIA\ +AFjHAAAgAAAA4KwAACDHAABwyAAAdKwAAEDHAAAgAAAAhccAACDCAAB8sAAArLkAACAAAADMuQAA\ +mLAAAHzFAAAgAAAAWNUAAOCsAAAgAAAAudIAAMS8AABc1QAAIAAAADCuAAAgwgAAQMcAACAAAABE\ +1QAAlMYAAFjOAAAgAAAASsUAAOSyAAAKAAAAUAAAAGMAAABoAAAAbgAAAAUBAAAHAQAAIAAAAHcA\ +AAAgAAAAdAAAABkBAAAgAAAAQgEAAPMAAABkAAAAegEAACAAAABqAAAAZQAAAHwBAABhAAAAIAAA\ +AGwAAAB1AAAAYgAAACAAAABvAAAAWwEAAG0AAAAgAAAAcwAAAGsAAAByAAAAegAAAHkAAABEAQAA\ +IAAAAGYAAABpAAAAZwAAAC4AAAAKAAAAQgAAAGwAAABvAAAAdwAAAHoAAAB5AAAAIAAAAG4AAABp\ +AAAAZwAAAGgAAAB0AAAALQAAAGYAAAByAAAAdQAAAG0AAABwAAAAcwAAACAAAAB2AAAAZQAAAHgA\ +AAAnAAAAZAAAACAAAABKAAAAYQAAAGMAAABrAAAAIAAAAFEAAAAuAAAACgAAAEYGAAA1BgAAIAAA\ +AC0GAABDBgAASgYAAEUGAAAgAAAARAYAAEcGAAAgAAAAMwYAADEGAAAgAAAAQgYAACcGAAA3BgAA\ +OQYAACAAAABIBgAAMAYAAEgGAAAgAAAANAYAACMGAABGBgAAIAAAADkGAAA4BgAASgYAAEUGAAAg\ +AAAARQYAAEMGAAAqBgAASAYAACgGAAAgAAAAOQYAAEQGAABJBgAAIAAAACsGAABIBgAAKAYAACAA\ +AAAjBgAALgYAADYGAAAxBgAAIAAAAEgGAABFBgAAOgYAAEQGAABBBgAAIAAAACgGAAAsBgAARAYA\ +AC8GAAAgAAAAIwYAADIGAAAxBgAAQgYAACAAAAAKAAAAEgQAACAAAABHBAAAMAQAAEkEAAAwBAAA\ +RQQAACAAAABOBAAAMwQAADAEAAAgAAAANgQAADgEAAA7BAAAIAAAADEEAABLBAAAIAAAAEYEAAA4\ +BAAAQgQAAEAEAABDBAAAQQQAAD8AAAAgAAAAFAQAADAEAAAsAAAAIAAAAD0EAAA+BAAAIAAAAEQE\ +AAAwBAAAOwQAAEwEAABIBAAAOAQAADIEAABLBAAAOQQAACAAAABNBAAAOgQAADcEAAA1BAAAPAQA\ +AD8EAAA7BAAATwQAAEAEAAAhAAAACgAAAKQDAACsAwAAxwMAALkDAADDAwAAxAMAALcDAAAgAAAA\ +sQMAALsDAADOAwAAwAMAALcDAAC+AwAAIAAAALIDAACxAwAAxgMAAK4DAADCAwAAIAAAAMgDAAC3\ +AwAAvAMAAK0DAAC9AwAAtwMAACAAAACzAwAAtwMAACwAAAAgAAAAtAMAAMEDAACxAwAAwwMAALoD\ +AAC1AwAAuwMAAK8DAAC2AwAAtQMAALkDAAAgAAAAxQMAAMADAACtAwAAwQMAACAAAAC9AwAAyQMA\ +ALgDAADBAwAAvwMAAM0DAAAgAAAAugMAAMUDAAC9AwAAzAMAAMIDAAAKAAAAVgAAAGkAAABjAAAA\ +dAAAAG8AAAByAAAAIAAAAGoAAABhAAAAZwAAAHQAAAAgAAAAegAAAHcAAAD2AAAAbAAAAGYAAAAg\ +AAAAQgAAAG8AAAB4AAAAawAAAOQAAABtAAAAcAAAAGYAAABlAAAAcgAAACAAAABxAAAAdQAAAGUA\ +AAByAAAAIAAAAPwAAABiAAAAZQAAAHIAAAAgAAAAZAAAAGUAAABuAAAAIAAAAGcAAAByAAAAbwAA\ +AN8AAABlAAAAbgAAACAAAABTAAAAeQAAAGwAAAB0AAAAZQAAAHIAAAAgAAAARAAAAGUAAABpAAAA\ +YwAAAGgAAAAKAAAAlokAAM6RAAAhcQAAUJYAAONeAAAM/wAAl3oAABZZAAAJZwAAzYUAAClZAAAK\ +AAAACgAAAAAAAAA="; + +const char utf8Encoded[] = +"44GE44KN44Gv44Gr44G744G444Go44CA44Gh44KK44Gs44KL44KS44CA44KP44GL44KI44Gf44KM\ +44Gd44CA44Gk44Gt44Gq44KJ44KA44CA44GG44KQ44Gu44GK44GP44KE44G+44CA44GR44G144GT\ +44GI44Gm44CA44GC44GV44GN44KG44KB44G/44GX44CA44KR44Gy44KC44Gb44GZCteb15og15TX\ +qteo16HXpyDXoNek16Ug16LXnCDXkteV15bXnCDXp9eY158sINep15PXl9ejINeQ16og16bXkdeZ\ +INec157XmdedCu2CpOyKpOydmCDqs6DsnKDsobDqsbTsnYAg7J6F7Iig64G866asIOunjOuCmOyV\ +vCDtlZjqs6Ag7Yq567OE7ZWcIOq4sOyIoOydgCDtlYTsmpTsuZgg7JWK64ukClBjaG7EhcSHIHcg\ +dMSZIMWCw7NkxbogamXFvGEgbHViIG/Fm20gc2tyennFhCBmaWcuCkJsb3d6eSBuaWdodC1mcnVt\ +cHMgdmV4J2QgSmFjayBRLgrZhti1INit2YPZitmFINmE2Ycg2LPYsSDZgtin2LfYuSDZiNiw2Ygg\ +2LTYo9mGINi52LjZitmFINmF2YPYqtmI2Kgg2LnZhNmJINir2YjYqCDYo9iu2LbYsSDZiNmF2LrZ\ +hNmBINio2KzZhNivINij2LLYsdmCIArQkiDRh9Cw0YnQsNGFINGO0LPQsCDQttC40Lsg0LHRiyDR\ +htC40YLRgNGD0YE/INCU0LAsINC90L4g0YTQsNC70YzRiNC40LLRi9C5INGN0LrQt9C10LzQv9C7\ +0Y/RgCEKzqTOrM+HzrnPg8+EzrcgzrHOu8+Oz4DOt86+IM6yzrHPhs6uz4Igz4jOt868zq3Ovc63\ +IM6zzrcsIM60z4HOsc+DzrrOtc67zq/Ots61zrkgz4XPgM6tz4Egzr3Pic64z4HOv8+NIM66z4XO\ +vc+Mz4IKVmljdG9yIGphZ3QgenfDtmxmIEJveGvDpG1wZmVyIHF1ZXIgw7xiZXIgZGVuIGdyb8Of\ +ZW4gU3lsdGVyIERlaWNoCuimlumHjueEoemZkOW7o++8jOeql+WkluacieiXjeWkqQoKAA=="; + + + + +const char asciiEncodedIso1[] = +"ISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZ\ +WltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fgA="; + +const char asciiEncodedUtf32[] = +"IQAAACIAAAAjAAAAJAAAACUAAAAmAAAAJwAAACgAAAApAAAAKgAAACsAAAAsAAAALQAAAC4AAAAv\ +AAAAMAAAADEAAAAyAAAAMwAAADQAAAA1AAAANgAAADcAAAA4AAAAOQAAADoAAAA7AAAAPAAAAD0A\ +AAA+AAAAPwAAAEAAAABBAAAAQgAAAEMAAABEAAAARQAAAEYAAABHAAAASAAAAEkAAABKAAAASwAA\ +AEwAAABNAAAATgAAAE8AAABQAAAAUQAAAFIAAABTAAAAVAAAAFUAAABWAAAAVwAAAFgAAABZAAAA\ +WgAAAFsAAABcAAAAXQAAAF4AAABfAAAAYAAAAGEAAABiAAAAYwAAAGQAAABlAAAAZgAAAGcAAABo\ +AAAAaQAAAGoAAABrAAAAbAAAAG0AAABuAAAAbwAAAHAAAABxAAAAcgAAAHMAAAB0AAAAdQAAAHYA\ +AAB3AAAAeAAAAHkAAAB6AAAAewAAAHwAAAB9AAAAfgAAAAAAAAA="; + + +RUNNER_TEST(String_ConverterFromASCII) +{ + char* inStr = NULL; + int inSize = wbxml_base64_decode(asciiEncodedIso1, &inStr); + RUNNER_ASSERT(inSize > 0); + RUNNER_ASSERT(NULL != inStr); + inStr[inSize] = '\0'; + { + DPL::String asciiString = DPL::FromASCIIString(inStr); + + std::string result = DPL::ToUTF8String(asciiString); + + RUNNER_ASSERT(strlen(inStr) == result.size()); + + RUNNER_ASSERT(0 == memcmp(inStr, result.c_str(), result.size())); + } + + free(inStr); +} + +RUNNER_TEST(String_ConverterFromUTF8) +{ + char* inStr = NULL; + int inSize = wbxml_base64_decode(asciiEncodedIso1, &inStr); + RUNNER_ASSERT(inSize > 0); + RUNNER_ASSERT(NULL != inStr); + { + DPL::String asciiString = DPL::FromUTF8String(inStr); + + std::string result = DPL::ToUTF8String(asciiString); + + RUNNER_ASSERT(strlen(inStr) == result.size()); + + RUNNER_ASSERT(0 == memcmp(inStr, result.c_str(), result.size())); + } + + free(inStr); +} + +RUNNER_TEST(String_ConverterFromUTF32) +{ + wchar_t* inStr = NULL; + int inSize = wbxml_base64_decode(utf32Encoded, reinterpret_cast(&inStr)); + RUNNER_ASSERT(inSize > 0); + RUNNER_ASSERT(NULL != inStr); + char* outStr = NULL; + int outSize = wbxml_base64_decode(utf8Encoded, &outStr); + RUNNER_ASSERT(outSize > 0); + RUNNER_ASSERT(NULL != outStr); + outStr[outSize] = '\0'; + { + DPL::String utfString = DPL::FromUTF32String(inStr); + std::string result = DPL::ToUTF8String(utfString); + + RUNNER_ASSERT(strlen(outStr) == result.size()); + RUNNER_ASSERT(0 == memcmp(outStr, result.c_str(), result.size())); + + + RUNNER_ASSERT(inSize / sizeof(wchar_t) - 1 == utfString.size()); + RUNNER_ASSERT(0 == memcmp(inStr, &(utfString[0]), utfString.size() * sizeof(wchar_t))); + + } + + free(inStr); +} + +template +void String_TokenizeReal(const DelimiterType& delimiter) +{ + DPL::String str(L".##..abc.#."); + std::vector tokens; + DPL::Tokenize(str, delimiter, std::back_inserter(tokens)); + + std::vector expectedTokens; + for ( int i = 0 ; i < 5 ; i++ ) + expectedTokens.push_back(L""); + expectedTokens.push_back(L"abc"); + for ( int i = 0 ; i < 3 ; i++ ) + expectedTokens.push_back(L""); + + RUNNER_ASSERT(expectedTokens == tokens); + tokens.clear(); + expectedTokens.clear(); + + DPL::Tokenize(str, delimiter, std::back_inserter(tokens), true); + expectedTokens.push_back(L"abc"); + RUNNER_ASSERT(expectedTokens == tokens); +} + +RUNNER_TEST(String_Tokenize) +{ + String_TokenizeReal(L"#."); + String_TokenizeReal(L".#"); + String_TokenizeReal(L".....####.###.."); + String_TokenizeReal(DPL::String(L".#")); + + std::vector tokens; + DPL::Tokenize(std::string("abc.def"), '.', std::back_inserter(tokens)); + std::vector expectedTokens; + expectedTokens.push_back("abc"); + expectedTokens.push_back("def"); + + RUNNER_ASSERT(tokens == expectedTokens); +} + +template +void TestInStreams( + std::basic_string argumentInString, + std::basic_string argumentResultString) +{ + typedef std::basic_string + String; + std::basic_istringstream + istream(argumentInString); + int intValue = 0; + double doubleValue = 0.0; + float floatValue = 0.0; + String stringValue; + + istream >> intValue; + RUNNER_ASSERT(!istream.fail()); + istream >> doubleValue; + RUNNER_ASSERT(!istream.fail()); + istream >> floatValue; + RUNNER_ASSERT(!istream.fail()); + istream >> stringValue; + RUNNER_ASSERT(!istream.fail()); + + RUNNER_ASSERT(1 == intValue); + RUNNER_ASSERT(fabs(1.1f - doubleValue) < 0.00001); + RUNNER_ASSERT(fabs(1.1f - floatValue) < 0.00001); + RUNNER_ASSERT(argumentResultString == stringValue); +} + +template +void TestOutStreams( + std::basic_string argumentInString, + std::basic_string argumentResultString) +{ + typedef std::basic_string + String; + + std::basic_ostringstream + ostream; + + int intValue = 1; + double doubleValue = 1.1; + float floatValue = 1.1f; + String stringValue = argumentInString; + + ostream << intValue; + RUNNER_ASSERT(!ostream.fail()); + ostream << doubleValue; + RUNNER_ASSERT(!ostream.fail()); + ostream << floatValue; + RUNNER_ASSERT(!ostream.fail()); + ostream << stringValue; + RUNNER_ASSERT(!ostream.fail()); + + RUNNER_ASSERT(ostream.str() == argumentResultString); +} + +RUNNER_TEST(String_Streams) +{ + TestInStreams >("1 1.1 1.1 test", "test"); + TestInStreams >(L"1 1.1 1.1 test", L"test"); + TestInStreams(L"1 1.1 1.1 test", L"test"); + TestOutStreams >("test", "11.11.1test"); + TestOutStreams >(L"test", L"11.11.1test"); + TestOutStreams(L"test", L"11.11.1test"); +} + +RUNNER_TEST(String_CompareCaseSensitive) +{ + RUNNER_ASSERT( + DPL::StringCompare( + DPL::FromUTF32String(L"Ala Makota ma żołądkówkę"), + DPL::FromUTF32String(L"Ala Makota ma żołądkówkę")) == 0); +} + +RUNNER_TEST(String_CompareCaseInsensitive) +{ + RUNNER_ASSERT( + DPL::StringCompare( + DPL::FromUTF32String(L"Ala Makota ma żołądkówkę"), + DPL::FromUTF32String(L"AlA MakOTA ma ŻoŁąDKÓwkę"), + true) == 0); +} + diff --git a/tests/core/test_task.cpp b/tests/core/test_task.cpp new file mode 100644 index 0000000..4ad00c3 --- /dev/null +++ b/tests/core/test_task.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_task.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of task tests + */ +#include +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +class MySingleTask + : public DPL::TaskDecl +{ +protected: + void StepOne() + { + } + +public: + MySingleTask() + : DPL::TaskDecl(this) + { + AddStep(&MySingleTask::StepOne); + } +}; + +class MyMultiTask + : public DPL::MultiTaskDecl +{ +protected: + typedef DPL::MultiTaskDecl BaseType; + + void StepOne() + { + LogInfo("Step one"); + } + + void StepTwo() + { + LogInfo("Step two"); + } + + void StepThree() + { + LogInfo("Step three"); + } + +public: + MyMultiTask() + : BaseType(this, 2) + { + BaseType::StepList depListStepThree; + depListStepThree.push_back(&MyMultiTask::StepOne); + depListStepThree.push_back(&MyMultiTask::StepTwo); + AddStep(&MyMultiTask::StepThree, depListStepThree); + + BaseType::StepList depListStepTwo; + depListStepTwo.push_back(&MyMultiTask::StepOne); + AddStep(&MyMultiTask::StepTwo, depListStepTwo); + + BaseType::StepList depListStepOne; + AddStep(&MyMultiTask::StepOne, depListStepOne); + } +}; + +RUNNER_TEST(Task_SingleTask) +{ + MySingleTask task; + while (task.NextStep()); +} + +RUNNER_TEST(Task_MultiTask) +{ + MyMultiTask task; + while (task.NextStep()); +} diff --git a/tests/core/test_thread.cpp b/tests/core/test_thread.cpp new file mode 100644 index 0000000..202d468 --- /dev/null +++ b/tests/core/test_thread.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_thread.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of thread tests + */ +#include +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +bool g_wasFooDeleted; + +class Foo +{ +public: + int id; + Foo(int i=0): id(i) + { + LogInfo("Foo: ctor: " << id); + } + + ~Foo() + { + LogInfo("Foo: dtor: " << id); + g_wasFooDeleted = true; + } + + void Bar() + { + LogInfo("Foo: bar"); + } +}; + +typedef DPL::ThreadLocalVariable TlsFoo; +TlsFoo g_foo; + +class FooThread + : public DPL::Thread +{ +protected: + virtual int ThreadEntry() + { + LogInfo("In thread"); + + RUNNER_ASSERT(!g_foo); + RUNNER_ASSERT(g_foo.IsNull()); + + g_foo = Foo(); + g_foo->Bar(); + + return 0; + } +}; + +RUNNER_TEST(Thread_ThreadLocalVariable_FooDeletion) +{ + static TlsFoo staticFooForMain; + staticFooForMain = Foo(1); + + TlsFoo fooForMain; + fooForMain = Foo(2); + + RUNNER_ASSERT(!g_foo); + RUNNER_ASSERT(g_foo.IsNull()); + + g_wasFooDeleted = false; + + FooThread thread1; + thread1.Run(); + thread1.Quit(); + + RUNNER_ASSERT(!g_foo); + RUNNER_ASSERT(g_foo.IsNull()); + + RUNNER_ASSERT(g_wasFooDeleted == true); + + FooThread thread2; + thread2.Run(); + thread2.Quit(); + + RUNNER_ASSERT(!g_foo); + RUNNER_ASSERT(g_foo.IsNull()); +} diff --git a/tests/core/test_type_list.cpp b/tests/core/test_type_list.cpp new file mode 100644 index 0000000..98167f3 --- /dev/null +++ b/tests/core/test_type_list.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file test_type_list.cpp + * @author Krzysztof Jackiewicz (k.jackiewicz@samsung.com) + * @version 0.1 + * @brief + */ + +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +RUNNER_TEST(TypeList_TypeCount) +{ + typedef DPL::TypeListDecl::Type TestTypeList1; + typedef DPL::TypeListDecl::Type TestTypeList2; + typedef DPL::TypeListDecl<>::Type TestTypeList3; + typedef DPL::TypeList TestTypeList4; + + RUNNER_ASSERT(TestTypeList1::Size == 3); + RUNNER_ASSERT(TestTypeList2::Size == 1); + RUNNER_ASSERT(TestTypeList3::Size == 0); + RUNNER_ASSERT(TestTypeList4::Size == 4); + + RUNNER_ASSERT(TestTypeList4::Tail::Tail::Size == 2); +} diff --git a/tests/core/test_zip_input.cpp b/tests/core/test_zip_input.cpp new file mode 100644 index 0000000..9498e76 --- /dev/null +++ b/tests/core/test_zip_input.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_zip_input.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of zip input tests + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +const char* PATH_NO_FILE = "/opt/apps/wrt/wrt-commons/tests/core/no_such_file"; +const char* PATH_ARCHIVE = "/opt/apps/wrt/wrt-commons/tests/core/sample.zip"; +const char* ARCHIVED_FILE = "sample.txt"; +} + +RUNNER_TEST_GROUP_INIT(DPL) + +RUNNER_TEST(ZipInput_OpenFailed) +{ + bool opened = true; + + Try + { + DPL::ZipInput zip(PATH_NO_FILE); + (void)zip; + } + Catch(DPL::ZipInput::Exception::OpenFailed) + { + opened = false; + } + + RUNNER_ASSERT(opened == false); +} + +RUNNER_TEST(ZipInput_OpenFile) +{ + DPL::ZipInput zip(PATH_ARCHIVE); + + FOREACH(iter, zip) + { + LogDebug("---------"); + LogDebug("FileInfo: "); +#define FIELD(X) LogDebug(#X ": " << iter->X) + FIELD(name); + FIELD(comment); + FIELD(version); + FIELD(versionNeeded); + FIELD(flag); + FIELD(compressionMethod); + FIELD(dosDate); + FIELD(crc); + FIELD(compressedSize); + FIELD(uncompressedSize); + FIELD(diskNumberStart); + FIELD(internalFileAttributes); + FIELD(externalFileAttributes); +#undef FIELD + } +} + +RUNNER_TEST(ZipInput_UnzipSingleFile) +{ + DPL::ZipInput zip(PATH_ARCHIVE); + DPL::ZipInput::File *file = zip.OpenFile(ARCHIVED_FILE); + DPL::AbstractWaitableInputAdapter fileAdapter(file); + DPL::BinaryQueue buffer; + DPL::AbstractWaitableOutputAdapter bufferAdapter(&buffer); + + DPL::Copy(&fileAdapter, &bufferAdapter); + + DPL::ScopedArray data(new char[buffer.Size() + 1]); + buffer.Flatten(data.Get(), buffer.Size()); + data[buffer.Size()] = '\0'; + + RUNNER_ASSERT(std::string(data.Get()) == "test"); +} diff --git a/tests/db/CMakeLists.txt b/tests/db/CMakeLists.txt new file mode 100644 index 0000000..5276c06 --- /dev/null +++ b/tests/db/CMakeLists.txt @@ -0,0 +1,76 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +# +# Test files +# +# Define all DPL tests sources. +# Runner is responsible for runnint it all and +# generating proper output files +# + +SET(TARGET_NAME "dpl-tests-db") + +# Set DPL tests sources +SET(DPL_TESTS_SOURCES + ${PROJECT_SOURCE_DIR}/tests/db/main.cpp + ${PROJECT_SOURCE_DIR}/tests/db/test_orm.cpp + ${PROJECT_SOURCE_DIR}/tests/db/test_sql_connection.cpp +) + +ADD_SUBDIRECTORY(orm) + +INCLUDE_DIRECTORIES( + ${SYS_EFL_INCLUDE_DIRS} + ${DPL_TEST_INCLUDE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/orm +) + +LINK_DIRECTORIES(${SYS_EFL_LIBRARY_DIRS}) + +ADD_EXECUTABLE(${TARGET_NAME} ${DPL_TESTS_SOURCES}) + +TARGET_LINK_LIBRARIES( + ${TARGET_NAME} + ${TARGET_DPL_EFL} + ${TARGET_DPL_DB_EFL} + ${TARGET_DPL_TEST_ENGINE_EFL} +) + +SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + BUILD_WITH_INSTALL_RPATH ON + INSTALL_RPATH_USE_LINK_PATH ON +) + +INSTALL(TARGETS ${TARGET_NAME} + DESTINATION bin + PERMISSIONS OWNER_READ + OWNER_WRITE + OWNER_EXECUTE + GROUP_READ + GROUP_EXECUTE + WORLD_READ + WORLD_EXECUTE +) + +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/tests/db/orm/dpl_orm_test.db + DESTINATION /opt/apps/wrt/wrt-commons/tests/db +) \ No newline at end of file diff --git a/tests/db/main.cpp b/tests/db/main.cpp new file mode 100644 index 0000000..4ed6191 --- /dev/null +++ b/tests/db/main.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file main.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of main. + */ + +#include + +int main(int argc, char *argv[]) +{ + return DPL::Test::TestRunnerSingleton::Instance().ExecTestRunner(argc, argv); +} diff --git a/tests/db/orm/CMakeLists.txt b/tests/db/orm/CMakeLists.txt new file mode 100644 index 0000000..5526a76 --- /dev/null +++ b/tests/db/orm/CMakeLists.txt @@ -0,0 +1,9 @@ + +ADD_CUSTOM_COMMAND( OUTPUT dpl_orm_test_db.sql + COMMAND rm -f ${CMAKE_CURRENT_SOURCE_DIR}/dpl_orm_test.db + COMMAND gcc -Wall -I${DPL_DB_INCLUDE_DIR} -E ${CMAKE_CURRENT_SOURCE_DIR}/dpl_orm_test_db_sql_generator.h | grep --invert-match "^#" > ${CMAKE_CURRENT_SOURCE_DIR}/dpl_orm_test_db.sql + COMMAND sqlite3 ${CMAKE_CURRENT_SOURCE_DIR}/dpl_orm_test.db ".read ${CMAKE_CURRENT_SOURCE_DIR}/dpl_orm_test_db.sql" || rm -f ${CMAKE_CURRENT_SOURCE_DIR}/dpl_orm_test.db + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/dpl_orm_test_db_sql_generator.h ${CMAKE_CURRENT_SOURCE_DIR}/dpl_orm_test_db +) + +ADD_CUSTOM_TARGET( Sqlite3Db ALL DEPENDS dpl_orm_test_db.sql ) diff --git a/tests/db/orm/dpl_orm_test.db b/tests/db/orm/dpl_orm_test.db new file mode 100644 index 0000000000000000000000000000000000000000..6c3d768e1ce403afaa09a48ef6cafc1567e2c934 GIT binary patch literal 16384 zcmeI&!A`_*=d5wp z=B!?4eju4Y^1S8$tILk&H%%%o?!2hl=Z<|*Ijpkk)XikAT8+MH@wS@66LQgIxYUwc z42!96lqNRX@`qv|e_mI(%p^A&7E|8nY&KUzKLP>}fB*y_009U<00Izz00bZaffW{* z&HuXjM$9ks!+ci>0s;_#00bZa0SG_<0uX=z1Rwx`^avDnLw$} zY + +#include "dpl_orm_test_db_definitions" diff --git a/tests/db/orm/generator_dpl_orm_test.h b/tests/db/orm/generator_dpl_orm_test.h new file mode 100644 index 0000000..39bb1b7 --- /dev/null +++ b/tests/db/orm/generator_dpl_orm_test.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ORM_GENERATOR_DPL_ORM_TEST_H +#define ORM_GENERATOR_DPL_ORM_TEST_H + +#define ORM_GENERATOR_DATABASE_NAME dpl_orm_test_db_definitions +#include +#undef ORM_GENERATOR_DATABASE_NAME + +#endif diff --git a/tests/db/test_orm.cpp b/tests/db/test_orm.cpp new file mode 100644 index 0000000..ddcbe79 --- /dev/null +++ b/tests/db/test_orm.cpp @@ -0,0 +1,712 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +namespace { +const char* PATH_DB = "/opt/apps/wrt/wrt-commons/tests/db/dpl_orm_test.db"; +} + +//utils + +#define TEST_REPETITION 16 + +class SmartAttach +{ +public: + + SmartAttach(bool autoattach = true) : + m_interface(PATH_DB, + DPL::DB::SqlConnection::Flag::UseLucene), + m_autoattach(autoattach) + { + if (m_autoattach) { + m_interface.AttachToThread(); + } + } + + ~SmartAttach() + { + if (m_autoattach) { + m_interface.DetachFromThread(); + } + } + + DPL::DB::ThreadDatabaseSupport* get() + { + return &m_interface; + } +private: + DPL::DB::ThreadDatabaseSupport m_interface; + bool m_autoattach; +}; + +template +bool ContainerContentsEqual(const ContainerType1& container1, const ContainerType2& container2) +{ + using namespace DPL::DB::ORM::dpl_orm_test::TestTableInsert; + typedef std::set Set1; + typedef std::set Set2; + Set1 set1(container1.begin(), container1.end()); + Set2 set2(container2.begin(), container2.end()); + + for (typename Set1::iterator it = set1.begin(); + it != set1.end(); + it++) + { + LogDebug("Set1 element: " << *it); + } + + for (typename Set2::iterator it = set2.begin(); it != set2.end(); it++) + { + LogDebug("Set2 element: " << *it); + } + + return set1 == set2; +} + +template +std::list makeList(const T& a, const T& b) +{ + std::list list; + list.push_back(a); + list.push_back(b); + return list; +} + +//tests + +RUNNER_TEST_GROUP_INIT(DPL) + +RUNNER_TEST(ORM_SelectSingleValue) +{ + SmartAttach interface; + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::dpl_orm_test; + //Getting each column + { + TestTable::Select select(interface.get()); + select.Where(Equals(8)); + int result; + RUNNER_ASSERT_MSG((result = *select.GetSingleValue()) == 6, "Got " << result); + } + { + TestTable::Select select(interface.get()); + select.Where(Equals(8)); + DPL::String result; + RUNNER_ASSERT_MSG((result = *select.GetSingleValue()) == L"seven", "Got " << result); + } + { + TestTable::Select select(interface.get()); + select.Where(Equals(8)); + int result; + RUNNER_ASSERT_MSG((result = select.GetSingleValue()) == 8, "Got " << result); + } + { + TestTable::Select select(interface.get()); + select.Where(Equals(8)); + int result; + RUNNER_ASSERT_MSG((result = select.GetSingleValue()) == 9, "Got " << result); + } + { + TestTable::Select select(interface.get()); + select.Where(Equals(8)); + DPL::String result; + RUNNER_ASSERT_MSG((result = select.GetSingleValue()) == L"ten", "Got " << result); + } + + //Where on each column + { + TestTable::Select select(interface.get()); + select.Where(Equals(6)); + int result; + RUNNER_ASSERT_MSG((result = *select.GetSingleValue()) == 6, "Got " << result); + } + { + TestTable::Select select(interface.get()); + select.Where(Equals(DPL::String(L"seven"))); + DPL::String result; + RUNNER_ASSERT_MSG((result = *select.GetSingleValue()) == L"seven", "Got " << result); + } + { + TestTable::Select select(interface.get()); + select.Where(Equals(8)); + int result; + RUNNER_ASSERT_MSG((result = select.GetSingleValue()) == 8, "Got " << result); + } + { + TestTable::Select select(interface.get()); + select.Where(Equals(9)); + int result; + RUNNER_ASSERT_MSG((result = select.GetSingleValue()) == 9, "Got " << result); + } + { + TestTable::Select select(interface.get()); + select.Where(Equals(L"ten")); + DPL::String result; + RUNNER_ASSERT_MSG((result = select.GetSingleValue()) == L"ten", "Got " << result); + } +} + +RUNNER_TEST(ORM_SelectSingleRow) +{ + SmartAttach interface; + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::dpl_orm_test; + { + TestTable::Select select(interface.get()); + select.Where(Equals(3)); + TestTable::Row result = select.GetSingleRow(); + TestTable::Row expected; + expected.Set_ColumnOptInt(1); + expected.Set_ColumnOptText(DPL::String(L"two")); + expected.Set_ColumnInt(3); + expected.Set_ColumnInt2(4); + expected.Set_ColumnText(L"five"); + RUNNER_ASSERT_MSG(result == expected, "Got " << result); + } + + { + TestTable::Select select(interface.get()); + select.Where(Equals(DPL::String(L"seven"))); + TestTable::Row result = select.GetSingleRow(); + TestTable::Row expected; + expected.Set_ColumnOptInt(6); + expected.Set_ColumnOptText(DPL::String(L"seven")); + expected.Set_ColumnInt(8); + expected.Set_ColumnInt2(9); + expected.Set_ColumnText(L"ten"); + RUNNER_ASSERT_MSG(result == expected, "Got " << result); + } +} + +RUNNER_TEST(ORM_SelectRowList) +{ + SmartAttach interface; + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::dpl_orm_test; + { + TestTable::Select select(interface.get()); + select.Where(Equals(3)); + std::list result = select.GetRowList(); + RUNNER_ASSERT_MSG(result.size() == 1, "Got " << result.size()); + + TestTable::Row expected; + expected.Set_ColumnOptInt(1); + expected.Set_ColumnOptText(DPL::String(L"two")); + expected.Set_ColumnInt(3); + expected.Set_ColumnInt2(4); + expected.Set_ColumnText(L"five"); + RUNNER_ASSERT_MSG(*(result.begin()) == expected, "Got " << *(result.begin()) ); + } + + { + TestTable::Select select(interface.get()); + select.Where(Equals(DPL::String(L"seven"))); + std::list result = select.GetRowList(); + RUNNER_ASSERT_MSG(result.size() == 1, "Got " << result.size()); + + TestTable::Row expected; + expected.Set_ColumnOptInt(6); + expected.Set_ColumnOptText(DPL::String(L"seven")); + expected.Set_ColumnInt(8); + expected.Set_ColumnInt2(9); + expected.Set_ColumnText(L"ten"); + RUNNER_ASSERT_MSG(*(result.begin()) == expected, "Got " << *(result.begin()) ); + } + + { + TestTable::Select select(interface.get()); + select.Where(Equals(99)); + std::list result = select.GetRowList(); + + TestTable::Row expected1; + expected1.Set_ColumnInt(99); + expected1.Set_ColumnInt2(11); + expected1.Set_ColumnText(L"twelve"); + + TestTable::Row expected2; + expected2.Set_ColumnInt(99); + expected2.Set_ColumnInt2(13); + expected2.Set_ColumnText(L"fourteen"); + + RUNNER_ASSERT(ContainerContentsEqual(makeList(expected1, expected2), result)); + } +} + +RUNNER_TEST(ORM_SelectValueList) +{ + SmartAttach interface; + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::dpl_orm_test; + //Getting each column + { + TestTable::Select select(interface.get()); + select.Where(Is(DPL::Optional::Null)); + RUNNER_ASSERT(ContainerContentsEqual(select.GetValueList(), + makeList(99, 99))); + } + { + TestTable::Select select(interface.get()); + select.Where(Is(DPL::Optional::Null)); + RUNNER_ASSERT(ContainerContentsEqual(select.GetValueList(), + makeList(11, 13))); + } + { + TestTable::Select select(interface.get()); + select.Where(Is(DPL::Optional::Null)); + RUNNER_ASSERT(ContainerContentsEqual(select.GetValueList(), + makeList(DPL::String(L"twelve"), DPL::String(L"fourteen")))); + } + { + TestTable::Select select(interface.get()); + select.Where(Is(DPL::Optional::Null)); + RUNNER_ASSERT(ContainerContentsEqual(select.GetValueList(), + makeList(DPL::Optional::Null,DPL::Optional::Null))); + } + + //Where on each column + { + TestTable::Select select(interface.get()); + select.Where(Is(DPL::Optional::Null)); + RUNNER_ASSERT(ContainerContentsEqual(select.GetValueList(), + makeList(11, 13))); + } + { + TestTable::Select select(interface.get()); + select.Where(Is(DPL::Optional::Null)); + RUNNER_ASSERT(ContainerContentsEqual(select.GetValueList(), + makeList(11, 13))); + } + { + TestTable::Select select(interface.get()); + select.Where(Is(99)); + RUNNER_ASSERT(ContainerContentsEqual(select.GetValueList(), + makeList(11, 13))); + } +} + +RUNNER_TEST(ORM_MultipleCalls) +{ + for (int j = 0 ; j < TEST_REPETITION ; j++ ) + { + for (int i = 0 ; i < TEST_REPETITION ; i++ ) + ORM_SelectSingleValue(); + + for (int i = 0 ; i < TEST_REPETITION ; i++ ) + ORM_SelectSingleRow(); + + for (int i = 0 ; i < TEST_REPETITION ; i++ ) + ORM_SelectRowList(); + + for (int i = 0 ; i < TEST_REPETITION ; i++ ) + ORM_SelectValueList(); + } +} + +RUNNER_TEST(ORM_Insert) +{ + SmartAttach interface; + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::dpl_orm_test; + + TestTableInsert::Select select1(interface.get()); + std::list resultList = select1.GetValueList(); + RUNNER_ASSERT_MSG(resultList.size() == 0, "Returned list has wrong size: " << resultList.size()); + std::list list; + + TestTableInsert::Insert insert(interface.get()); + TestTableInsert::Row row; + row.Set_ColumnOptInt(1); + row.Set_ColumnInt2(2); + row.Set_ColumnText(L"three"); + insert.Values(row); + insert.Execute(); + + row.Set_ColumnInt(99); + list.push_back(row); + { + TestTableInsert::Select select2(interface.get()); + RUNNER_ASSERT_MSG(ContainerContentsEqual(select2.GetRowList(), list), "Returned list doesn't match."); + } + + TestTableInsert::Insert insert2(interface.get()); + TestTableInsert::Row row2; + row2.Set_ColumnInt(4); + row2.Set_ColumnInt2(5); + row2.Set_ColumnText(L"six"); + insert2.Values(row2); + insert2.Execute(); + + list.push_back(row2); + { + TestTableInsert::Select select(interface.get()); + RUNNER_ASSERT_MSG(ContainerContentsEqual(select.GetRowList(), list), "Returned list doesn't match."); + } + + TestTableInsert::Insert insert3(interface.get()); + TestTableInsert::Row row3; + row3.Set_ColumnOptInt(1); + row3.Set_ColumnInt2(7); + row3.Set_ColumnText(L"eight"); + insert3.Values(row3); + insert3.Execute(); + + row3.Set_ColumnInt(99); + list.push_back(row3); + { + TestTableInsert::Select select3(interface.get()); + RUNNER_ASSERT_MSG(ContainerContentsEqual(select3.GetRowList(), list), "Returned list doesn't match."); + } + + TestTableInsert::Insert insert4(interface.get()); + TestTableInsert::Row row4; + row4.Set_ColumnOptInt(9); + row4.Set_ColumnInt2(10); + row4.Set_ColumnText(L"eleven"); + insert4.Values(row4); + insert4.Execute(); + + row4.Set_ColumnInt(99); + list.push_back(row4); + { + TestTableInsert::Select select4(interface.get()); + RUNNER_ASSERT_MSG(ContainerContentsEqual(select4.GetRowList(), list), "Returned list doesn't match."); + } + + // restore original table state + { + TestTableInsert::Delete del(interface.get()); + del.Execute(); + + TestTableInsert::Select select(interface.get()); + RUNNER_ASSERT(select.GetRowList().size() == 0); + } +} + +RUNNER_TEST(ORM_MultipleBindInsert) +{ + for ( int i = 0 ; i < TEST_REPETITION ; i++ ) + { + ORM_Insert(); + } +} + +RUNNER_TEST(ORM_Delete) +{ + SmartAttach interface; + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::dpl_orm_test; + TestTableDelete::Select selectStart(interface.get()); + selectStart.OrderBy("ColumnInt2 ASC"); + std::list list = selectStart.GetRowList(); + std::list originalList = list; + + std::vector vector(list.begin(), list.end()); + RUNNER_ASSERT_MSG(list.size() == 4, "Returned list has wrong size: " << list.size()); + + typedef DPL::String S; + + //no-act deletes + { + TestTableDelete::Delete del(interface.get()); + del.Where(And(Equals(1), Equals(S(L"seven")))); + del.Execute(); + + TestTableDelete::Select select(interface.get()); + RUNNER_ASSERT_MSG(ContainerContentsEqual(select.GetRowList(), list), "Returned list doesn't match."); + } + + { + TestTableDelete::Delete del(interface.get()); + del.Where(And(Equals(6), Equals(S(L"two")))); + del.Execute(); + + TestTableDelete::Select select(interface.get()); + RUNNER_ASSERT_MSG(ContainerContentsEqual(select.GetRowList(), list), "Returned list doesn't match."); + } + + { + TestTableDelete::Delete del(interface.get()); + del.Where(Equals(10)); + del.Execute(); + + TestTableDelete::Select select(interface.get()); + RUNNER_ASSERT_MSG(ContainerContentsEqual(select.GetRowList(), list), "Returned list doesn't match."); + } + + //act deletes + { + list.remove(vector[1]); + + TestTableDelete::Delete del(interface.get()); + del.Where(And(Equals(6), Equals(L"ten"))); + del.Execute(); + + TestTableDelete::Select select(interface.get()); + RUNNER_ASSERT_MSG(ContainerContentsEqual(select.GetRowList(), list), "Returned list doesn't match."); + } + + { + list.remove(vector[2]); + list.remove(vector[3]); + + TestTableDelete::Delete del(interface.get()); + del.Where(Is(DPL::Optional::Null)); + del.Execute(); + + TestTableDelete::Select select(interface.get()); + RUNNER_ASSERT_MSG(ContainerContentsEqual(select.GetRowList(), list), "Returned list doesn't match."); + } + + { + TestTableDelete::Delete del(interface.get()); + del.Execute(); + + TestTableDelete::Select select(interface.get()); + RUNNER_ASSERT_MSG(select.GetRowList().size() == 0, "Returned list is not empty"); + } + + // Restore original table state + // This also tests if multiple different binds for Insert are working properly + for (std::list::iterator i = originalList.begin(); i != originalList.end(); i++) + { + TestTableDelete::Insert insert(interface.get()); + insert.Values(*i); + insert.Execute(); + } + + { + TestTableDelete::Select select(interface.get()); + RUNNER_ASSERT_MSG(ContainerContentsEqual(select.GetRowList(), originalList), "Returned list doesn't match."); + } + +} + +RUNNER_TEST(ORM_MultipleBindDelete) +{ + for ( int i = 0 ; i < TEST_REPETITION ; i++ ) + { + ORM_Delete(); + } +} + +RUNNER_TEST(ORM_MultipleBindWhere) +{ + SmartAttach interface; + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::dpl_orm_test; + { + TestTable::Select select(interface.get()); + int result; + select.Where(Equals(8)); + RUNNER_ASSERT_MSG((result = *select.GetSingleValue()) == 6, "Got " << result); + + select.Where(Equals(3)); + RUNNER_ASSERT_MSG((result = *select.GetSingleValue()) == 1, "Got " << result); + + select.Where(Equals(8)); + RUNNER_ASSERT_MSG((result = *select.GetSingleValue()) == 6, "Got " << result); + + select.Where(Equals(3)); + RUNNER_ASSERT_MSG((result = *select.GetSingleValue()) == 1, "Got " << result); + } + + { + TestTable::Select select(interface.get()); + int result; + select.Where(And(Equals(99), + Equals(L"fourteen"))); + RUNNER_ASSERT_MSG((result = select.GetSingleValue()) == 13, "Got " << result); + + select.Where(And(Equals(99), + Equals(L"twelve"))); + RUNNER_ASSERT_MSG((result = select.GetSingleValue()) == 11, "Got " << result); + + select.Where(And(Equals(99), + Equals(L"fourteen"))); + RUNNER_ASSERT_MSG((result = select.GetSingleValue()) == 13, "Got " << result); + + select.Where(And(Equals(99), + Equals(L"twelve"))); + RUNNER_ASSERT_MSG((result = select.GetSingleValue()) == 11, "Got " << result); + } + + { + TestTable::Select select(interface.get()); + int result; + select.Where(And(Equals(L"fourteen"), + Equals(99))); + RUNNER_ASSERT_MSG((result = select.GetSingleValue()) == 13, "Got " << result); + + select.Where(And(Equals(L"twelve"), + Equals(99))); + RUNNER_ASSERT_MSG((result = select.GetSingleValue()) == 11, "Got " << result); + + select.Where(And(Equals(L"fourteen"), + Equals(99))); + RUNNER_ASSERT_MSG((result = select.GetSingleValue()) == 13, "Got " << result); + + select.Where(And(Equals(L"twelve"), + Equals(99))); + RUNNER_ASSERT_MSG((result = select.GetSingleValue()) == 11, "Got " << result); + + } + +} + +RUNNER_TEST(ORM_Update) +{ + SmartAttach interface; + using namespace DPL::DB::ORM; + using namespace DPL::DB::ORM::dpl_orm_test; + + std::list list; + + TestTableInsert::Delete del(interface.get()); + del.Execute(); + + // INSERT + { + TestTableInsert::Insert insert(interface.get()); + TestTableInsert::Row row; + row.Set_ColumnOptInt(5); + row.Set_ColumnInt2(2); + row.Set_ColumnText(L"two"); + insert.Values(row); + insert.Execute(); + + row.Set_ColumnInt(99); + list.push_back(row); + } + { + TestTableInsert::Insert insert(interface.get()); + TestTableInsert::Row row; + row.Set_ColumnOptInt(1); + row.Set_ColumnInt2(2); + row.Set_ColumnText(L"three"); + insert.Values(row); + insert.Execute(); + + row.Set_ColumnInt(99); + list.push_back(row); + } + { + TestTableInsert::Insert insert(interface.get()); + TestTableInsert::Row row; + row.Set_ColumnOptInt(2); + row.Set_ColumnInt2(3); + row.Set_ColumnText(L"three"); + insert.Values(row); + insert.Execute(); + + row.Set_ColumnInt(99); + list.push_back(row); + + // CHECK + TestTableInsert::Select select(interface.get()); + RUNNER_ASSERT_MSG(ContainerContentsEqual(select.GetRowList(), list), "Returned list doesn't match."); + } + { + // UPDATE - no rows + TestTableInsert::Update update(interface.get()); + TestTableInsert::Row row; + row.Set_ColumnInt2(4); + row.Set_ColumnText(L"four"); + update.Values(row); + update.Where(Equals(12)); + update.Execute(); + + // CHECK + TestTableInsert::Select select(interface.get()); + RUNNER_ASSERT_MSG(ContainerContentsEqual(select.GetRowList(), list), "Returned list doesn't match."); + } + { + // UPDATE - one row + TestTableInsert::Update update(interface.get()); + TestTableInsert::Row row; + row.Set_ColumnInt2(2); + row.Set_ColumnText(L"four"); + update.Values(row); + update.Where(Equals(3)); + update.Execute(); + + list.back().Set_ColumnInt2(2); + list.back().Set_ColumnText(L"four"); + + // CHECK + TestTableInsert::Select select(interface.get()); + RUNNER_ASSERT_MSG(ContainerContentsEqual(select.GetRowList(), list), "Returned list doesn't match."); + } + + { + // UPDATE - multiple rows + TestTableInsert::Update update(interface.get()); + TestTableInsert::Row row; + row.Set_ColumnText(L"dup"); + update.Values(row); + update.Where(Equals(2)); + update.Execute(); + + FOREACH(it, list) + { + it->Set_ColumnText(L"dup"); + } + + // CHECK + TestTableInsert::Select select(interface.get()); + RUNNER_ASSERT_MSG(ContainerContentsEqual(select.GetRowList(), list), "Returned list doesn't match."); + } + + // restore original table state + { + TestTableInsert::Delete del2(interface.get()); + del2.Execute(); + + TestTableInsert::Select select(interface.get()); + RUNNER_ASSERT(select.GetRowList().size() == 0); + } +} + +RUNNER_TEST(ORM_MultipleBindUpdate) +{ + for ( int i = 0 ; i < TEST_REPETITION ; i++ ) + { + ORM_Update(); + } +} + +RUNNER_TEST(ORM_transactions) +{ + SmartAttach interface; + DPL::DB::ORM::dpl_orm_test::ScopedTransaction transaction(interface.get()); +} + +RUNNER_TEST(ORM_MultiAttach) +{ + SmartAttach interface(false); + RUNNER_ASSERT_MSG(!interface.get()->IsAttached(), "Is attached, but shouldn't be."); + interface.get()->AttachToThread(); + RUNNER_ASSERT_MSG(interface.get()->IsAttached(), "Isn't attached, but should be."); + interface.get()->AttachToThread(); + RUNNER_ASSERT_MSG(interface.get()->IsAttached(), "Isn't attached, but should be."); + interface.get()->DetachFromThread(); + RUNNER_ASSERT_MSG(interface.get()->IsAttached(), "Isn't attached, but should be."); + interface.get()->DetachFromThread(); + RUNNER_ASSERT_MSG(!interface.get()->IsAttached(), "Is attached, but shouldn't be."); +} diff --git a/tests/db/test_sql_connection.cpp b/tests/db/test_sql_connection.cpp new file mode 100644 index 0000000..8dd3316 --- /dev/null +++ b/tests/db/test_sql_connection.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_sql_connection.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of sql connection tests + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +class AbstractSynchronizationObjectGenerator +{ +public: + virtual ~AbstractSynchronizationObjectGenerator() {} + + virtual DPL::DB::SqlConnection::SynchronizationObject *Create() = 0; +}; + +class NaiveSynchronizationObjectGenerator + : public AbstractSynchronizationObjectGenerator +{ +public: + virtual DPL::DB::SqlConnection::SynchronizationObject *Create() + { + return new DPL::DB::NaiveSynchronizationObject(); + } +}; + +void MassiveReadWriteTest(AbstractSynchronizationObjectGenerator *generator); + +class StressGenerator + : public DPL::Thread +{ +private: + size_t m_prefix; + std::string m_dbFileName; + AbstractSynchronizationObjectGenerator *m_generator; + +protected: + virtual int ThreadEntry() + { + DPL::DB::SqlConnection connection( + m_dbFileName, + DPL::DB::SqlConnection::Flag::None, + m_generator->Create()); + + DPL::DB::SqlConnection::DataCommandAutoPtr countCommand = + connection.PrepareDataCommand( + "SELECT COUNT(*) FROM test WHERE value=?"); + + for (size_t i = 0; i < 10; ++i) + { + std::ostringstream valueStream; + + valueStream << "value_"; + valueStream << static_cast(m_prefix); + valueStream << "_"; + valueStream << static_cast(i); + + std::string value = valueStream.str(); + + connection.ExecCommand( + "INSERT INTO test VALUES ('%s');", + value.c_str()); + + countCommand->BindString(1, value.c_str()); + + RUNNER_ASSERT(countCommand->Step()); + + RUNNER_ASSERT(countCommand->GetColumnString(0) == "1"); + + countCommand->Reset(); + } + + countCommand.reset(); + + return 0; + } + +public: + StressGenerator(size_t prefix, + const std::string &dbFileName, + AbstractSynchronizationObjectGenerator *generator) + : m_prefix(prefix), + m_dbFileName(dbFileName), + m_generator(generator) + { + } +}; + +typedef DPL::SharedPtr ThreadPtr; + +void MassiveReadWriteTest(AbstractSynchronizationObjectGenerator *generator) +{ + std::ostringstream dbFileNameStream; + dbFileNameStream << "/tmp/dpl_tests_db_"; + dbFileNameStream << rand() << ".db"; + + std::string dbFileName = dbFileNameStream.str(); + + LogDebug("Temporary database used: " << dbFileName); + + DPL::DB::SqlConnection connection(dbFileName); + connection.ExecCommand("BEGIN TRANSACTION;"); + connection.ExecCommand("CREATE TABLE test(value TEXT);"); + connection.ExecCommand("COMMIT;"); + + const size_t STRESS_GENERATOR_COUNT = 5; + ThreadPtr stressGenerators[STRESS_GENERATOR_COUNT]; + + for (size_t i = 0; i < STRESS_GENERATOR_COUNT; ++i) + { + stressGenerators[i].Reset( + new StressGenerator(i, dbFileName, generator)); + + stressGenerators[i]->Run(); + } + + for (size_t i = 0; i < STRESS_GENERATOR_COUNT; ++i) + stressGenerators[i]->Quit(); + + unlink(dbFileName.c_str()); +} + +RUNNER_TEST(SqlConnection_MassiveReadWrite_NaiveSynchronization) +{ + srand(time(NULL)); + + NaiveSynchronizationObjectGenerator m_generator; + MassiveReadWriteTest(&m_generator); +} diff --git a/tests/dbus/CMakeLists.txt b/tests/dbus/CMakeLists.txt new file mode 100644 index 0000000..accd45a --- /dev/null +++ b/tests/dbus/CMakeLists.txt @@ -0,0 +1,98 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) +# @version 1.0 +# @brief +# + +INCLUDE(FindPkgConfig) + +SET(TARGET_DBUS_TESTS "dpl-tests-dbus") +SET(TARGET_DBUS_TEST_SERVICE "dpl-dbus-test-service") + +PKG_CHECK_MODULES(DBUS_PKG + ecore + appcore-efl + gio-2.0 + gobject-2.0 + REQUIRED +) + +SET(DBUS_TESTS_SRCS + ${PROJECT_SOURCE_DIR}/tests/dbus/main.cpp + ${PROJECT_SOURCE_DIR}/tests/dbus/test_cases.cpp + ${PROJECT_SOURCE_DIR}/tests/dbus/dbus_test.cpp + ${PROJECT_SOURCE_DIR}/tests/dbus/loop_control.cpp +) + +SET(DBUS_TEST_SERVICE_SRCS + ${PROJECT_SOURCE_DIR}/tests/dbus/test_service.cpp + ${PROJECT_SOURCE_DIR}/tests/dbus/loop_control.cpp +) + +INCLUDE_DIRECTORIES( + ${DBUS_PKG_INCLUDE_DIRS} + ${DPL_TEST_INCLUDE_DIR} +) + +LINK_DIRECTORIES(${DBUS_PKG_LIBRARY_DIRS}) + +ADD_EXECUTABLE(${TARGET_DBUS_TESTS} + ${DBUS_TESTS_SRCS} +) + +TARGET_LINK_LIBRARIES(${TARGET_DBUS_TESTS} + ${DBUS_PKG_LIBRARIES} + ${TARGET_DPL_EFL} + ${TARGET_DPL_DBUS_EFL} + ${TARGET_DPL_TEST_ENGINE_EFL} +) + +SET_TARGET_PROPERTIES(${TARGET_DBUS_TESTS} PROPERTIES + BUILD_WITH_INSTALL_RPATH ON + INSTALL_RPATH_USE_LINK_PATH ON +) + +ADD_EXECUTABLE(${TARGET_DBUS_TEST_SERVICE} + ${DBUS_TEST_SERVICE_SRCS} +) + +TARGET_LINK_LIBRARIES(${TARGET_DBUS_TEST_SERVICE} + ${DBUS_PKG_LIBRARIES} + ${TARGET_DPL_EFL} + ${TARGET_DPL_DBUS_EFL} +) + +SET_TARGET_PROPERTIES(${TARGET_DBUS_TEST_SERVICE} PROPERTIES + BUILD_WITH_INSTALL_RPATH ON + INSTALL_RPATH_USE_LINK_PATH ON +) + +INSTALL(TARGETS ${TARGET_DBUS_TESTS} ${TARGET_DBUS_TEST_SERVICE} + DESTINATION bin + PERMISSIONS OWNER_READ + OWNER_WRITE + OWNER_EXECUTE + GROUP_READ + GROUP_EXECUTE + WORLD_READ + WORLD_EXECUTE +) + +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/tests/dbus/data/org.tizen.DBusTestService.service + DESTINATION /usr/share/dbus-1/services +) diff --git a/tests/dbus/data/org.tizen.DBusTestService.service b/tests/dbus/data/org.tizen.DBusTestService.service new file mode 100644 index 0000000..f024543 --- /dev/null +++ b/tests/dbus/data/org.tizen.DBusTestService.service @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.tizen.DBusTestService +Exec=/usr/bin/dpl-dbus-test-service diff --git a/tests/dbus/dbus_test.cpp b/tests/dbus/dbus_test.cpp new file mode 100644 index 0000000..6071f9f --- /dev/null +++ b/tests/dbus/dbus_test.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file dbus_test.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @brief Implementation file for DBusTest and DBusTestManager. + */ + +#include +#include "loop_control.h" +#include "dbus_test.h" + +DBusTest::DBusTest(const std::string& name) + : m_name(name), + m_status(Status::NONE) +{ +} + +void DBusTest::run(unsigned int timeout) +{ + DPL::Event::ControllerEventHandler::Touch(); + DPL::Event::ControllerEventHandler::Touch(); + + DPL::Event::ControllerEventHandler::PostTimedEvent( + TimeoutEvent(), timeout); + + LoopControl::wrt_start_loop(); + + switch (m_status) + { + case Status::FAILED: + throw DPL::Test::TestRunner::TestFailed(m_name.c_str(), + __FILE__, + __LINE__, + m_message); + + default: + break; + } +} + +void DBusTest::quit() +{ + DPL::Event::ControllerEventHandler::PostEvent(QuitEvent()); +} + +void DBusTest::setStatus(Status status) +{ + m_status = status; +} + +void DBusTest::setMessage(const std::string& message) +{ + m_message = message; +} + +void DBusTest::success() +{ + m_status = Status::SUCCESS; +} + +void DBusTest::fail(const std::string& message) +{ + m_status = Status::FAILED; + m_message = message; +} + +void DBusTest::OnEventReceived(const TimeoutEvent& /*event*/) +{ + fail("Test timed out."); + + // Saving one event dispatch since Quit and Timeout work on the same thread. + LoopControl::wrt_end_loop(); +} + +void DBusTest::OnEventReceived(const QuitEvent& /*event*/) +{ + LoopControl::wrt_end_loop(); +} + +DBusTestManager& DBusTestManager::getInstance() +{ + static DBusTestManager instance; + return instance; +} + +DBusTestManager::DBusTestManager() : m_test(NULL) { } + +DBusTest& DBusTestManager::getCurrentTest() const +{ + Assert(NULL != m_test && "Test not set."); + + return *m_test; +} + +void DBusTestManager::setCurrentTest(DBusTest& test) +{ + m_test = &test; +} + +void DBusTestManager::clear() +{ + m_test = NULL; +} diff --git a/tests/dbus/dbus_test.h b/tests/dbus/dbus_test.h new file mode 100644 index 0000000..9b5061e --- /dev/null +++ b/tests/dbus/dbus_test.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file dbus_test.h + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @brief Header file for DBusTest and DBusTestManager. + */ + +#ifndef WRT_TESTS_DBUS_TESTS_DBUS_TEST_H +#define WRT_TESTS_DBUS_TESTS_DBUS_TEST_H + +#include +#include +#include + +DECLARE_GENERIC_EVENT_0(QuitEvent) +DECLARE_GENERIC_EVENT_0(TimeoutEvent) + +class DBusTest : + private DPL::Event::Controller::Type> +{ +public: + enum class Status + { + NONE, + SUCCESS, + FAILED + }; + + explicit DBusTest(const std::string& name); + + void run(unsigned int timeout); + void quit(); + + void setStatus(Status status); + void setMessage(const std::string& message); + + void success(); + void fail(const std::string& message = std::string()); + +private: + void OnEventReceived(const TimeoutEvent& event); + void OnEventReceived(const QuitEvent& event); + + std::string m_name; + Status m_status; + std::string m_message; +}; + +class DBusTestManager : private DPL::Noncopyable +{ +public: + static DBusTestManager& getInstance(); + + DBusTest& getCurrentTest() const; + void setCurrentTest(DBusTest& test); + + void clear(); + +private: + DBusTestManager(); + + DBusTest* m_test; +}; + +#define DBUS_TEST(TestProc) \ + void DBus##TestProc(); \ + RUNNER_TEST(TestProc) \ + { \ + DBusTest test(#TestProc); \ + DBusTestManager::getInstance().setCurrentTest(test); \ + DBus##TestProc(); \ + DBusTestManager::getInstance().clear(); \ + } \ + void DBus##TestProc() + +#endif diff --git a/tests/dbus/loop_control.cpp b/tests/dbus/loop_control.cpp new file mode 100644 index 0000000..c6c250e --- /dev/null +++ b/tests/dbus/loop_control.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file loop_control.cpp + * @author Jaroslaw Osmanski (j.osmanski@samsung.com) + * @version 1.0 + * @brief This is implementation of EFL version of loop control + */ + +#include "loop_control.h" +#include + +#include + +#include +#include + + +namespace LoopControl +{ +void init_loop(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + g_type_init(); + g_thread_init(NULL); + + LogInfo("Starting"); + elm_init(argc, argv); +} + +void wait_for_wrt_init() +{ + ecore_main_loop_begin(); +} + +void finish_wait_for_wrt_init() +{ + ecore_main_loop_quit(); +} + +void quit_loop() +{ + elm_shutdown(); +} + +void wrt_start_loop() +{ + ecore_main_loop_begin(); +} + +void wrt_end_loop() +{ + ecore_main_loop_quit(); +} + +void *abstract_window() +{ + return elm_win_add(NULL, "hello", ELM_WIN_BASIC); +} + +}//end of LoopControl namespace diff --git a/tests/dbus/loop_control.h b/tests/dbus/loop_control.h new file mode 100644 index 0000000..30aa6e8 --- /dev/null +++ b/tests/dbus/loop_control.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file loop_control.cpp + * @author Jaroslaw Osmanski (j.osmanski@samsung.com) + * @version 1.0 + * @brief This file is the definitions of loop controlling utilities + */ + + +#ifndef LOOP_CONTROL_H_ +#define LOOP_CONTROL_H_ + +namespace LoopControl +{ + +void init_loop(int argc, char *argv[]); +void wait_for_wrt_init(); +void finish_wait_for_wrt_init(); +void quit_loop(); + +void wrt_start_loop(); +void wrt_end_loop(); + +void *abstract_window(); + +} + +#endif /* LOOP_CONTROL_H_ */ diff --git a/tests/dbus/main.cpp b/tests/dbus/main.cpp new file mode 100644 index 0000000..b6bd681 --- /dev/null +++ b/tests/dbus/main.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file main.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of main. + */ + +#include "loop_control.h" +#include +#include + +int main(int argc, char *argv[]) +{ + LoopControl::init_loop(argc, argv); + + LogInfo("Running tests..."); + int status = DPL::Test::TestRunnerSingleton::Instance().ExecTestRunner(argc, argv); + + return status; +} diff --git a/tests/dbus/test_cases.cpp b/tests/dbus/test_cases.cpp new file mode 100644 index 0000000..fe0f9c1 --- /dev/null +++ b/tests/dbus/test_cases.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file TestCases.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief Implementation file for test cases for DBus internal tests. + */ + +#include +#include +#include +#include +#include +#include +#include "dbus_test.h" + +namespace { +const std::string dbusServiceName = "org.freedesktop.DBus"; +const std::string dbusObjectPath = "/"; +const std::string dbusInterfaceName = "org.freedesktop.DBus"; +const std::string dbusMethodGetId = "GetId"; + +const std::string serviceName = "org.tizen.DBusTestService"; +const std::string objectPath = "/org/tizen/DBusTestService"; +const std::string interfaceName = "org.tizen.DBusTestService"; +const std::string methodNameEcho = "echo"; +const std::string methodNameQuit = "quit"; +const std::string nodeInfo = + "" + "" + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +const std::string challenge = "Hello world!"; + +const int DEFAULT_TIMEOUT = 2; // in seconds +} + +RUNNER_TEST(AcquireSessionBus) +{ + try + { + DPL::DBus::Connection::sessionBus(); + } + catch (const DPL::DBus::Exception& ex) + { + RUNNER_ASSERT_MSG(false, ex.DumpToString()); + } +} + +RUNNER_TEST(AcquireSystemBus) +{ + try + { + DPL::DBus::Connection::systemBus(); + } + catch (const DPL::DBus::Exception& ex) + { + RUNNER_ASSERT_MSG(false, ex.DumpToString()); + } +} + +RUNNER_TEST(ParseNodeInfo) +{ + try + { + auto ifaces = DPL::DBus::Interface::fromXMLString(nodeInfo); + RUNNER_ASSERT(!ifaces.empty()); + + auto iface = ifaces.at(0); + RUNNER_ASSERT(NULL != iface->getVTable()); + RUNNER_ASSERT(NULL != iface->getInfo()); + } + catch (const DPL::DBus::Exception& ex) + { + RUNNER_ASSERT_MSG(false, ex.DumpToString()); + } +} + +RUNNER_TEST(InvokeRemoteMethod) +{ + try + { + auto connection = DPL::DBus::Connection::systemBus(); + auto freedesktop = connection->createObjectProxy(dbusServiceName, + dbusObjectPath); + auto getId = freedesktop->createMethodProxy + (dbusInterfaceName, dbusMethodGetId); + RUNNER_ASSERT(!getId().empty()); + } + catch (const DPL::DBus::Exception& ex) + { + RUNNER_ASSERT_MSG(false, ex.DumpToString()); + } +} + +class RegisterServiceListener : + public DPL::Event::EventListener +{ +public: + void OnEventReceived( + const DPL::DBus::ConnectionEvents::ServiceNameAcquiredEvent& event) + { + DBusTest& test = DBusTestManager::getInstance().getCurrentTest(); + + auto name = event.GetArg0(); + if (serviceName == name) + { + test.success(); + } + else + { + test.fail("Acquired service name: " + name); + } + test.quit(); + } +}; + +DBUS_TEST(RegisterService) +{ + try + { + RegisterServiceListener listener; + + auto connection = DPL::DBus::Connection::sessionBus(); + connection->DPL::Event::EventSupport::AddListener(&listener); + connection->registerService(serviceName); + + DBusTestManager::getInstance().getCurrentTest().run(DEFAULT_TIMEOUT); + } + catch (const DPL::DBus::Exception& ex) + { + RUNNER_ASSERT_MSG(false, ex.DumpToString()); + } +} + +/** + * This test checks: + * - object registration (done on the wrt-dbus-test-service side) + * - service registration (done on the wrt-dbus-test-service side) + * - dispatching method calls (done on the wrt-dbus-test-service side) + * - launching dbus service on demand + * - invoking remote method(s) + */ +DBUS_TEST(InvokeTestService) +{ + try + { + auto connection = DPL::DBus::Connection::sessionBus(); + auto testService = connection->createObjectProxy(serviceName, + objectPath); + auto echo = testService->createMethodProxy + (interfaceName, methodNameEcho); + auto response = echo(challenge); + + testService->createMethodProxy(interfaceName, methodNameQuit)(); + + RUNNER_ASSERT_MSG(response == challenge, + "[challenge = " << challenge << + ", response = " << response << "]"); + } + catch (const DPL::DBus::Exception& ex) + { + RUNNER_ASSERT_MSG(false, ex.DumpToString()); + } +} diff --git a/tests/dbus/test_service.cpp b/tests/dbus/test_service.cpp new file mode 100644 index 0000000..63e32fb --- /dev/null +++ b/tests/dbus/test_service.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file test_service.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @brief Implementation file for wrt-dbus-test-service. + */ + +#include +#include +#include +#include +#include +#include "loop_control.h" + +namespace { +const std::string serviceName = "org.tizen.DBusTestService"; +const std::string objectPath = "/org/tizen/DBusTestService"; +const std::string interfaceName = "org.tizen.DBusTestService"; +const std::string methodNameEcho = "echo"; +const std::string methodNameQuit = "quit"; +const std::string nodeInfo = + "" + "" + " " + " " + " " + " " + " " + " " + " " + " " + ""; +} + +class TestServiceDispatcher : public DPL::DBus::Dispatcher +{ +private: + void onMethodCall(GDBusConnection* /*connection*/, + const gchar* /*sender*/, + const gchar* /*objectPath*/, + const gchar* /*interfaceName*/, + const gchar* methodName, + GVariant* parameters, + GDBusMethodInvocation* invocation) + { + if (methodNameEcho == methodName) + { + LogDebug("Echo"); + g_dbus_method_invocation_return_value(invocation, + parameters); + } + else if (methodNameQuit == methodName) + { + LogDebug("Quit"); + g_dbus_method_invocation_return_value(invocation, NULL); + LoopControl::wrt_end_loop(); + } + } +}; + +int main(int argc, char* argv[]) +{ + LoopControl::init_loop(argc, argv); + + TestServiceDispatcher dispatcher; + + auto iface = DPL::DBus::Interface::fromXMLString(nodeInfo).at(0); + iface->setDispatcher(&dispatcher); + + auto object = DPL::DBus::Object::create(objectPath, iface); + + auto connection = DPL::DBus::Connection::sessionBus(); + connection->registerObject(object); + connection->registerService(serviceName); + + LoopControl::wrt_start_loop(); + + return 0; +} diff --git a/tests/event/CMakeLists.txt b/tests/event/CMakeLists.txt new file mode 100644 index 0000000..5be052c --- /dev/null +++ b/tests/event/CMakeLists.txt @@ -0,0 +1,70 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Lukasz Marek (l.marek@samsung.com) +# @version 1.0 +# @brief +# + +# +# Test files +# +# Define all DPL tests sources. +# Runner is responsible for runnint it all and +# generating proper output files +# + +SET(TARGET_NAME "dpl-tests-event") + +# Set DPL tests sources +SET(DPL_TESTS_SOURCES + ${PROJECT_SOURCE_DIR}/tests/event/main.cpp + ${PROJECT_SOURCE_DIR}/tests/event/test_controller.cpp + ${PROJECT_SOURCE_DIR}/tests/event/test_event_support.cpp + ${PROJECT_SOURCE_DIR}/tests/event/test_ic_delegate.cpp + ${PROJECT_SOURCE_DIR}/tests/event/test_property.cpp +) + +INCLUDE_DIRECTORIES( + ${SYS_EFL_INCLUDE_DIRS} + ${DPL_TEST_INCLUDE_DIR} +) + +LINK_DIRECTORIES(${SYS_EFL_LIBRARY_DIRS}) + +ADD_EXECUTABLE(${TARGET_NAME} ${DPL_TESTS_SOURCES}) + +TARGET_LINK_LIBRARIES( + ${TARGET_NAME} + ${TARGET_DPL_EFL} + ${TARGET_DPL_EVENT_EFL} + ${TARGET_DPL_TEST_ENGINE_EFL} +) + +SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + BUILD_WITH_INSTALL_RPATH ON + INSTALL_RPATH_USE_LINK_PATH ON +) + +INSTALL(TARGETS ${TARGET_NAME} + DESTINATION bin + PERMISSIONS OWNER_READ + OWNER_WRITE + OWNER_EXECUTE + GROUP_READ + GROUP_EXECUTE + WORLD_READ + WORLD_EXECUTE +) \ No newline at end of file diff --git a/tests/event/main.cpp b/tests/event/main.cpp new file mode 100644 index 0000000..4ed6191 --- /dev/null +++ b/tests/event/main.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file main.cpp + * @author Zbigniew Kostrzewa (z.kostrzewa@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of main. + */ + +#include + +int main(int argc, char *argv[]) +{ + return DPL::Test::TestRunnerSingleton::Instance().ExecTestRunner(argc, argv); +} diff --git a/tests/event/test_controller.cpp b/tests/event/test_controller.cpp new file mode 100644 index 0000000..33bb631 --- /dev/null +++ b/tests/event/test_controller.cpp @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_controller.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of test controller + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +RUNNER_TEST_GROUP_INIT(DPL) + +class IntController + : public DPL::Event::Controller::Type> +{ +private: + int m_value; + +protected: + virtual void OnEventReceived(const int &event) + { + m_value = event; + } + +public: + IntController() + : m_value(-1) + { + } + + int Value() const + { + return m_value; + } +}; + +DECLARE_GENERIC_EVENT_1(DoneSignalEvent, DPL::WaitableEvent *) + +class ThreadController + : public DPL::Event::Controller::Type> +{ +private: + DPL::Thread *m_value; + +protected: + virtual void OnEventReceived(const DoneSignalEvent &event) + { + m_value = DPL::Thread::GetCurrentThread(); + event.GetArg0()->Signal(); + } + +public: + ThreadController() + : m_value(NULL) + { + } + + DPL::Thread *Value() const + { + return m_value; + } +}; + +struct StrangeStruct +{ + int a; + float b; + double c; +}; + +class StrangeController + : public DPL::Event::Controller::Type> +{ +protected: + virtual void OnEventReceived(const char &event) { (void)event; } + virtual void OnEventReceived(const short &event) { (void)event; } + virtual void OnEventReceived(const int &event) { (void)event; } + virtual void OnEventReceived(const long &event) { (void)event; } + virtual void OnEventReceived(const unsigned char &event) { (void)event; } + virtual void OnEventReceived(const unsigned short &event) { (void)event; } + virtual void OnEventReceived(const unsigned int &event) { (void)event; } + virtual void OnEventReceived(const unsigned long &event) { (void)event; } + virtual void OnEventReceived(const float &event) { (void)event; } + virtual void OnEventReceived(const double &event) { (void)event; } + virtual void OnEventReceived(const StrangeStruct &event) { (void)event; } +}; + +RUNNER_TEST(Controller_InitSimple) +{ + IntController controller; + controller.Touch(); + RUNNER_ASSERT(controller.Value() == -1); +} + +RUNNER_TEST(Controller_InitStrange) +{ + StrangeController controller; + controller.Touch(); +} + +RUNNER_TEST(Controller_PostEventToThread) +{ + ThreadController controller; + controller.Touch(); + + DPL::Thread thread; + thread.Run(); + + controller.SwitchToThread(&thread); + + DPL::WaitableEvent waitHandle; + + controller.PostEvent(DoneSignalEvent(&waitHandle)); + + DPL::WaitForSingleHandle(waitHandle.GetHandle()); + + controller.SwitchToThread(NULL); + + RUNNER_ASSERT(controller.Value() == &thread); +} + +RUNNER_TEST(Controller_PostTimedEventToThread) +{ + ThreadController controller; + controller.Touch(); + + DPL::Thread thread; + thread.Run(); + + controller.SwitchToThread(&thread); + + DPL::WaitableEvent waitHandle; + + controller.PostTimedEvent(DoneSignalEvent(&waitHandle), 0.5); + + DPL::WaitForSingleHandle(waitHandle.GetHandle()); + + controller.SwitchToThread(NULL); + + RUNNER_ASSERT(controller.Value() == &thread); +} + +DECLARE_GENERIC_EVENT_2(TouchInThread, DPL::WaitableEvent *, DPL::Thread **) +DECLARE_GENERIC_EVENT_2(TouchedControllerSignal, DPL::WaitableEvent *, DPL::Thread **) + +class TouchInThreadController + : public DPL::Event::Controller::Type>, + private DPL::Event::Controller::Type> +{ +public: + typedef DPL::Event::Controller::Type> PublicController; + typedef DPL::Event::Controller::Type> PrivateController; + + virtual void OnEventReceived(const TouchInThread &event) + { + // Touch controller in thread + PrivateController::Touch(); + + // Post signal + PrivateController::PostEvent(TouchedControllerSignal(event.GetArg0(), event.GetArg1())); + } + + virtual void OnEventReceived(const TouchedControllerSignal &event) + { + // Return touched thread + *event.GetArg1() = DPL::Thread::GetCurrentThread(); + + // Signal waitable event + event.GetArg0()->Signal(); + } +}; + +RUNNER_TEST(Controller_TouchInThread) +{ + TouchInThreadController controller; + controller.PublicController::Touch(); + + DPL::Thread thread; + thread.Run(); + + controller.PublicController::SwitchToThread(&thread); + + DPL::WaitableEvent waitHandle; + DPL::Thread *touchedThread = NULL; + + controller.PublicController::PostEvent(TouchInThread(&waitHandle, &touchedThread)); + + DPL::WaitForSingleHandle(waitHandle.GetHandle()); + + controller.PublicController::SwitchToThread(NULL); + + RUNNER_ASSERT(touchedThread == &thread); +} + +RUNNER_TEST(Controller_SynchronizedEvent) +{ + IntController controller; + controller.Touch(); + + DPL::Thread thread; + thread.Run(); + + controller.SwitchToThread(&thread); + controller.PostSyncEvent(12345); + controller.SwitchToThread(NULL); + + RUNNER_ASSERT(controller.Value() == 12345); +} + +const int ControllersNumber = 5; +const int MaxEventsPerController = 1; +const int MaxEvents = ControllersNumber * MaxEventsPerController; +const int ControllersPerThread = 1; + +class TestController; //Forward Declaration + +typedef DPL::SharedPtr ControllerPtr; +typedef DPL::SharedPtr ThreadPtr; +typedef std::vector ControllerList; +typedef std::list ThreadList; + +DECLARE_GENERIC_EVENT_0(QuitEvent) +class QuitController + : public DPL::Event::Controller::Type>, + public DPL::ApplicationExt +{ +public: + explicit QuitController( ) : DPL::ApplicationExt(1, NULL, "test-app") { Touch(); } +protected: + virtual void OnEventReceived(const QuitEvent &) { Quit(); } +}; + +struct TestContext +{ + ControllerList controllers; + ThreadList threads; + QuitController quitter; + DPL::Atomic g_ReceivedCounter; + DPL::Atomic g_SentCounter; +}; +typedef DPL::ScopedPtr TestContextPtr; +TestContextPtr testContextPtr; + +DECLARE_GENERIC_EVENT_0(StartSendEvent) +DECLARE_GENERIC_EVENT_0(RandomEvent) +class TestController + : public DPL::Event::Controller::Type> +{ +public: + explicit TestController() { Touch(); } +protected: + virtual void OnEventReceived(const RandomEvent &) + { + ++testContextPtr->g_ReceivedCounter; + if(testContextPtr->g_ReceivedCounter == MaxEvents) + { + testContextPtr->quitter.DPL::Event::ControllerEventHandler::PostEvent(QuitEvent()); + return; + } + } + virtual void OnEventReceived(const StartSendEvent &) + { + for (int i=0 ; ig_SentCounter > MaxEvents) + { + return; + } + ++testContextPtr->g_SentCounter; + int id = rand() % static_cast(testContextPtr->controllers.size()); + testContextPtr->controllers.at(id)->DPL::Event::ControllerEventHandler::PostEvent(RandomEvent()); + } + } +}; + +RUNNER_TEST(Controllers_MultipleEvents) +{ + srand ( time(NULL) ); + + testContextPtr.Reset(new TestContext()); + testContextPtr->controllers.reserve(ControllersNumber); + + for (int i = 0; i < ControllersNumber ; ++i) + { + if(testContextPtr->controllers.size() % ControllersPerThread ==0) + { + ThreadPtr thread = ThreadPtr(new DPL::Thread()); + testContextPtr->threads.push_back(thread); + thread->Run(); + } + + ControllerPtr controller = ControllerPtr(new TestController()); + testContextPtr->controllers.push_back(controller); + if(testContextPtr->controllers.size() % 2 == 0) + { + //This controller is being switched to thread (otherwise it is touched to main thread) + ThreadPtr thread = testContextPtr->threads.back(); + controller->SwitchToThread(thread.Get()); + } + controller->DPL::Event::ControllerEventHandler::PostEvent(StartSendEvent()); + } + testContextPtr->quitter.Exec(); + RUNNER_ASSERT(testContextPtr->g_SentCounter == testContextPtr->g_ReceivedCounter); + testContextPtr.Reset(); +} diff --git a/tests/event/test_event_support.cpp b/tests/event/test_event_support.cpp new file mode 100644 index 0000000..af303f0 --- /dev/null +++ b/tests/event/test_event_support.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_event_support.cpp + * @author Piotr Marcinkiewicz (p.marcinkiew@samsung.com) + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains test for event support + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GENERIC_EVENT_0(TestEvent) + +class TestListener: public DPL::Event::EventListener +{ +public: + explicit TestListener() : m_dummyVar(0) { } + void OnEventReceived(const TestEvent &) { m_dummyVar = 1; } + int GetDummyVar() const { return m_dummyVar; } + void ZeroDummyVar() { m_dummyVar = 0; } + +private: + int m_dummyVar; +}; + +class TestEventSupport + : public DPL::Event::EventSupport +{ +public: + void TestEmitEvent() { EmitEvent(TestEvent()); } +}; + +DECLARE_GENERIC_EVENT_0(QuitEvent) + +class QuitController + : public DPL::Event::Controller::Type>, + public DPL::ApplicationExt +{ +public: + QuitController() : DPL::ApplicationExt(1, NULL, "test-app") { Touch(); } + +protected: + virtual void OnEventReceived(const QuitEvent &) { Quit(); } +}; + +RUNNER_TEST(EventSupport_DestroyBeforeProcessing) +{ + QuitController quitter; + quitter.PostTimedEvent(QuitEvent(), 1.0); + + TestListener eventListener; + { + TestEventSupport eventSupport; + eventSupport.AddListener(&eventListener); + eventSupport.TestEmitEvent(); + eventSupport.RemoveListener(&eventListener); + } + eventListener.ZeroDummyVar(); + + quitter.Exec(); + RUNNER_ASSERT(eventListener.GetDummyVar() == 0); +} + +int g_delegateTest; + +void OnDelegateTest(const int &k); + +void OnDelegateTest(const int &k) +{ + LogInfo("Got delegate call"); + g_delegateTest = k; +} + +class DelegateTestSupport + : public DPL::Event::EventSupport +{ +public: + void Test() + { + EmitEvent(7); + } +}; + +RUNNER_TEST(EventSupport_BindDelegate) +{ + g_delegateTest = 0; + + DelegateTestSupport support; + support.AddListener(&OnDelegateTest); + + QuitController quitter; + quitter.PostTimedEvent(QuitEvent(), 1.0); + + support.Test(); + + quitter.Exec(); + + support.RemoveListener(&OnDelegateTest); + + RUNNER_ASSERT(g_delegateTest == 7); +} diff --git a/tests/event/test_ic_delegate.cpp b/tests/event/test_ic_delegate.cpp new file mode 100644 index 0000000..60a5686 --- /dev/null +++ b/tests/event/test_ic_delegate.cpp @@ -0,0 +1,568 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_ic_delegate.cpp + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @author Lukasz Wrzosek (l.wrzosek@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of fast delegate tests. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +RUNNER_TEST_GROUP_INIT(DPL) + +const int IntVal = 123; +const std::string StringVal = "someString"; + +typedef DPL::Event::ICDelegate<> GetNothingDlpType; +typedef DPL::Event::ICDelegate GetIntDlgType; +typedef DPL::Event::ICDelegate GetIntAndStringDlgType; +DECLARE_GENERIC_EVENT_1(GetNothingEvent, GetNothingDlpType) +DECLARE_GENERIC_EVENT_1(GetIntEvent, GetIntDlgType) +DECLARE_GENERIC_EVENT_1(GetIntAndStringEvent, GetIntAndStringDlgType) + +class ICTestController +: public DPL::Event::Controller::Type> +{ + public: + ICTestController() { } + + protected: + virtual void OnEventReceived(const GetNothingEvent& event) + { + event.GetArg0()(); //calling intercontext delegate + } + virtual void OnEventReceived(const GetIntEvent& event) + { + event.GetArg0()(IntVal); //calling intercontext delegate + } + + virtual void OnEventReceived(const GetIntAndStringEvent& event) + { + event.GetArg0()(IntVal, StringVal); //calling intercontext delegate + } +}; + +struct TestResult +{ + TestResult() : + m_correctThread0(false), + m_correctThread1(false), + m_correctThread2(false), + m_int(-1), + m_int2(-1), + m_string("") + { + } + + void TestEventsPassed() + { + RUNNER_ASSERT(m_correctThread0); + RUNNER_ASSERT(m_correctThread1); + RUNNER_ASSERT(m_int == IntVal); + RUNNER_ASSERT(m_correctThread2); + RUNNER_ASSERT(m_int2 == IntVal); + RUNNER_ASSERT(m_string == StringVal); + } + + void TestEventsDidNotPass() + { + RUNNER_ASSERT(!m_correctThread0); + RUNNER_ASSERT(!m_correctThread1); + RUNNER_ASSERT(m_int == -1); + RUNNER_ASSERT(!m_correctThread2); + RUNNER_ASSERT(m_int2 == -1); + RUNNER_ASSERT(m_string == ""); + } + + bool m_correctThread0; + bool m_correctThread1; + bool m_correctThread2; + int m_int; + int m_int2; + std::string m_string; +}; + +class TestContextFreeClass : + protected DPL::Thread, + public DPL::Event::ICDelegateSupport +{ + public: + TestContextFreeClass(ICTestController* controller, TestResult* result) : + Thread(), + m_testResult(result), + m_controller(controller) + { + LogDebug("Context thread id = " << this); + } + + void Run() + { + LogDebug("Running Context Free thread"); + Thread::Run(); + } + + void Quit() + { + LogDebug("Exiting Context Free thread"); + Thread::Quit(); + } + + + void Wait() + { + LogDebug("Waiting for thread"); + DPL::WaitForSingleHandle(m_waitable.GetHandle()); + } + + protected: + void OnNothing() + { + LogDebug("Received nothing in thread = " << GetCurrentThread()); + m_testResult->m_correctThread0 = (GetCurrentThread() == this); + } + + void OnIntReceive(int val) + { + LogDebug("Received int in thread = " << GetCurrentThread()); + m_testResult->m_correctThread1 = (GetCurrentThread() == this); + m_testResult->m_int = val; + } + + void OnIntAndStringReceive(int val, std::string stringval) + { + LogDebug("Received int and string in thread = " << GetCurrentThread()); + m_testResult->m_correctThread2 = (GetCurrentThread() == this); + m_testResult->m_int2 = val; + m_testResult->m_string = stringval; + m_waitable.Signal(); + } + + virtual int ThreadEntry() + { + GetNothingEvent getNothingEvent( + makeICDelegate( + &TestContextFreeClass::OnNothing)); + m_controller->DPL::Event::ControllerEventHandler::PostEvent( + getNothingEvent); + + GetIntEvent getIntEvent( + makeICDelegate( + &TestContextFreeClass::OnIntReceive)); + m_controller->DPL::Event::ControllerEventHandler::PostEvent( + getIntEvent); + + GetIntAndStringEvent getIntAndStringEvent( + makeICDelegate( + &TestContextFreeClass::OnIntAndStringReceive)); + m_controller->DPL::Event::ControllerEventHandler::PostEvent( + getIntAndStringEvent); + + return Thread::ThreadEntry(); + } + + private: + TestResult* m_testResult; + DPL::WaitableEvent m_waitable; + ICTestController* m_controller; +}; + +RUNNER_TEST(ICDelegate_0) +{ + DPL::Thread thread; + thread.Run(); + LogDebug("Controller thread id = " << &thread); + + ICTestController testController; + testController.Touch(); + testController.SwitchToThread(&thread); + + TestResult result; + TestContextFreeClass* contextFree = + new TestContextFreeClass(&testController, &result); + result.TestEventsDidNotPass(); + + thread.Run(); + contextFree->Run(); + contextFree->Wait(); + contextFree->Quit(); + thread.Quit(); + + delete contextFree; + + result.TestEventsPassed(); +} + +RUNNER_TEST(ICDelegate_1) +{ + DPL::Thread thread; + LogDebug("Controller thread id = " << &thread); + + ICTestController testController; + testController.Touch(); + testController.SwitchToThread(&thread); + + TestResult result; + TestContextFreeClass* contextFree = + new TestContextFreeClass(&testController, &result); + result.TestEventsDidNotPass(); + + contextFree->Run(); + contextFree->Quit(); + delete contextFree; //deleting Delegates before actual Events are worked out + thread.Run(); + thread.Quit(); + + result.TestEventsDidNotPass(); +} + +class TestContextFree; +class TestRunnerInThread; + +namespace +{ +const int ControllersPerThread = 40; +const int ContextFreePerThread = 180; +const int TestsPerController = 110; +const int TestThreads = 23; +const int TestsPerThread = 100; +const int NumberOfEvents = 230; + +typedef DPL::SharedPtr ICTestControllerPtr; +typedef DPL::SharedPtr TestContextFreePtr; +typedef DPL::SharedPtr TestRunnerInThreadPtr; +typedef DPL::SharedPtr ThreadPtr; + +DPL::Mutex mutex; +std::list frees; +std::list ctrls; +std::list frees_threads; +std::list ctrls_threads; + +} + +class TestContextFree : public DPL::Event::ICDelegateSupport +{ + public: + TestContextFree(ICTestController* controller, + int eventsCount) : + m_controller(controller), + m_eventsCount(eventsCount) + { + } + + void Wait() + { + LogDebug("Waiting for thread"); + DPL::WaitForSingleHandle(m_waitable.GetHandle()); + } + + + void OnNothing() + { + LogDebug("Got"); + m_eventsCount--; + if (m_eventsCount > 0) { + LogDebug("posting next event"); + GetIntAndStringEvent getIntAndStringEvent( + makeICDelegate( + &TestContextFree::OnIntAndStringReceive)); + LogDebug("posting next event ..."); + m_controller->DPL::Event::ControllerEventHandler::PostEvent( + getIntAndStringEvent); + LogDebug("posting next event done"); + } else { + LogDebug("test finished"); + m_waitable.Signal(); + } + } + + void OnIntReceive(int) + { + LogDebug("Got"); + m_eventsCount--; + if (m_eventsCount > 0) { + LogDebug("posting next event"); + GetNothingEvent getNothingEvent( + makeICDelegate( + &TestContextFree::OnNothing)); + LogDebug("posting next event ..."); + m_controller->DPL::Event::ControllerEventHandler::PostEvent( + getNothingEvent); + LogDebug("posting next event done"); + } else { + LogDebug("test finished"); + m_waitable.Signal(); + } + } + + void OnIntAndStringReceive(int, std::string) + { + LogDebug("Got"); + m_eventsCount--; + if (m_eventsCount > 0) { + LogDebug("posting next event"); + + GetIntEvent getIntEvent( + makeICDelegate( + &TestContextFree::OnIntReceive)); + LogDebug("posting next event ..."); + m_controller->DPL::Event::ControllerEventHandler::PostEvent( + getIntEvent); + LogDebug("posting next event done"); + } else { + LogDebug("test finished"); + m_waitable.Signal(); + } + } + + void StartTestOnNothing() + { + GetNothingEvent getNothingEvent( + makeICDelegate( + &TestContextFree::OnNothing)); + m_controller->DPL::Event::ControllerEventHandler::PostEvent( + getNothingEvent); + } + + void StartTestOnInt() + { + GetIntEvent getIntEvent( + makeICDelegate( + &TestContextFree::OnIntReceive)); + m_controller->DPL::Event::ControllerEventHandler::PostEvent( + getIntEvent); + } + + void StartTestOnIntAndString() + { + GetIntAndStringEvent getIntAndStringEvent( + makeICDelegate( + &TestContextFree::OnIntAndStringReceive)); + m_controller->DPL::Event::ControllerEventHandler::PostEvent( + getIntAndStringEvent); + } + + bool CheckTest() + { + LogDebug("Checking test result"); + return m_eventsCount == 0; + } + + private: + ICTestController* m_controller; + int m_eventsCount; + DPL::WaitableEvent m_waitable; +}; + +class TestRunnerInThread : public DPL::Thread +{ + public: + TestRunnerInThread(int events, int tests) : + m_eventsCount(events), + m_tests(tests) {} + + void WaitForInit() + { + LogDebug("Waiting for thread"); + DPL::WaitForSingleHandle(m_init.GetHandle()); + } + + protected: + virtual int ThreadEntry() + { + LogDebug("Thread starts"); + { + DPL::Mutex::ScopedLock lock(&mutex); + for (int i = 0; i < m_tests; ++i) + { + if (i % TestsPerController == 0) { + if (ctrls.size() % ControllersPerThread == 0) { + ThreadPtr thread(new DPL::Thread()); + thread->Run(); + ctrls_threads.push_back(thread); + } + ICTestControllerPtr ptr(new ICTestController()); + ptr->Touch(); + ptr->SwitchToThread(ctrls_threads.back().Get()); + ctrls.push_back(ptr); + + TestContextFreePtr t(new TestContextFree(ctrls.back().Get(), + m_eventsCount)); + t->StartTestOnNothing(); + LogDebug(""); + frees.push_back(t); + } + } + } + m_init.Signal(); + LogDebug("Thread starts loop"); + return DPL::Thread::ThreadEntry(); + } + + private: + DPL::WaitableEvent m_init; + int m_eventsCount; + int m_tests; +}; + +RUNNER_TEST(ICDelegate_2) +{ + LogDebug("Creating test threads"); + for (int i = 0; i < TestThreads; ++i) + { + TestRunnerInThreadPtr ptr( + new TestRunnerInThread(NumberOfEvents, TestsPerThread)); + frees_threads.push_back(ptr); + frees_threads.back()->Run(); + } + + FOREACH(it, frees_threads) { + (*it)->WaitForInit(); + } + LogDebug("Creating test threads done"); + + FOREACH(it, frees) { + LogDebug("..."); + (*it)->Wait(); + } + + FOREACH(it, frees) { + RUNNER_ASSERT((*it)->CheckTest()); + } + + frees.clear(); + + FOREACH(it, frees_threads) { + (*it)->Quit(); + } + + frees_threads.clear(); + + FOREACH(it, ctrls) { + (*it)->SwitchToThread(NULL); + } + + FOREACH(it, ctrls_threads) { + (*it)->Quit(); + } + + ctrls.clear(); + ctrls_threads.clear(); +} + +namespace ReuseCheck { +const int ReuseCount = 5; +typedef DPL::Event::ICDelegate<> GetNothingDlpType; +DECLARE_GENERIC_EVENT_1(ReuseCountEvent, GetNothingDlpType) + +class ICReuseTestController +: public DPL::Event::Controller::Type> +{ + public: + ICReuseTestController() { m_reuseCount = 0; } + + protected: + virtual void OnEventReceived(const ReuseCountEvent& event) + { + event.GetArg0()(); //calling intercontext delegate + if(++m_reuseCount < ReuseCount){ + LogInfo("[Send] Reuse: " << m_reuseCount); + DPL::Event::ControllerEventHandler::PostEvent(event); + } + } + + int m_reuseCount; +}; + +class ReuseTestContextFreeClass : + protected DPL::Thread, + public DPL::Event::ICDelegateSupport +{ + public: + ReuseTestContextFreeClass(ICReuseTestController* controller) : + Thread(), + m_controller(controller), + m_reuseCount(0) + { } + + void Run() { Thread::Run(); } + void Quit() { Thread::Quit(); } + void Wait() { DPL::WaitForSingleHandle(m_waitable.GetHandle()); } + + protected: + void OnReuseReceive() + { + LogDebug("[Received] : " << ++m_reuseCount); + if(m_reuseCount == ReuseCount) + m_waitable.Signal(); + } + + virtual int ThreadEntry() + { + ReuseCountEvent reuseEvent( + makeICDelegate( + &ReuseTestContextFreeClass::OnReuseReceive, + DPL::Event::ICD::Reuse::Yes)); + m_controller->DPL::Event::ControllerEventHandler::PostEvent( + reuseEvent); + + return Thread::ThreadEntry(); + } + + private: + DPL::WaitableEvent m_waitable; + ICReuseTestController* m_controller; + int m_reuseCount; +}; + +RUNNER_TEST(ICDelegate_3) +{ + DPL::Thread thread; + thread.Run(); + LogDebug("Controller thread id = " << &thread); + + ICReuseTestController testController; + testController.Touch(); + testController.SwitchToThread(&thread); + + ReuseTestContextFreeClass* contextFree = + new ReuseTestContextFreeClass(&testController); + + thread.Run(); + contextFree->Run(); + contextFree->Wait(); + contextFree->Quit(); + thread.Quit(); + + delete contextFree; + + RUNNER_ASSERT(true); +} +} //namespace ReuseCheck diff --git a/tests/event/test_property.cpp b/tests/event/test_property.cpp new file mode 100644 index 0000000..c095e5b --- /dev/null +++ b/tests/event/test_property.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file test_property.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of test property + */ +#include +#include +#include +#include + +namespace { +const int PROPERTY_VALUE_INT = 2; +const std::string PROPERTY_VALUE_STRING = "aaa"; +} + +int ReadSomething2(DPL::Event::Model */*model*/); +int ReadSomething2(DPL::Event::Model */*model*/) +{ + return PROPERTY_VALUE_INT; +} + +std::string ReadSomething(DPL::Event::Model */*model*/); +std::string ReadSomething(DPL::Event::Model */*model*/) +{ + return PROPERTY_VALUE_STRING; +} + +void WriteSomething(const std::string &/*value*/, DPL::Event::Model */*model*/); +void WriteSomething(const std::string &/*value*/, DPL::Event::Model */*model*/) +{ +} + +class MyModel + : public DPL::Event::Model +{ +public: + ~MyModel() {} + + DPL::Event::Property + Caption; + + DPL::Event::Property + Testproperty0; + + DPL::Event::Property + Testproperty1; + + DPL::Event::Property + Testproperty2; + + DPL::Event::Property Testproperty3; + + DPL::Event::Property Testproperty4; + + DPL::Event::Property Testproperty5; + + MyModel() + : Caption(this, "Foo caption"), + Testproperty0(this, "", &ReadSomething), + Testproperty1(this), + Testproperty2(this), + Testproperty3(this), + Testproperty4(this, "test", &ReadSomething, &WriteSomething), + Testproperty5(this, &ReadSomething2) + { + } +}; + +std::string g_caption; + +void OnNameChanged(const DPL::Event::PropertyEvent &event); +void OnNameChanged(const DPL::Event::PropertyEvent &event) +{ + g_caption = event.value; +} + +RUNNER_TEST(Model_Test) +{ + MyModel model; + + g_caption = "It is a bad caption"; + + model.Caption.AddListener(&OnNameChanged); + model.Caption.Set("Test name"); + + RUNNER_ASSERT(model.Testproperty4.Get() == PROPERTY_VALUE_STRING); + RUNNER_ASSERT(PROPERTY_VALUE_INT == model.Testproperty5.Get()); + RUNNER_ASSERT(g_caption == "Test name"); + RUNNER_ASSERT(model.Caption.Get() == "Test name"); + + model.Caption.RemoveListener(&OnNameChanged); +} diff --git a/tests/localization/CMakeLists.txt b/tests/localization/CMakeLists.txt new file mode 100644 index 0000000..47e8229 --- /dev/null +++ b/tests/localization/CMakeLists.txt @@ -0,0 +1,76 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) +# @version 1.0 +# @brief +# + +# +# Test files +# +# Define all DPL tests sources. +# Runner is responsible for runnint it all and +# generating proper output files +# + +SET(TARGET_LOC "dpl-tests-loc") + +SET(MAIN_DIR ${PROJECT_SOURCE_DIR}/tests/localization) + +SET(LOC_TESTS_SOURCES + ${MAIN_DIR}/test_localization.cpp + ${MAIN_DIR}/test_suite01.cpp + ${MAIN_DIR}/mockup_src/widget_dao.cpp + ${PROJECT_SOURCE_DIR}/modules/localization/src/localization_utils.cpp + ${PROJECT_SOURCE_DIR}/modules/localization/src/w3c_file_localization.cpp +) + +INCLUDE_DIRECTORIES( + ${SYS_EFL_INCLUDE_DIRS} + ${DPL_TEST_INCLUDE_DIR} + ${MAIN_DIR}/mockup_include + ${PROJECT_SOURCE_DIR}/modules/localization/include +) + +LINK_DIRECTORIES(${SYS_EFL_LIBRARY_DIRS}) + +ADD_EXECUTABLE(${TARGET_LOC} ${LOC_TESTS_SOURCES}) + +TARGET_LINK_LIBRARIES( + ${TARGET_LOC} + ${SYS_EFL_LIBRARIES} + ${TARGET_DPL_EFL} + ${TARGET_DPL_TEST_ENGINE_EFL} +) + +SET_TARGET_PROPERTIES(${TARGET_LOC} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin/ + BUILD_WITH_INSTALL_RPATH ON + INSTALL_RPATH_USE_LINK_PATH ON +) + +INSTALL(TARGETS ${TARGET_LOC} + DESTINATION bin + PERMISSIONS OWNER_READ + OWNER_WRITE + OWNER_EXECUTE + GROUP_READ + GROUP_EXECUTE + WORLD_READ + WORLD_EXECUTE + ) + +ADD_SUBDIRECTORY(files) diff --git a/tests/localization/files/CMakeLists.txt b/tests/localization/files/CMakeLists.txt new file mode 100644 index 0000000..c887914 --- /dev/null +++ b/tests/localization/files/CMakeLists.txt @@ -0,0 +1,19 @@ +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/tests/localization/files/one + DESTINATION + /opt/apps/widget/tests/localization/widget1/locales/pl-en + ) + +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/tests/localization/files/one + ${PROJECT_SOURCE_DIR}/tests/localization/files/two + DESTINATION + /opt/apps/widget/tests/localization/widget2/locales/pl-en + ) + +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/tests/localization/files/two + DESTINATION + /opt/apps/widget/tests/localization/widget2/locales/en-en + ) + diff --git a/tests/localization/files/one b/tests/localization/files/one new file mode 100644 index 0000000..e69de29 diff --git a/tests/localization/files/two b/tests/localization/files/two new file mode 100644 index 0000000..e69de29 diff --git a/tests/localization/mockup_include/dpl/wrt-dao-ro/common_dao_types.h b/tests/localization/mockup_include/dpl/wrt-dao-ro/common_dao_types.h new file mode 100644 index 0000000..9e49d0d --- /dev/null +++ b/tests/localization/mockup_include/dpl/wrt-dao-ro/common_dao_types.h @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * @file common_dao_types.h + * @author Michal Ciepielski (m.ciepielski@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of common data types for wrtdb + */ + +#ifndef WRT_SRC_CONFIGURATION_COMMON_DAO_TYPES_H_ +#define WRT_SRC_CONFIGURATION_COMMON_DAO_TYPES_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace WrtDB { +namespace Powder { + +typedef std::set StringSet; +//! Widget description +struct Description +{ + //!Content level + typedef enum + { + Level0 = 0, + Level1, + Level2, + Level3, + Level4, + Level5, + LevelUnknown + } LevelEnum; + struct LevelEntry + { + LevelEnum level; //!< content level + + typedef StringSet Context; + + //! POWDER context + //! xa This material appears in an artistic context + //! xb This material appears in an educational context + //! xc This material appears in a medical context + //! xd This material appears in a sports context + //! xe This material appears in a violent context + Context context; + explicit LevelEntry(LevelEnum level = LevelUnknown); + //! Function checks if context is valid + //! \param[in] level POWDER content level + //! \param[in] context POWDER context + bool isContextValid(LevelEnum level, + const DPL::OptionalString& context) const; + }; + + struct CategoryEntry + { + //! Levels entries for POWDER description + typedef std::vector LevelsContainer; + LevelsContainer levels; + //! Function checks if context is valid + //! \param[out] reason set if context invalid + //! \param[in] level POWDER content level + //! \param[in] context POWDER context + bool isCategoryValid(LevelEntry& reason, + LevelEnum level, + const DPL::OptionalString& context) const; + }; + + //! POWDER Category -> Category entry map for Widget + //! + //! nu Nudity + //! se Sex + //! vi Violence + //! la Potentially offensive language + //! dr Drug use + //! ga Gambling + //! ha Hate or harmful activities + //! ug Use of user-generated content + typedef std::map CategoryEntries; + + CategoryEntries categories; + + //! Age rating for widget + //! If Null not set + DPL::OptionalInt ageRating; +}; +} // namespace Powder + +namespace ChildProtection { + +//! Blacklist with forbidden URLs +//! It should be stored in WidgetDAO +typedef std::vector BlackList; + +//! Widget Child protection record +//! Record should be stored in WingetDAO +struct Record +{ + //! Child protection enabled + bool enabled; + explicit Record(bool enabled) : + enabled(enabled) + { + } +}; + +//! Powder processing +struct PowderRules +{ + //! Rule set by parent about forbidden category + //! Powder category + //! nu Nudity + //! se Sex + //! vi Violence + //! la Potentially offensive language + //! dr Drug use + //! ga Gambling + //! ha Hate or harmful activities + //! ug Use of user-generated content + //! Powder context + //! xa This material appears in an artistic conteaxt + //! xb This material appears in an educational context + //! xc This material appears in a medical context + //! xd This material appears in a sports context + //! xe This material appears in a violent context + struct CategoryRule + { + DPL::String category; + Powder::Description::LevelEnum level; + DPL::OptionalString context; + explicit CategoryRule(const DPL::String& category = DPL::String(), + Powder::Description::LevelEnum level = + Powder::Description::LevelUnknown, + const DPL::OptionalString& context = DPL::OptionalString()); + }; + + struct PowderResult + { + //! Reasoning outcome: part of POWDER description used to invalidate + Powder::Description::LevelEntry invalidDescription; + //! Reasoning outcome: rule set by parent not full filed by description + CategoryRule invalidRule; + + //! Reasoning outcome: type of invalidity + enum InvalidReason + { + InvalidRule, //!< One of rules was not fulfilled + InvalidAge, //!< Age is invalid + AgeRatingNotSet, //!< Age rating for widget is not set + Valid //!< Description valid + }; + InvalidReason reason; + explicit PowderResult(InvalidReason reason = Valid, + const Powder::Description::LevelEntry& invalidDescription = + Powder::Description::LevelEntry(), + const CategoryRule& invalidRule = CategoryRule()); + }; + + typedef std::pair ResultPair; + + //! Function checks if rule is fulfilled by description + //! \param[in] rule checked rule + //! \param[in] description + //! \retval true rule is valid + //! \retval false rule is invalid + ResultPair isRuleValidForDescription(const CategoryRule& rule, + const Powder::Description& description) const; + //! Function checks if age limit is fulfilled by description + //! \param[in] description + //! \retval true age is valid + //! \retval false age is invalid + ResultPair isAgeValidForDescription( + const Powder::Description& description) const; + + //! It is the maximum age rating valid for child + //! Uniform age is stored in WidgetDAO + DPL::OptionalInt ageLimit; + + //! Set to true if age rating is required + //! If ageLimit is not set value is ignored + bool isAgeRatingRequired; + + //! Set of rules configured by parent + //! Rules are stored in WidgetDAO and are uniform for all widgets + typedef std::vector RulesContainer; + RulesContainer rules; + + //! Function check if Widget description is valid for ChildProtection + //! configuration + //! \param description widget description + //! \retval true widget is valid + //! \retval false widget is invalid + ResultPair isDescriptionValid(const Powder::Description& description) + const; + + PowderRules() : + isAgeRatingRequired(false) + { + } +}; +} // namespace ChildProtection + +class PluginMetafileData +{ + public: + struct Feature + { + std::string m_name; + std::set m_deviceCapabilities; + + bool operator< (const Feature& obj) const + { + return m_name < obj.m_name; + } + }; + typedef std::set FeatureContainer; + + public: + + PluginMetafileData() + { + } + + std::string m_libraryName; + std::string m_featuresInstallURI; + std::string m_featuresKeyCN; + std::string m_featuresRootCN; + std::string m_featuresRootFingerprint; + + FeatureContainer m_featureContainer; +}; + +class PluginObjectsDAO +{ + public: + typedef std::set Objects; + typedef DPL::SharedPtr ObjectsPtr; + + public: + explicit PluginObjectsDAO() {} + + protected: + ObjectsPtr m_implemented; + ObjectsPtr m_dependent; +}; + +/** + * @brief Widget id describes web-runtime global widget identifier. + * + * Notice that only up to one widget can exist at the same time. + * DbWidgetHandle can be translated into corresponding WidgetModel by invoking + * FindWidgetModel routine. + */ +typedef int DbWidgetHandle; + +/** + * @brief Structure to hold the information of widget's size + */ +struct DbWidgetSize +{ + DPL::OptionalInt width; + DPL::OptionalInt height; + + DbWidgetSize(DPL::OptionalInt w = DPL::OptionalInt::Null, + DPL::OptionalInt h = DPL::OptionalInt::Null) : + width(w), + height(h) + { + } +}; + +inline bool operator ==(const DbWidgetSize &objA, const DbWidgetSize &objB) +{ + if (!objA.height || !objA.width || !objB.width || !objB.height) { + return false; + } else { + return *objA.height == *objB.height && *objA.width == *objB.width; + } +} + +/** + * Widget [G]lobal [U]nique [ID]entifier + * Orginated from appstore ID + */ +typedef DPL::OptionalString WidgetGUID; + +struct WidgetAccessInfo +{ + DPL::String strIRI; /* origin iri */ + bool bSubDomains; /* do we want access to subdomains ? */ + + bool operator ==(const WidgetAccessInfo& info) const + { + return info.strIRI == strIRI && + info.bSubDomains == bSubDomains; + } +}; + +typedef std::list WidgetAccessInfoList; + +typedef std::list WindowModeList; + +/** + * @brief Widget configuration parameter key + */ +typedef DPL::String WidgetParamKey; + +/** + * @brief Widget configuration parameter value + */ +typedef DPL::String WidgetParamValue; + +/** + * @brief A map of widget configuration parameters. + * + * Widget configuration parameters are read from database and are stored + * along with feature that they describe. + */ +typedef std::multimap WidgetParamMap; + +/** + * @brief Widget feature host information about possible javascript extensions + * that widget may use + * + * Widget features are declared in configuration file in widget installation + * package. Each declared special feature is contained in some wrt-plugin that + * declares to implement it. After widget launch wrt searches for proper plugin + * libraries and load needed features. + * + * Widget features can be required or optional. It is possible to start widget + * without missing feature. When required feature cannot be loaded widget will + * not start. + */ + +enum { + INVALID_PLUGIN_HANDLE = -1 +}; +typedef int DbPluginHandle; + +struct DbWidgetFeature +{ + DPL::String name; /// Feature name + bool required; /// Whether feature is required + DbPluginHandle pluginId; /// Plugin id that implement this feature + WidgetParamMap params; /// Widget's params + + DbWidgetFeature() : + required(false), + pluginId(INVALID_PLUGIN_HANDLE) + { + } +}; + +inline bool operator < (const DbWidgetFeature &objA, + const DbWidgetFeature &objB) +{ + return objA.name.compare(objB.name) < 0; +} + +inline bool operator==(const DbWidgetFeature &featureA, + const DbWidgetFeature &featureB) +{ + return featureA.name == featureB.name && + featureA.required == featureB.required && + featureA.pluginId == featureB.pluginId; +} + +/** + * @brief Default container for features list + */ +typedef std::multiset DbWidgetFeatureSet; + +/** + * @brief Default container with DbWidgetHandle's + */ +typedef std::list DbWidgetHandleList; + +/** + * @brief Widget specific type + * + * Widget type describes belowed in WAC, TIZEN WebApp + */ +enum AppType +{ + APP_TYPE_UNKNOWN = 0, // unknown + APP_TYPE_WAC10, // WAC 1.0 + APP_TYPE_WAC20, // WAC 2.0 + APP_TYPE_TIZENWEBAPP, // slp webapp +}; + +class WidgetType +{ + public: + WidgetType() + :appType(APP_TYPE_UNKNOWN) + { + } + WidgetType(const AppType type) + :appType(type) + { + } + bool operator== (const AppType& other) const + { + return appType == other; + } + std::string getApptypeToString() + { + switch (appType) { +#define X(x) case x: return #x; + X(APP_TYPE_UNKNOWN) + X(APP_TYPE_WAC10) + X(APP_TYPE_WAC20) + X(APP_TYPE_TIZENWEBAPP) +#undef X + default: + return "UNKNOWN"; + } + } + + AppType appType; +}; + +} // namespace WrtDB + +struct WidgetSetting +{ + DPL::String settingName; + DPL::String settingValue; + + bool operator ==(const WidgetSetting& info) const + { + return (info.settingName == settingName && + info.settingValue == settingValue); + } + bool operator !=(const WidgetSetting& info) const + { + return (info.settingName != settingName || + info.settingValue != settingValue); + } +}; + +typedef std::list WidgetSettings; + +/** + * @brief Widget Application Service + * + * Application sercvice describes details of behaviour + * when widget receives aul bundle data. + */ +struct WidgetApplicationService +{ + public: + DPL::String src; /* start uri */ + DPL::String operation; /* service name */ + DPL::String scheme; /* scheme type*/ + DPL::String mime; /* mime type */ + + bool operator== (const WidgetApplicationService& other) const + { + return src == other.src && + operation == other.operation && + scheme == other.scheme && + mime == other.mime; + } +}; + +typedef std::list WidgetApplicationServiceList; +#endif /* WRT_SRC_CONFIGURATION_COMMON_DAO_TYPES_H_ */ diff --git a/tests/localization/mockup_include/dpl/wrt-dao-rw/widget_dao.h b/tests/localization/mockup_include/dpl/wrt-dao-rw/widget_dao.h new file mode 100644 index 0000000..42b6c5b --- /dev/null +++ b/tests/localization/mockup_include/dpl/wrt-dao-rw/widget_dao.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file contains the declaration of widget dao class. + * + * @file widget_dao_read_only.h + * @author Yang Jie (jie2.yang@samsung.com) + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of widget dao + */ + +#ifndef _WRT_SRC_CONFIGURATION_WIDGET_DAO_READ_ONLY_H_ +#define _WRT_SRC_CONFIGURATION_WIDGET_DAO_READ_ONLY_H_ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +typedef DPL::OptionalString WidgetGUID; + +namespace ConfigParserData { + +struct Icon +{ + Icon(const DPL::String& src) : src(src) + { + } + DPL::String src; + DPL::OptionalInt width; + DPL::OptionalInt height; + bool operator==(const Icon&) const; + bool operator!=(const Icon&) const; + bool operator >(const Icon&) const; + bool operator>=(const Icon&) const; + bool operator <(const Icon&) const; + bool operator<=(const Icon&) const; +}; +} // namespace ConfigParserData +namespace WrtDB { + +typedef std::list StringList; + +struct WidgetLocalizedInfo +{ + DPL::OptionalString name; + DPL::OptionalString shortName; + DPL::OptionalString description; + DPL::OptionalString license; + DPL::OptionalString licenseHref; +}; + +typedef std::list LanguageTagList; + +class WidgetDAO +{ + public: + /** + * WidgetDAO Exception classes + */ + class Exception + { + public: + DECLARE_EXCEPTION_TYPE(DPL::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, DatabaseError) + DECLARE_EXCEPTION_TYPE(Base, ReadOnlyProperty) + DECLARE_EXCEPTION_TYPE(Base, GUIDisNull) + DECLARE_EXCEPTION_TYPE(Base, UnexpectedEmptyResult) + DECLARE_EXCEPTION_TYPE(Base, WidgetNotExist) + DECLARE_EXCEPTION_TYPE(Base, AlreadyRegistered) + }; + + protected: + DbWidgetHandle m_widgetHandle; + + public: + struct WidgetLocalizedIconRow + { + int appId; + int iconId; + DPL::String widgetLocale; + }; + typedef std::list WidgetLocalizedIconList; + + struct WidgetIconRow + { + int iconId; + int appId; + DPL::String iconSrc; + DPL::OptionalInt iconWidth; + DPL::OptionalInt iconHeight; + }; + typedef std::list WidgetIconList; + + struct WidgetStartFileRow + { + int startFileId; + int appId; + DPL::String src; + }; + typedef std::list WidgetStartFileList; + + struct WidgetLocalizedStartFileRow + { + int startFileId; + int appId; + DPL::String widgetLocale; + DPL::String type; + DPL::String encoding; + }; + typedef std::list LocalizedStartFileList; + + + /** + * This is a constructor. + * + * @param[in] widgetHandle application id of widget. + */ + WidgetDAO(DbWidgetHandle widgetHandle) + : m_widgetHandle(widgetHandle) + {} + + /** + * Destructor + */ + virtual ~WidgetDAO(){} + + /** + * This method returns widget handle(m_widgetHandle). + * + * @return widget handle(m_widgetHandle). + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in DB table. + */ + DbWidgetHandle getHandle() const { return m_widgetHandle; } + DbWidgetHandle getHandle(const WidgetGUID GUID) const; + static DbWidgetHandle getHandle(const DPL::String pkgName); + + /** + * This method returns the root directory of widget resource. + * + * @return path name of root directory. + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + DPL::String getPath() const; + void setPath(const DPL::String &path) const; + + /** + * This method returns the defaultlocale for the widget. + * + * @return defaultlocale + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + DPL::OptionalString getDefaultlocale() const; + + /** + * This method returns list of localized icons files; + * + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + WidgetLocalizedIconList getLocalizedIconList() const; + + /** + * This method returns list of icons files; + * + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + WidgetIconList getIconList() const; + + /** + * This method returns list of localized start files; + * + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + LocalizedStartFileList getLocalizedStartFileList() const; + void setLocalizedStartFileList(const LocalizedStartFileList &list) const; + /** + * This method returns list of start files; + * + * @exception WRT_CONF_ERR_EMDB_FAILURE - Fail to query DB table. + * @exception WRT_CONF_ERR_EMDB_NO_RECORD - Can not find matching records in + * DB table. + */ + WidgetStartFileList getStartFileList() const; + void setStartFileList(const WidgetStartFileList &list) const; + + WidgetLocalizedInfo getLocalizedInfo(const DPL::String& languageTag) const; + protected: + static std::map s_startFileMap; + static std::map s_localizedStartFileMap; + static std::map s_pathMap; +}; + +} // namespace WrtDB + +#endif // _WRT_SRC_CONFIGURATION_WIDGET_DAO_READ_ONLY_H_ + diff --git a/tests/localization/mockup_src/widget_dao.cpp b/tests/localization/mockup_src/widget_dao.cpp new file mode 100644 index 0000000..3fe877b --- /dev/null +++ b/tests/localization/mockup_src/widget_dao.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This file contains the declaration of widget dao class. + * + * @file widget_dao_read_only.cpp + * @author Yang Jie (jie2.yang@samsung.com) + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @author Pawel Sikorski (p.sikorski@samsung.com) + * @version 1.0 + * @brief This file contains the declaration of widget dao + */ + +#include + +#include +#include +#include +#include + +namespace WrtDB { + +std::map WidgetDAO::s_startFileMap; +std::map WidgetDAO::s_localizedStartFileMap; +std::map WidgetDAO::s_pathMap; + +DbWidgetHandle WidgetDAO::getHandle(const WidgetGUID /* GUID */) const +{ + LogError("Not impleneted!"); + return 0; +} + +DbWidgetHandle WidgetDAO::getHandle(const DPL::String /* pkgName */) +{ + LogError("Not implemented!"); + return 0; +} + +DPL::String WidgetDAO::getPath() const +{ + return s_pathMap[m_widgetHandle]; +} + +void WidgetDAO::setPath(const DPL::String &path) const +{ + s_pathMap[m_widgetHandle] = path; +} + +WidgetLocalizedInfo + WidgetDAO::getLocalizedInfo(const DPL::String& /* languageTag */) + const +{ + LogError("Not implemented!"); + return WidgetLocalizedInfo(); +} + +DPL::OptionalString WidgetDAO::getDefaultlocale() const +{ + LogError("Not implemented!"); + return DPL::OptionalString(); +} + +WidgetDAO::WidgetLocalizedIconList WidgetDAO::getLocalizedIconList() const +{ + LogError("Not implemented!"); + return WidgetLocalizedIconList(); +} + +WidgetDAO::WidgetIconList WidgetDAO::getIconList() const +{ + LogError("Not implemented!"); + return WidgetIconList(); +} + +WidgetDAO::LocalizedStartFileList WidgetDAO::getLocalizedStartFileList() const +{ + return s_localizedStartFileMap[m_widgetHandle]; +} + +void WidgetDAO::setLocalizedStartFileList(const LocalizedStartFileList &list) const { + s_localizedStartFileMap[m_widgetHandle] = list; +} + +WidgetDAO::WidgetStartFileList WidgetDAO::getStartFileList() const +{ + return s_startFileMap[m_widgetHandle]; +} + +void WidgetDAO::setStartFileList(const WidgetStartFileList &list) const +{ + s_startFileMap[m_widgetHandle] = list; +} + +} // namespace WrtDB diff --git a/tests/localization/test_localization.cpp b/tests/localization/test_localization.cpp new file mode 100644 index 0000000..42ffe3a --- /dev/null +++ b/tests/localization/test_localization.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file main.cpp + * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of main + */ +#include + +int main(int argc, char *argv[]) +{ + return DPL::Test::TestRunnerSingleton::Instance().ExecTestRunner(argc, argv); +} + diff --git a/tests/localization/test_suite01.cpp b/tests/localization/test_suite01.cpp new file mode 100644 index 0000000..83f53f5 --- /dev/null +++ b/tests/localization/test_suite01.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +namespace { + +WrtDB::LanguageTagList generateLanguageTags(){ + WrtDB::LanguageTagList tags; + tags.push_back(L"pl-pl"); + tags.push_back(L"en-en"); + tags.push_back(L"pl-en"); + return tags; +} + +static const WrtDB::LanguageTagList languageTags = generateLanguageTags(); +static const DPL::String widget1Path = L"/opt/apps/widget/tests/localization/widget1/"; +static const DPL::String widget2Path = L"/opt/apps/widget/tests/localization/widget2/"; + +} // anonymous namespace + +RUNNER_TEST(test01_getFilePathInWidgetPackageFromUrl){ + const int widgetHandle = 1; + WrtDB::WidgetDAO dao(widgetHandle); + dao.setPath(widget1Path); + + auto result = W3CFileLocalization::getFilePathInWidgetPackageFromUrl( + widgetHandle, + languageTags, + L"widget://one"); + + RUNNER_ASSERT(*result == L"/opt/apps/widget/tests/localization/widget1/locales/pl-en/one"); +} + +RUNNER_TEST(test02_getFilePathInWidgetPackageFromUrl){ + const int widgetHandle = 2; + WrtDB::WidgetDAO dao(widgetHandle); + dao.setPath(widget2Path); + + auto result = W3CFileLocalization::getFilePathInWidgetPackageFromUrl( + widgetHandle, + languageTags, + L"widget://one"); + + RUNNER_ASSERT(*result == L"/opt/apps/widget/tests/localization/widget2/locales/pl-en/one"); +} + +RUNNER_TEST(test03_getFilePathInWidgetPackageFromUrl){ + const int widgetHandle = 2; + WrtDB::WidgetDAO dao(widgetHandle); + dao.setPath(widget2Path); + + auto result = W3CFileLocalization::getFilePathInWidgetPackageFromUrl( + widgetHandle, + languageTags, + L"widget://two"); + + RUNNER_ASSERT(*result == L"/opt/apps/widget/tests/localization/widget2/locales/en-en/two"); +} + +RUNNER_TEST(test04_getFilePathInWidgetPackage){ + const int widgetHandle = 1; + WrtDB::WidgetDAO dao(widgetHandle); + dao.setPath(widget1Path); + + auto result = W3CFileLocalization::getFilePathInWidgetPackage( + widgetHandle, + languageTags, + L"one"); + + RUNNER_ASSERT(*result == L"locales/pl-en/one"); +} + +RUNNER_TEST(test05_getFilePathInWidgetPackage){ + const int widgetHandle = 2; + WrtDB::WidgetDAO dao(widgetHandle); + dao.setPath(widget2Path); + + auto result = W3CFileLocalization::getFilePathInWidgetPackage( + widgetHandle, + languageTags, + L"two"); + + RUNNER_ASSERT(*result == L"locales/en-en/two"); +} + diff --git a/tests/vcore/CMakeLists.txt b/tests/vcore/CMakeLists.txt new file mode 100644 index 0000000..443438d --- /dev/null +++ b/tests/vcore/CMakeLists.txt @@ -0,0 +1,129 @@ +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com) +# @author Pawel Sikorski (p.sikorski@samsung.com) +# @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) +# @version 1.0 +# @brief +# +INCLUDE(FindPkgConfig) +SET(TARGET_VCORE_TEST "dpl-tests-vcore") + +PKG_CHECK_MODULES(VCORE_TEST_DEP + libsoup-2.4 + REQUIRED + ) + +SET(VCORE_TESTS_SOURCES + ${PROJECT_SOURCE_DIR}/tests/vcore/vcore_tests.cpp + ${PROJECT_SOURCE_DIR}/tests/vcore/TestCases.cpp + ${PROJECT_SOURCE_DIR}/tests/vcore/TestEnv.cpp + ${PROJECT_SOURCE_DIR}/tests/vcore/TestCRL.cpp + ) + +INCLUDE_DIRECTORIES( + ${SYS_EFL_INCLUDE_DIRS} + ${DPL_TEST_INCLUDE_DIR} + ${PROJECT_SOURCE_DIR}/modules/vcore/src + ${PROJECT_SOURCE_DIR}/tests/vcore + ${PROJECT_SOURCE_DIR}/modules/widget_dao/include # global_config.h + ${VCORE_TEST_DEP_INCLUDE_DIRS} + ) + +ADD_EXECUTABLE(${TARGET_VCORE_TEST} ${VCORE_TESTS_SOURCES}) + +TARGET_LINK_LIBRARIES(${TARGET_VCORE_TEST} + ${SYS_EFL_LIBRARIES} + ${TARGET_DPL_EFL} + ${TARGET_DPL_TEST_ENGINE_EFL} + ${TARGET_VCORE_LIB} + ${TARGET_WRT_DAO_RO_LIB} # global_config.h_ + ${VCORE_TEST_DEP_LIBRARIES} + ) + +INSTALL(TARGETS ${TARGET_VCORE_TEST} + DESTINATION bin + PERMISSIONS OWNER_READ + OWNER_WRITE + OWNER_EXECUTE + GROUP_READ + GROUP_EXECUTE + WORLD_READ + WORLD_EXECUTE + ) + +INSTALL(FILES ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/dpl-tests-vcore-ocsp-server.sh + DESTINATION bin + PERMISSIONS OWNER_READ + OWNER_WRITE + OWNER_EXECUTE + GROUP_READ + GROUP_EXECUTE + WORLD_READ + WORLD_EXECUTE + ) + +ADD_CUSTOM_COMMAND(TARGET ${TARGET_VCORE_TEST} POST_BUILD + COMMAND ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/create_certs.sh + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/ + COMMENT "Generate certificate chains" + ) + +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/tests/vcore/test-cases/widget/author-signature.xml + ${PROJECT_SOURCE_DIR}/tests/vcore/test-cases/widget/signature1.xml + ${PROJECT_SOURCE_DIR}/tests/vcore/test-cases/widget/signature22.xml + ${PROJECT_SOURCE_DIR}/tests/vcore/test-cases/widget/config.xml + ${PROJECT_SOURCE_DIR}/tests/vcore/test-cases/widget/index.html + DESTINATION + /opt/apps/widget/tests/vcore_widget_uncompressed + ) + +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/tests/vcore/test-cases/keys/ocsp_level0deprecated.crt + ${PROJECT_SOURCE_DIR}/tests/vcore/test-cases/keys/ocsp_level1.crt + ${PROJECT_SOURCE_DIR}/tests/vcore/test-cases/keys/ocsp_level2.crt + ${PROJECT_SOURCE_DIR}/tests/vcore/test-cases/keys/ocsp_rootca.crt + ${PROJECT_SOURCE_DIR}/tests/vcore/test-cases/keys/operator.root.cert.pem + ${PROJECT_SOURCE_DIR}/tests/vcore/test-cases/keys/root_cacert.pem + ${PROJECT_SOURCE_DIR}/tests/vcore/test-cases/keys/CAbundle.crt + DESTINATION + /opt/apps/widget/tests/vcore_keys + ) + +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/demoCA/cacert.pem + ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/1second_level.pem + ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/1third_level.pem + ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/2second_level.pem + ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/2third_level.pem + ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/3second_level.pem + ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/3third_level.pem + ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/cacrl1.pem + ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/cacrl2.pem + ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/respcert.pem + ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/respcert.key + ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/openssl.cnf + DESTINATION + /opt/apps/widget/tests/vcore_certs/ +) + +INSTALL(DIRECTORY + ${PROJECT_SOURCE_DIR}/tests/vcore/certificate-generator/demoCA + DESTINATION + /opt/apps/widget/tests/vcore_certs/ +) + diff --git a/tests/vcore/TestCRL.cpp b/tests/vcore/TestCRL.cpp new file mode 100644 index 0000000..37998df --- /dev/null +++ b/tests/vcore/TestCRL.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include "TestCRL.h" + +using namespace ValidationCore; +using namespace std; + + +namespace { +const char *CRL_LOOKUP_DIR = "/opt/etc/ssl/certs/"; +const char *beginCertificate = "-----BEGIN CERTIFICATE-----"; +const char *endCertificate = "-----END CERTIFICATE-----"; +const char *beginTrustedCertificate = "-----BEGIN TRUSTED CERTIFICATE-----"; +const char *endTrustedCertificate = "-----END TRUSTED CERTIFICATE-----"; + + +bool whiteCharacter(char a){ + return a == '\n'; +} + +} + +TestCRL::TestCRL() +{ + //Add additional lookup dir + int rv = X509_LOOKUP_add_dir(m_lookup, CRL_LOOKUP_DIR, X509_FILETYPE_PEM); + if (!rv) { + LogError("Failed to add lookup dir for PEM files."); + ThrowMsg(CRLException::StorageError, + "Failed to add lookup dir for PEM files."); + } + LogInfo("CRL storage initialization complete."); +} + +std::string TestCRL::getFileContent(const std::string &filename) +{ + //Only PEM formatted files allowed + LogInfo("Read file: " << filename); + DPL::FileInputMapping file(filename); + string content(reinterpret_cast(file.GetAddress()), + file.GetSize()); + + size_t posBegin = content.find(beginCertificate); + size_t posEnd = content.find(endCertificate); + if (posBegin != string::npos && + posEnd != string::npos) { + posBegin += strlen(beginCertificate); + } else { + posBegin = content.find(beginTrustedCertificate); + posEnd = content.find(endTrustedCertificate); + if (posBegin != string::npos && + posEnd != string::npos) { + posBegin += strlen(beginTrustedCertificate); + } else { + LogError("Failed to parse PEM file"); + return string(); + } + } + //Remove whitespaces + string cert(content, posBegin, posEnd - posBegin); + cert.erase(std::remove_if(cert.begin(), cert.end(), whiteCharacter), + cert.end()); + + return cert; +} + +void TestCRL::addCRLToStore(const string &filename, const string &uri) +{ + LogInfo("Read file: " << filename); + //Only PEM formatted files allowed + DPL::FileInputMapping file(filename); + char *buffer = new char[file.GetSize()]; + memcpy(buffer, file.GetAddress(), file.GetSize()); + CRLDataPtr crl(new CRLData(buffer, file.GetSize(), uri)); + updateCRL(crl); +} diff --git a/tests/vcore/TestCRL.h b/tests/vcore/TestCRL.h new file mode 100644 index 0000000..d6c1ee6 --- /dev/null +++ b/tests/vcore/TestCRL.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _TEST_CRL_H +#define _TEST_CRL_H + +#include +#include + +class TestCRL : public ValidationCore::CRL +{ + public: + TestCRL(); + + void addCRLToStore(const std::string &filename, const std::string &uri); + + //convinient function + std::string getFileContent(const std::string &filename); +}; + +#endif diff --git a/tests/vcore/TestCases.cpp b/tests/vcore/TestCases.cpp new file mode 100644 index 0000000..8fdd4e7 --- /dev/null +++ b/tests/vcore/TestCases.cpp @@ -0,0 +1,1325 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "TestEnv.h" +#include +#include +#include +#include +#include +#include +#include "TestCRL.h" +#include + +namespace { + +const std::string widget_path = + "/opt/apps/widget/tests/vcore_widget_uncompressed/"; +const std::string keys_path = "/opt/apps/widget/tests/vcore_keys/"; +const std::string widget_store_path = "/opt/apps/widget/tests/vcore_widgets/"; +const std::string cert_store_path = "/opt/apps/widget/tests/vcore_certs/"; +const std::string crl_URI = "http://localhost/my.crl"; + +const std::string anka_ec_key_type = "urn:oid:1.2.840.10045.3.1.7"; +const std::string anka_ec_public_key = + "BGi9RmTUjpqCpQjx6SSiKdfmtjQBFNSN7ghm6TuaH9r4x73WddeLxLioH3VEmFLC+QLiR"\ + "kPxDxL/6YmQdgfGrqk="; + +const std::string rsa_modulus = + "ocwjKEFaPxLNcPTz2PtT2Gyu5jzkWaPo4thjZo3rXuNbD4TzjY02UGnTxvflNeORLpSS1"\ + "PeYr/1E/Nhr7qQAzj9g0DwW7p8zQEdOUi3v76VykeB0pFJH+0Fxp6LVBX9Z+EvZk+dbOy"\ + "GJ4Njm9B6M09axXlV11Anj9B/HYUDfDX8="; +const std::string rsa_exponent = "AQAB"; + +const std::string magda_dsa_p = + "2BYIQj0ePUVxzrdBT41eCblraa9Dqag7QXFMCRM2PtyS22JPDKuV77tBc/jg0V3htHWdR"\ + "q9n6/kQDwrP7FIPoLATLIiC3oAYWj46Mr6d9k/tt/JZU6PvULmB2k1wrrmvKUi+U+I5Ro"\ + "qe8ui8lqR9pp9u2WCh2QmFfCohKNjN5qs="; +const std::string magda_dsa_q = "4p4JcDqz+S7CbWyd8txApZw0sik="; +const std::string magda_dsa_g = + "AQrLND1ZGFvzwBpPPXplmPh1ijPx1O2gQEvPvyjR88guWcGqQc0m7dTb6PEvbI/oZ0o91"\ + "k7VEkfthURnNR1WtOLT8dmAuKQfwTQLPwCwUM/QiuWSlCyKLTE4Ev8aOG7ZqWudsKm/td"\ + "n9pUNGtcod1wo1ZtP7PfEJ6rYZGQDOlz8="; + +const std::string googleCA = +"MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG" +"A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz" +"cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2" +"MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV" +"BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt" +"YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN" +"ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE" +"BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is" +"I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G" +"CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do" +"lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc" +"AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k"; + +const std::string google2nd = +"MIIDIzCCAoygAwIBAgIEMAAAAjANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJV" +"UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsTLkNsYXNzIDMgUHVi" +"bGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNTEzMDAw" +"MDAwWhcNMTQwNTEyMjM1OTU5WjBMMQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhh" +"d3RlIENvbnN1bHRpbmcgKFB0eSkgTHRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBD" +"QTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1NNn0I0Vf67NMf59HZGhPwtx" +"PKzMyGT7Y/wySweUvW+Aui/hBJPAM/wJMyPpC3QrccQDxtLN4i/1CWPN/0ilAL/g" +"5/OIty0y3pg25gqtAHvEZEo7hHUD8nCSfQ5i9SGraTaEMXWQ+L/HbIgbBpV8yeWo" +"3nWhLHpo39XKHIdYYBkCAwEAAaOB/jCB+zASBgNVHRMBAf8ECDAGAQH/AgEAMAsG" +"A1UdDwQEAwIBBjARBglghkgBhvhCAQEEBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAX" +"BgNVBAMTEFByaXZhdGVMYWJlbDMtMTUwMQYDVR0fBCowKDAmoCSgIoYgaHR0cDov" +"L2NybC52ZXJpc2lnbi5jb20vcGNhMy5jcmwwMgYIKwYBBQUHAQEEJjAkMCIGCCsG" +"AQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMDQGA1UdJQQtMCsGCCsGAQUF" +"BwMBBggrBgEFBQcDAgYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEB" +"BQUAA4GBAFWsY+reod3SkF+fC852vhNRj5PZBSvIG3dLrWlQoe7e3P3bB+noOZTc" +"q3J5Lwa/q4FwxKjt6lM07e8eU9kGx1Yr0Vz00YqOtCuxN5BICEIlxT6Ky3/rbwTR" +"bcV0oveifHtgPHfNDs5IAn8BL7abN+AqKjbc1YXWrOU/VG+WHgWv"; + +const std::string google3rd = +"MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM" +"MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg" +"THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x" +"MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh" +"MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw" +"FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC" +"gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN" +"gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L" +"05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM" +"BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl" +"LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF" +"BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw" +"Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0" +"ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF" +"AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5" +"u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6" +"z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw=="; + +const std::string certVerisign = +"MIIG+DCCBeCgAwIBAgIQU9K++SSnJF6DygHkbKokdzANBgkqhkiG9w0BAQUFADCB" +"vjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL" +"ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2Ug" +"YXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykwNjE4MDYGA1UEAxMv" +"VmVyaVNpZ24gQ2xhc3MgMyBFeHRlbmRlZCBWYWxpZGF0aW9uIFNTTCBTR0MgQ0Ew" +"HhcNMTAwNTI2MDAwMDAwWhcNMTIwNTI1MjM1OTU5WjCCASkxEzARBgsrBgEEAYI3" +"PAIBAxMCVVMxGTAXBgsrBgEEAYI3PAIBAhMIRGVsYXdhcmUxGzAZBgNVBA8TElYx" +"LjAsIENsYXVzZSA1LihiKTEQMA4GA1UEBRMHMjQ5Nzg4NjELMAkGA1UEBhMCVVMx" +"DjAMBgNVBBEUBTk0MDQzMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHFA1N" +"b3VudGFpbiBWaWV3MSIwIAYDVQQJFBk0ODcgRWFzdCBNaWRkbGVmaWVsZCBSb2Fk" +"MRcwFQYDVQQKFA5WZXJpU2lnbiwgSW5jLjEmMCQGA1UECxQdIFByb2R1Y3Rpb24g" +"U2VjdXJpdHkgU2VydmljZXMxGTAXBgNVBAMUEHd3dy52ZXJpc2lnbi5jb20wggEi" +"MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCj+PvvK+fZOXwno0yT/OTy2Zm9" +"ehnZjTtO/X2IWBEa3jG30C52uHFQI4NmXiQVNvJHkBaAj0ilVjvGdxXmkyyFsugt" +"IWOTZ8pSKdX1tmGFIon6Ko9+lBFkVkudA1ogAUbtTB8IcdeOlpK78T4SjdVMhY18" +"150YzSw6hRKlw52wBaDxtGZElvOth41K7TUcaDnQVzz5SBPW5MUhi7AWrdoSk17O" +"BozOzmB/jkYDVDnwLcbR89SLHEOle/idSYSDQUmab3y0JS8RyQV1+DB70mnFALnD" +"fLiL47nMQQCGxXgp5voQ2YmSXhevKmEJ9vvtC6C7yv2W6yomfS/weUEce9pvAgMB" +"AAGjggKCMIICfjCBiwYDVR0RBIGDMIGAghB3d3cudmVyaXNpZ24uY29tggx2ZXJp" +"c2lnbi5jb22CEHd3dy52ZXJpc2lnbi5uZXSCDHZlcmlzaWduLm5ldIIRd3d3LnZl" +"cmlzaWduLm1vYmmCDXZlcmlzaWduLm1vYmmCD3d3dy52ZXJpc2lnbi5ldYILdmVy" +"aXNpZ24uZXUwCQYDVR0TBAIwADAdBgNVHQ4EFgQU8oBwK/WBXCZDWi0dbuDgPyTK" +"iJIwCwYDVR0PBAQDAgWgMD4GA1UdHwQ3MDUwM6AxoC+GLWh0dHA6Ly9FVkludGwt" +"Y3JsLnZlcmlzaWduLmNvbS9FVkludGwyMDA2LmNybDBEBgNVHSAEPTA7MDkGC2CG" +"SAGG+EUBBxcGMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LnZlcmlzaWduLmNv" +"bS9ycGEwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUFBwMCBglghkgBhvhCBAEw" +"HwYDVR0jBBgwFoAUTkPIHXbvN1N6T/JYb5TzOOLVvd8wdgYIKwYBBQUHAQEEajBo" +"MCsGCCsGAQUFBzABhh9odHRwOi8vRVZJbnRsLW9jc3AudmVyaXNpZ24uY29tMDkG" +"CCsGAQUFBzAChi1odHRwOi8vRVZJbnRsLWFpYS52ZXJpc2lnbi5jb20vRVZJbnRs" +"MjAwNi5jZXIwbgYIKwYBBQUHAQwEYjBgoV6gXDBaMFgwVhYJaW1hZ2UvZ2lmMCEw" +"HzAHBgUrDgMCGgQUS2u5KJYGDLvQUjibKaxLB4shBRgwJhYkaHR0cDovL2xvZ28u" +"dmVyaXNpZ24uY29tL3ZzbG9nbzEuZ2lmMA0GCSqGSIb3DQEBBQUAA4IBAQB9VZxB" +"wDMRGyhFWYkY5rwUVGuDJiGeas2xRJC0G4+riQ7IN7pz2a2BhktmZ5HbxXL4ZEY4" +"yMN68DEVErhtKiuL02ng27alhlngadKQzSL8pLdmQ+3jEwm9nva5C/7pbeqy+qGF" +"is4IWNYOc4HKNkABxXm5v0ouys8HPNkTLFLep0gLqRXW3gYN2XbKUWMs7z7hJpkY" +"GxP8YQSxi513O2dWVCXB8S6erIz9E/bcfdXoCPyQdn42y3IEoJvPvBS3S55fD4+Q" +"Q43GPhumSg9a6S3hnyw8DX5OiUGmqgQrtSeDRsNmWqtWizEQbe+fotZpEn/7zYTa" +"tk1ni/k5jDH/QeuG"; + +const std::string crlExampleCertificate = +"MIIFlDCCBHygAwIBAgIBADANBgkqhkiG9w0BAQUFADBDMRIwEAYKCZImiZPyLGQB" +"GRYCZXMxGDAWBgoJkiaJk/IsZAEZFghpcmlzZ3JpZDETMBEGA1UEAxMKSVJJU0dy" +"aWRDQTAeFw0wNTA2MjgwNTAyMjhaFw0xNTA2MjYwNTAyMjhaMEMxEjAQBgoJkiaJ" +"k/IsZAEZFgJlczEYMBYGCgmSJomT8ixkARkWCGlyaXNncmlkMRMwEQYDVQQDEwpJ" +"UklTR3JpZENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1CQiWlff" +"ajoMSTuismKqLQ+Mt33Tq4bBpCZvCBXhqan1R0ksILPtK1L7C8QWqPk6AZZpuNmY" +"cNVtJGc8ksgDWvX0EB3GKwZTZ8RrSRlSEe9Otq+Ur7S9uxM1JMmCr6zZTMFANzBS" +"4btnduV78C09IhFYG4OW8IPhNrbfPaeOR+PRPAa/qdSONAwTrM1sZkIvGpAkBWM6" +"Pn7TK9BAK6GLvwgii780fWj3Cwgmp8EDCTievBbWj+z8/apMEy9R0vyB2dWNNCnk" +"6q8VvrjgMsJt33O3BqOoBuZ8R/SS9OFWLFSU3s7cfrRaUSJk/Mx8OGFizRkcXSzX" +"0Nidcg7hX5i78wIDAQABo4ICkTCCAo0wDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E" +"FgQUnUJkLlupXvH/bMg8NtPxtkOYrRowawYDVR0jBGQwYoAUnUJkLlupXvH/bMg8" +"NtPxtkOYrRqhR6RFMEMxEjAQBgoJkiaJk/IsZAEZFgJlczEYMBYGCgmSJomT8ixk" +"ARkWCGlyaXNncmlkMRMwEQYDVQQDEwpJUklTR3JpZENBggEAMA4GA1UdDwEB/wQE" +"AwIBxjARBglghkgBhvhCAQEEBAMCAAcwOwYJYIZIAYb4QgENBC4WLElSSVNHcmlk" +"IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENlcnRpZmljYXRlMIGZBgNVHR8EgZEw" +"gY4wLqAsoCqGKGh0dHA6Ly93d3cuaXJpc2dyaWQuZXMvcGtpL2NybC9jYWNybC5w" +"ZW0wXKBaoFiGVmxkYXA6Ly9sZGFwLmlyaXNncmlkLmVzOjEzODAvY249SVJJU0dy" +"aWRDQSxkYz1pcmlzZ3JpZCxkYz1lcz9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0" +"MDcGCWCGSAGG+EIBAwQqFihodHRwOi8vd3d3LmlyaXNncmlkLmVzL3BraS9jcmwv" +"Y2FjcmwucGVtME4GCWCGSAGG+EIBCARBFj9odHRwOi8vd3d3LmlyaXNncmlkLmVz" +"L3BraS9wb2xpY3kvMS4zLjYuMS40LjEuNzU0Ny4yLjIuNC4xLjEuMS8waQYDVR0g" +"BGIwYDBeBg0rBgEEAbp7AgIEAQEBME0wSwYIKwYBBQUHAgEWP2h0dHA6Ly93d3cu" +"aXJpc2dyaWQuZXMvcGtpL3BvbGljeS8xLjMuNi4xLjQuMS43NTQ3LjIuMi40LjEu" +"MS4xLzANBgkqhkiG9w0BAQUFAAOCAQEAaqRfyLER+P2QOZLLdz66m7FGsgtFsAEx" +"wiNrIChFWfyHVZG7Ph1fn/GDD5LMsrU23lx3NBN5/feHuut1XNYKNs8vtV07D70r" +"DKjUlPbmWV0B+/GDxe1FDGop/tKQfyHSUaBuauXChFU/2INu5lhBerNl7QxNJ1ws" +"cWGiT7R+L/2EjgzWgH1V/0zmIOMep6kY7MUs8rlyF0O5MNFs232cA1trl9kvhAGU" +"9p58Enf5DWMrh17SPH586yIJeiWZtPez9G54ftY+XIqfn0X0zso0dnoXNJQYS043" +"/5vSnoHdRx/EmN8yjeEavZtC48moN0iJ38eB44uKgCD77rZW5s1XqA=="; + +//class TestCleanup +//{ +// public: +// explicit TestCleanup(bool bCheckForFakeVerification = false) +// { +// if (bCheckForFakeVerification) { +// bool bUnsetEnvVar = true; +// +// m_strEnvVar = "CHECK_ONLY_DOMAIN_INSTEAD_OF_VALIDATION"; +// if (getenv(m_strEnvVar.c_str()) != NULL) { +// bUnsetEnvVar = false; +// } else { +// setenv(m_strEnvVar.c_str(), "1", 0); +// } +// } +// } +// +// ~TestCleanup() +// { +// if (!m_strRootCAPath.empty()) { +// removeCertGivenByFilename(m_strRootCAPath.c_str()); +// } +// +// if (!m_strEnvVar.empty()) { +// unsetenv(m_strEnvVar.c_str()); +// } +// } +// +// void setRootCAPath(const std::string& strRootCAPath) +// { +// m_strRootCAPath = strRootCAPath; +// } +// +// private: +// std::string m_strRootCAPath; +// std::string m_strEnvVar; +//}; +// +//class PolicyChanger : public DPL::Event::EventListener +//{ +// public: +// PolicyChanger() +// { +// DPL::Event::EventDeliverySystem::AddListener(this); +// } +// +// ~PolicyChanger() +// { +// DPL::Event::EventDeliverySystem::RemoveListener(this); +// } +// +// void OnEventReceived(const AceUpdateResponseEvent& event) +// { +// if (0 != event.GetArg0()) { +// LogError("Policy change failed"); +// } +// Assert(0 == event.GetArg0() && "Policy change failed"); +// LoopControl::finish_wait_for_wrt_init(); +// } +// +// void updatePolicy(const std::string& path) +// { +// AceUpdateRequestEvent event(path); +// DPL::Event::EventDeliverySystem::Publish(event); +// LoopControl::wait_for_wrt_init(); +// } +//}; + +} // namespace anonymous + +using namespace ValidationCore; + +////////////////////////////////////////////////// +//////// VALIDATION CORE TEST SUITE //////////// +////////////////////////////////////////////////// + +RUNNER_TEST(test01_signature_finder) +{ + SignatureFileInfoSet signatureSet; + SignatureFinder signatureFinder(widget_path); + RUNNER_ASSERT_MSG( + SignatureFinder::NO_ERROR == signatureFinder.find(signatureSet), + "SignatureFinder failed"); + RUNNER_ASSERT_MSG(signatureSet.size() == 3, + "Some signature has not been found"); + + SignatureFileInfo first = *(signatureSet.begin()); + RUNNER_ASSERT_MSG( + std::string("author-signature.xml") == first.getFileName(), + "Author Signature"); + RUNNER_ASSERT_MSG(-1 == first.getFileNumber(), "Wrong signature number."); + first = *(signatureSet.rbegin()); + RUNNER_ASSERT_MSG(std::string("signature22.xml") == first.getFileName(), + "Wrong signature fileName."); + RUNNER_ASSERT_MSG(22 == first.getFileNumber(), "Wrong signature number."); +} + +RUNNER_TEST(test02_signature_reader) +{ + SignatureFileInfoSet signatureSet; + SignatureFinder signatureFinder(widget_path); + RUNNER_ASSERT_MSG( + SignatureFinder::NO_ERROR == signatureFinder.find(signatureSet), + "SignatureFinder failed"); + + SignatureFileInfoSet::reverse_iterator iter = signatureSet.rbegin(); + + for (; iter != signatureSet.rend(); ++iter) { + SignatureData data(widget_path + iter->getFileName(), + iter->getFileNumber()); + SignatureReader xml; + xml.initialize(data, WrtDB::GlobalConfig::GetSignatureXmlSchema()); + xml.read(data); + } +} + +RUNNER_TEST(test03_signature_validator) +{ + SignatureFileInfoSet signatureSet; + SignatureFinder signatureFinder(widget_path); + RUNNER_ASSERT_MSG( + SignatureFinder::NO_ERROR == signatureFinder.find(signatureSet), + "SignatureFinder failed"); + + SignatureFileInfoSet::reverse_iterator iter = signatureSet.rbegin(); + + for (; iter != signatureSet.rend(); ++iter) { + SignatureData data(widget_path + iter->getFileName(), + iter->getFileNumber()); + SignatureReader xml; + xml.initialize(data, WrtDB::GlobalConfig::GetSignatureXmlSchema()); + xml.read(data); + + SignatureValidator validator( + false, + false, + false); + + if (data.isAuthorSignature()) { + RUNNER_ASSERT_MSG( + SignatureValidator::SIGNATURE_DISREGARD == + validator.check(data, widget_path), + "Validation failed"); + } else { + RUNNER_ASSERT_MSG( + SignatureValidator::SIGNATURE_VERIFIED == + validator.check(data, widget_path), + "Validation failed"); + } + } +} + +RUNNER_TEST(test05_signature_reference) +{ + SignatureFileInfoSet signatureSet; + SignatureFinder signatureFinder(widget_path); + RUNNER_ASSERT_MSG( + SignatureFinder::NO_ERROR == signatureFinder.find(signatureSet), + "SignatureFinder failed"); + + SignatureFileInfoSet::reverse_iterator iter = signatureSet.rbegin(); + + for (; iter != signatureSet.rend(); ++iter) { + SignatureData data(widget_path + iter->getFileName(), + iter->getFileNumber()); + SignatureReader xml; + xml.initialize(data, WrtDB::GlobalConfig::GetSignatureXmlSchema()); + xml.read(data); + + SignatureValidator sval( + false, + false, + false); + sval.check(data, widget_path); + + ReferenceValidator val(widget_path); + RUNNER_ASSERT( + ReferenceValidator::NO_ERROR == val.checkReferences(data)); + } +} + +RUNNER_TEST(test07t01_base64) +{ + std::string strraw = "1234567890qwertyuiop[]asdfghjkl;'zxcvbnm,."; + std::string strenc = + "MTIzNDU2Nzg5MHF3ZXJ0eXVpb3BbXWFzZGZnaGprbDsnenhjdmJubSwu"; + + Base64Encoder encoder; + encoder.reset(); + encoder.append(strraw); + encoder.finalize(); + RUNNER_ASSERT_MSG(strenc == encoder.get(), "Error in Base64Encoder."); + + Base64Decoder decoder; + decoder.reset(); + decoder.append(strenc); + RUNNER_ASSERT(decoder.finalize()); + RUNNER_ASSERT_MSG(strraw == decoder.get(), "Error in Base64Decoder."); +} + +RUNNER_TEST(test07t02_base64) +{ + const size_t MAX = 40; + char buffer[MAX]; + for (size_t i = 0; i(i); + } + + std::string raw(&buffer[0], &buffer[MAX]); + + RUNNER_ASSERT(MAX == raw.size()); + + Base64Encoder encoder; + encoder.reset(); + encoder.append(raw); + encoder.finalize(); + std::string enc = encoder.get(); + + Base64Decoder decoder; + decoder.reset(); + decoder.append(enc); + RUNNER_ASSERT(decoder.finalize()); + RUNNER_ASSERT_MSG(raw == decoder.get(), "Error in Base64 conversion."); +} + +RUNNER_TEST(test07t03_base64) +{ + std::string invalid = "1234)"; + + Base64Decoder decoder; + decoder.reset(); + decoder.append(invalid); + RUNNER_ASSERT(false == decoder.finalize()); +} + +RUNNER_TEST(test07t04_base64) +{ + std::string invalid = "12234"; + + Base64Decoder decoder; + decoder.reset(); + + bool exception = false; + Try { + std::string temp = decoder.get(); + } Catch(Base64Decoder::Exception::NotFinalized) { + exception = true; + } + + RUNNER_ASSERT_MSG(exception, "Base64Decoder does not throw error."); +} + +RUNNER_TEST(test08t01_Certificate) +{ + Certificate cert(certVerisign, Certificate::FORM_BASE64); + + DPL::OptionalString result; + + result = cert.getCommonName(Certificate::FIELD_SUBJECT); + RUNNER_ASSERT_MSG(!result.IsNull(), "No common name"); + RUNNER_ASSERT_MSG(*result == DPL::FromUTF8String("www.verisign.com"), + "CommonName mismatch"); + + result = cert.getCommonName(Certificate::FIELD_ISSUER); + RUNNER_ASSERT_MSG(!result.IsNull(), "No common name"); + RUNNER_ASSERT_MSG(result == DPL::FromUTF8String( + "VeriSign Class 3 Extended Validation SSL SGC CA"), + "CommonName mismatch"); + + result = cert.getCountryName(); + RUNNER_ASSERT_MSG(!result.IsNull(), "No country"); + RUNNER_ASSERT_MSG(*result == DPL::FromUTF8String("US"), + "Country mismatch"); +} + +RUNNER_TEST(test08t02_Certificate) +{ + Certificate cert(certVerisign, Certificate::FORM_BASE64); + + Certificate::Fingerprint fin = + cert.getFingerprint(Certificate::FINGERPRINT_SHA1); + + unsigned char buff[20] = { + 0xb9, 0x72, 0x1e, 0xd5, 0x49, + 0xed, 0xbf, 0x31, 0x84, 0xd8, + 0x27, 0x0c, 0xfe, 0x03, 0x11, + 0x19, 0xdf, 0xc2, 0x2b, 0x0a}; + RUNNER_ASSERT_MSG(fin.size() == 20, "Wrong size of fingerprint"); + + for (size_t i = 0; i<20; ++i) { + RUNNER_ASSERT_MSG(fin[i] == buff[i], "Fingerprint mismatch"); + } +} + +RUNNER_TEST(test08t03_Certificate) +{ + Certificate cert(certVerisign, Certificate::FORM_BASE64); + + Certificate::AltNameSet nameSet = cert.getAlternativeNameDNS(); + + RUNNER_ASSERT(nameSet.size() == 8); + + DPL::String str = DPL::FromUTF8String("verisign.com"); + RUNNER_ASSERT(nameSet.find(str) != nameSet.end()); + + str = DPL::FromUTF8String("fake.com"); + RUNNER_ASSERT(nameSet.find(str) == nameSet.end()); + +} + +RUNNER_TEST(test09t01_CertificateCollection) +{ + CertificateList list; + list.push_back(CertificatePtr( + new Certificate(google2nd, Certificate::FORM_BASE64))); + list.push_back(CertificatePtr( + new Certificate(googleCA, Certificate::FORM_BASE64))); + list.push_back(CertificatePtr( + new Certificate(google3rd, Certificate::FORM_BASE64))); + + CertificateCollection collection; + collection.load(list); + + bool exception = false; + + Try { + RUNNER_ASSERT(collection.isChain()); + } Catch (CertificateCollection::Exception::WrongUsage) { + exception = true; + } + + RUNNER_ASSERT_MSG(exception, "Exception expected!"); + + RUNNER_ASSERT_MSG(collection.sort(), "Sort failed"); + + RUNNER_ASSERT(collection.isChain()); + + std::string encoded = collection.toBase64String(); + + collection.clear(); + + RUNNER_ASSERT_MSG(collection.size() == 0, "Function clear failed."); + + collection.load(encoded); + + RUNNER_ASSERT_MSG(collection.sort(), "Sort failed"); + + list = collection.getChain(); + + RUNNER_ASSERT( + DPL::ToUTF8String(*(list.front().Get()->getCommonName())) == + "www.google.com"); + RUNNER_ASSERT( + DPL::ToUTF8String(*(list.back().Get()->getOrganizationName())) == + "VeriSign, Inc."); +} + +RUNNER_TEST(test51t01_ocsp_validation_negative) +{ + CertificateCacheDAO::clearCertificateCache(); + + CertificateList lOCSPCertificates; + CertificatePtr certificatePtr; + CertificatePtr pCert0; + CertificatePtr pCert1; + CertificatePtr pCert2; + CertificatePtr pRootCert; + std::string caRootPath(keys_path + "ocsp_rootca.crt"), + certLevel0Path(keys_path + "ocsp_level0deprecated.crt"), + certLevel1Path(keys_path + "ocsp_level1.crt"), + certLevel2Path(keys_path + "ocsp_level2.crt"); + + pRootCert = RevocationCheckerBase::loadPEMFile(caRootPath.c_str()); + if (!pRootCert) { + RUNNER_ASSERT_MSG(false, "Couldn't load ocsp_rootca.crt"); + } + lOCSPCertificates.push_back(pRootCert); + + pCert0 = RevocationCheckerBase::loadPEMFile(certLevel0Path.c_str()); + if (!pCert0) { + RUNNER_ASSERT_MSG(false, "Couldn't load ocsp_level0.crt"); + } + lOCSPCertificates.push_back(CertificatePtr(pCert0)); + + pCert1 = RevocationCheckerBase::loadPEMFile(certLevel1Path.c_str()); + if (!pCert1) { + RUNNER_ASSERT_MSG(false, "Couldn't load ocsp_level1.crt"); + } + lOCSPCertificates.push_back(CertificatePtr(pCert1)); + + pCert2 = RevocationCheckerBase::loadPEMFile(certLevel2Path.c_str()); + if (!pCert2) { + RUNNER_ASSERT_MSG(false, "Couldn't load ocsp_level2.crt"); + } + lOCSPCertificates.push_back(CertificatePtr(pCert2)); + + OCSP ocsp; + ocsp.setDigestAlgorithmForCertId(ValidationCore::OCSP::SHA1); + ocsp.setDigestAlgorithmForRequest(ValidationCore::OCSP::SHA1); + + CertificateCollection collection; + collection.load(lOCSPCertificates); + RUNNER_ASSERT(collection.sort()); + CertificateList sorted = collection.getChain(); + + ocsp.setTrustedStore(sorted); + VerificationStatusSet status = ocsp.validateCertificateList(sorted); + + RUNNER_ASSERT_MSG(!status.contains(VERIFICATION_STATUS_CONNECTION_FAILED), + "Caught OCSP connection error from store exception"); + RUNNER_ASSERT_MSG(status.contains(VERIFICATION_STATUS_GOOD), + "Caught OCSP verification error exception"); + RUNNER_ASSERT_MSG(status.contains(VERIFICATION_STATUS_VERIFICATION_ERROR), + "Caught OCSP verification error exception"); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test51t02_ocsp_validation_positive) +{ + CertificateCacheDAO::clearCertificateCache(); + + CertificateList lOCSPCertificates; + CertificatePtr certificatePtr; + CertificatePtr pCert0; + CertificatePtr pCert1; + CertificatePtr pCert2; + CertificatePtr pRootCert; + std::string caRootPath(keys_path + "ocsp_rootca.crt"), + certLevel1Path(keys_path + "ocsp_level1.crt"), + certLevel2Path(keys_path + "ocsp_level2.crt"); + + pRootCert = RevocationCheckerBase::loadPEMFile(caRootPath.c_str()); + if (!pRootCert) { + RUNNER_ASSERT_MSG(false, "Couldn't load ocsp_rootca.crt"); + } + lOCSPCertificates.push_back(pRootCert); + + pCert1 = RevocationCheckerBase::loadPEMFile(certLevel1Path.c_str()); + if (!pCert1) { + RUNNER_ASSERT_MSG(false, "Couldn't load ocsp_level1.crt"); + } + lOCSPCertificates.push_back(CertificatePtr(pCert1)); + + pCert2 = RevocationCheckerBase::loadPEMFile(certLevel2Path.c_str()); + if (!pCert2) { + RUNNER_ASSERT_MSG(false, "Couldn't load ocsp_level2.crt"); + } + lOCSPCertificates.push_back(CertificatePtr(pCert2)); + + OCSP ocsp; + ocsp.setDigestAlgorithmForCertId(ValidationCore::OCSP::SHA1); + ocsp.setDigestAlgorithmForRequest(ValidationCore::OCSP::SHA1); + + CertificateCollection collection; + collection.load(lOCSPCertificates); + RUNNER_ASSERT(collection.sort()); + CertificateList sorted = collection.getChain(); + + ocsp.setTrustedStore(sorted); + VerificationStatusSet status = ocsp.validateCertificateList(sorted); + + RUNNER_ASSERT_MSG(!status.contains(VERIFICATION_STATUS_CONNECTION_FAILED), + "Caught OCSP connection error from store exception"); + RUNNER_ASSERT_MSG(status.contains(VERIFICATION_STATUS_GOOD), + "Caught OCSP verification error exception"); + RUNNER_ASSERT_MSG(!status.contains(VERIFICATION_STATUS_VERIFICATION_ERROR), + "Caught OCSP verification error exception"); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test51t04_ocsp_request) +{ + CertificateList lTrustedCerts; + + lTrustedCerts.push_back(CertificatePtr( + new Certificate(google3rd, Certificate::FORM_BASE64))); + lTrustedCerts.push_back(CertificatePtr( + new Certificate(google2nd, Certificate::FORM_BASE64))); + lTrustedCerts.push_back(CertificatePtr( + new Certificate(googleCA, Certificate::FORM_BASE64))); + + CertificateCollection chain; + chain.load(lTrustedCerts); + chain.sort(); + + OCSP ocsp; + ocsp.setDigestAlgorithmForCertId(OCSP::SHA1); + ocsp.setDigestAlgorithmForRequest(OCSP::SHA1); + ocsp.setTrustedStore(lTrustedCerts); + VerificationStatus result = ocsp.checkEndEntity(chain); + + RUNNER_ASSERT(VERIFICATION_STATUS_GOOD == result); +} + +RUNNER_TEST(test51t05_cached_ocsp_validation_negative) +{ + CertificateCacheDAO::clearCertificateCache(); + + CertificateList lOCSPCertificates; + CertificatePtr certificatePtr; + CertificatePtr pCert0; + CertificatePtr pCert1; + CertificatePtr pCert2; + CertificatePtr pRootCert; + std::string caRootPath(keys_path + "ocsp_rootca.crt"), + certLevel0Path(keys_path + "ocsp_level0deprecated.crt"), + certLevel1Path(keys_path + "ocsp_level1.crt"), + certLevel2Path(keys_path + "ocsp_level2.crt"); + + pRootCert = RevocationCheckerBase::loadPEMFile(caRootPath.c_str()); + RUNNER_ASSERT_MSG(pRootCert, "Couldn't load ocsp_rootca.crt"); + lOCSPCertificates.push_back(pRootCert); + + pCert0 = RevocationCheckerBase::loadPEMFile(certLevel0Path.c_str()); + RUNNER_ASSERT_MSG(pCert0, "Couldn't load ocsp_level0.crt"); + lOCSPCertificates.push_back(CertificatePtr(pCert0)); + + pCert1 = RevocationCheckerBase::loadPEMFile(certLevel1Path.c_str()); + RUNNER_ASSERT_MSG(pCert1, "Couldn't load ocsp_level1.crt"); + lOCSPCertificates.push_back(CertificatePtr(pCert1)); + + pCert2 = RevocationCheckerBase::loadPEMFile(certLevel2Path.c_str()); + RUNNER_ASSERT_MSG(pCert2, "Couldn't load ocsp_level2.crt"); + lOCSPCertificates.push_back(CertificatePtr(pCert2)); + + CachedOCSP ocsp; + + CertificateCollection collection; + collection.load(lOCSPCertificates); + RUNNER_ASSERT(collection.sort()); + + VerificationStatus status = ocsp.check(collection); + + RUNNER_ASSERT_MSG(status != VERIFICATION_STATUS_GOOD, + "Caught OCSP verification error exception"); + + OCSPCachedStatusList respList; + CertificateCacheDAO::getOCSPStatusList(&respList); + unsigned len = respList.size(); + + status = ocsp.check(collection); + + RUNNER_ASSERT_MSG(status != VERIFICATION_STATUS_GOOD, + "Caught OCSP verification error exception"); + + respList.clear(); + CertificateCacheDAO::getOCSPStatusList(&respList); + RUNNER_ASSERT_MSG(respList.size() == len && len > 0, + "Caught OCSP cache error exception"); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test51t06_cached_ocsp_validation_positive) +{ + CertificateCacheDAO::clearCertificateCache(); + + CertificateList lOCSPCertificates; + CertificatePtr certificatePtr; + CertificatePtr pCert0; + CertificatePtr pCert1; + CertificatePtr pCert2; + CertificatePtr pRootCert; + std::string caRootPath(keys_path + "ocsp_rootca.crt"), + certLevel1Path(keys_path + "ocsp_level1.crt"), + certLevel2Path(keys_path + "ocsp_level2.crt"); + + pRootCert = RevocationCheckerBase::loadPEMFile(caRootPath.c_str()); + RUNNER_ASSERT_MSG(pRootCert, "Couldn't load ocsp_rootca.crt"); + lOCSPCertificates.push_back(pRootCert); + + pCert1 = RevocationCheckerBase::loadPEMFile(certLevel1Path.c_str()); + RUNNER_ASSERT_MSG(pCert1, "Couldn't load ocsp_level1.crt"); + lOCSPCertificates.push_back(CertificatePtr(pCert1)); + + pCert2 = RevocationCheckerBase::loadPEMFile(certLevel2Path.c_str()); + RUNNER_ASSERT_MSG(pCert2, "Couldn't load ocsp_level2.crt"); + lOCSPCertificates.push_back(CertificatePtr(pCert2)); + + CachedOCSP ocsp; + + CertificateCollection collection; + collection.load(lOCSPCertificates); + RUNNER_ASSERT(collection.sort()); + + VerificationStatus status = ocsp.check(collection); + + RUNNER_ASSERT_MSG(status == VERIFICATION_STATUS_GOOD, + "Caught OCSP verification error exception"); + + OCSPCachedStatusList respList; + CertificateCacheDAO::getOCSPStatusList(&respList); + unsigned len = respList.size(); + + status = ocsp.check(collection); + + RUNNER_ASSERT_MSG(status == VERIFICATION_STATUS_GOOD, + "Caught OCSP verification error exception"); + + respList.clear(); + CertificateCacheDAO::getOCSPStatusList(&respList); + RUNNER_ASSERT_MSG(respList.size() == len && len > 0, + "Caught OCSP cache error exception"); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test61_crl_test_revocation_no_crl) +{ + //Clear CRL cache so there is no CRL for those certificates URI. + CertificateCacheDAO::clearCertificateCache(); + //Prepare certificate chain + TestCRL crl; + std::string cacertStr(crl.getFileContent(cert_store_path + "cacert.pem")); + std::string certAStr( + crl.getFileContent(cert_store_path + "1second_level.pem")); + std::string certBStr( + crl.getFileContent(cert_store_path + "1third_level.pem")); + + CertificateLoader loader; + CertificateList certList; + CertificateCollection collection; + RUNNER_ASSERT(loader.loadCertificateFromRawData(cacertStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + RUNNER_ASSERT(loader.loadCertificateFromRawData(certAStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + RUNNER_ASSERT(loader.loadCertificateFromRawData(certBStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + + collection.load(certList); + + CRL::RevocationStatus status = crl.checkCertificateChain(collection); + RUNNER_ASSERT_MSG(status.isCRLValid == false, + "Some certificate have no CRL extension!"); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test62_crl_test_revocation_set1) +{ + CertificateCacheDAO::clearCertificateCache(); + + //Prepare certificate chain + TestCRL crl; + std::string cacertStr(crl.getFileContent(cert_store_path + "cacert.pem")); + std::string certAStr( + crl.getFileContent(cert_store_path + "1second_level.pem")); + std::string certBStr( + crl.getFileContent(cert_store_path + "1third_level.pem")); + crl.addCRLToStore(cert_store_path + "cacrl1.pem", crl_URI); + + CertificateLoader loader; + CertificateList certList; + CertificateCollection collection; + RUNNER_ASSERT(loader.loadCertificateFromRawData(cacertStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + RUNNER_ASSERT(loader.loadCertificateFromRawData(certAStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + RUNNER_ASSERT(loader.loadCertificateFromRawData(certBStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + + collection.load(certList); + + CRL::RevocationStatus status = crl.checkCertificateChain(collection); + RUNNER_ASSERT(status.isCRLValid); + RUNNER_ASSERT(status.isRevoked); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test63_crl_test_revocation_set1) +{ + CertificateCacheDAO::clearCertificateCache(); + + //Prepare certificate chain + TestCRL crl; + std::string cacertStr(crl.getFileContent(cert_store_path + "cacert.pem")); + std::string certAStr( + crl.getFileContent(cert_store_path + "1second_level.pem")); + std::string certBStr( + crl.getFileContent(cert_store_path + "1third_level.pem")); + crl.addCRLToStore(cert_store_path + "cacrl1.pem", crl_URI); + + CertificateLoader loader; + CertificateList certList; + CertificateCollection collection; + RUNNER_ASSERT(loader.loadCertificateFromRawData(cacertStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + RUNNER_ASSERT(loader.loadCertificateFromRawData(certAStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + RUNNER_ASSERT(loader.loadCertificateFromRawData(certBStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + + collection.load(certList); + + CRL::RevocationStatus status = crl.checkCertificateChain(collection); + RUNNER_ASSERT(status.isCRLValid); + RUNNER_ASSERT(status.isRevoked); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test64_crl_test_revocation_set2) +{ + CertificateCacheDAO::clearCertificateCache(); + + //Prepare certificate chain + TestCRL crl; + std::string cacertStr(crl.getFileContent(cert_store_path + "cacert.pem")); + std::string certAStr( + crl.getFileContent(cert_store_path + "2second_level.pem")); + std::string certBStr( + crl.getFileContent(cert_store_path + "2third_level.pem")); + crl.addCRLToStore(cert_store_path + "cacrl1.pem", crl_URI); + + CertificateLoader loader; + CertificateList certList; + CertificateCollection collection; + RUNNER_ASSERT(loader.loadCertificateFromRawData(cacertStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + RUNNER_ASSERT(loader.loadCertificateFromRawData(certAStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + RUNNER_ASSERT(loader.loadCertificateFromRawData(certBStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + + collection.load(certList); + + CRL::RevocationStatus status = crl.checkCertificateChain(collection); + RUNNER_ASSERT(status.isCRLValid); + RUNNER_ASSERT(!status.isRevoked); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test65_crl_test_revocation_set2) +{ + CertificateCacheDAO::clearCertificateCache(); + + //Prepare certificate chain + TestCRL crl; + std::string cacertStr(crl.getFileContent(cert_store_path + "cacert.pem")); + std::string certAStr( + crl.getFileContent(cert_store_path + "2second_level.pem")); + std::string certBStr( + crl.getFileContent(cert_store_path + "2third_level.pem")); + crl.addCRLToStore(cert_store_path + "cacrl2.pem", crl_URI); + + CertificateLoader loader; + CertificateList certList; + CertificateCollection collection; + RUNNER_ASSERT(loader.loadCertificateFromRawData(cacertStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + RUNNER_ASSERT(loader.loadCertificateFromRawData(certAStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + RUNNER_ASSERT(loader.loadCertificateFromRawData(certBStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + + collection.load(certList); + + CRL::RevocationStatus status = crl.checkCertificateChain(collection); + RUNNER_ASSERT(status.isCRLValid); + RUNNER_ASSERT(status.isRevoked); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test66_crl_update_expired_lists) +{ + CertificateCacheDAO::clearCertificateCache(); + + CertificateLoader loader; + loader.loadCertificateFromRawData(google2nd); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + TestCRL crl; + RUNNER_ASSERT_MSG( + crl.updateList(loader.getCertificatePtr(), CRL::UPDATE_ON_EXPIRED), + "CRL update on expired succeeded"); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test67_crl_update_lists_on_demand) +{ + CertificateCacheDAO::clearCertificateCache(); + + CertificateLoader loader; + loader.loadCertificateFromRawData(google2nd); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + TestCRL crl; + RUNNER_ASSERT_MSG( + crl.updateList(loader.getCertificatePtr(), CRL::UPDATE_ON_DEMAND), + "CRL update on demand succeeded"); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test68_cached_crl_test_positive) +{ + CertificateCacheDAO::clearCertificateCache(); + + TestCRL crl; + + std::string cacertStr(crl.getFileContent(cert_store_path + "cacert.pem")); + std::string certAStr( + crl.getFileContent(cert_store_path + "2second_level.pem")); + std::string certBStr( + crl.getFileContent(cert_store_path + "2third_level.pem")); + crl.addCRLToStore(cert_store_path + "cacrl1.pem", crl_URI); + + CertificateLoader loader; + CertificateList certList; + CertificateCollection collection; + RUNNER_ASSERT(loader.loadCertificateFromRawData(cacertStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + RUNNER_ASSERT(loader.loadCertificateFromRawData(certAStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + RUNNER_ASSERT(loader.loadCertificateFromRawData(certBStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + + collection.load(certList); + + CRL::RevocationStatus status = crl.checkCertificateChain(collection); + + CachedCRL cached; + VerificationStatus cached_status = cached.check(collection); + CRLCachedDataList list; + CertificateCacheDAO::getCRLResponseList(&list); + unsigned len = list.size(); + + RUNNER_ASSERT(status.isCRLValid); + RUNNER_ASSERT(!status.isRevoked && + cached_status == VERIFICATION_STATUS_GOOD); + + cached_status = cached.check(collection); + list.clear(); + CertificateCacheDAO::getCRLResponseList(&list); + + RUNNER_ASSERT(len == list.size()); + RUNNER_ASSERT(!status.isRevoked && + cached_status == VERIFICATION_STATUS_GOOD); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test69_cached_crl_test_negative) +{ + CertificateCacheDAO::clearCertificateCache(); + + //Prepare certificate chain + TestCRL crl; + std::string cacertStr(crl.getFileContent(cert_store_path + "cacert.pem")); + std::string certAStr( + crl.getFileContent(cert_store_path + "2second_level.pem")); + std::string certBStr( + crl.getFileContent(cert_store_path + "2third_level.pem")); + crl.addCRLToStore(cert_store_path + "cacrl2.pem", crl_URI); + + CertificateLoader loader; + CertificateList certList; + CertificateCollection collection; + RUNNER_ASSERT(loader.loadCertificateFromRawData(cacertStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + RUNNER_ASSERT(loader.loadCertificateFromRawData(certAStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + RUNNER_ASSERT(loader.loadCertificateFromRawData(certBStr) == + CertificateLoader::NO_ERROR); + RUNNER_ASSERT(!!loader.getCertificatePtr()); + certList.push_back(loader.getCertificatePtr()); + + collection.load(certList); + + CRL::RevocationStatus status = crl.checkCertificateChain(collection); + CachedCRL cached; + VerificationStatus cached_status = cached.check(collection); + CRLCachedDataList list; + CertificateCacheDAO::getCRLResponseList(&list); + unsigned len = list.size(); + + RUNNER_ASSERT(status.isCRLValid); + RUNNER_ASSERT(status.isRevoked && + cached_status == VERIFICATION_STATUS_REVOKED); + + cached_status = cached.check(collection); + list.clear(); + CertificateCacheDAO::getCRLResponseList(&list); + + RUNNER_ASSERT(len == list.size()); + RUNNER_ASSERT(status.isRevoked && + cached_status == VERIFICATION_STATUS_REVOKED); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test70_ocsp_local_validation_positive) +{ + CertificateCacheDAO::clearCertificateCache(); + + CertificateList lOCSPCertificates; + CertificatePtr certificatePtr; + CertificatePtr pCert0; + CertificatePtr pRootCert; + std::string caRootPath(cert_store_path + "cacert.pem"), + certLevel0Path(cert_store_path + "1second_level.pem"); + + pRootCert = RevocationCheckerBase::loadPEMFile(caRootPath.c_str()); + if (!pRootCert) { + RUNNER_ASSERT_MSG(false, "Couldn't load cacert.pem"); + } + lOCSPCertificates.push_back(pRootCert); + + pCert0 = RevocationCheckerBase::loadPEMFile(certLevel0Path.c_str()); + if (!pCert0) { + RUNNER_ASSERT_MSG(false, "Couldn't load 1second_level.pem"); + } + lOCSPCertificates.push_back(CertificatePtr(pCert0)); + + OCSP ocsp; + ocsp.setDigestAlgorithmForCertId(ValidationCore::OCSP::SHA1); + ocsp.setDigestAlgorithmForRequest(ValidationCore::OCSP::SHA1); + + CertificateCollection collection; + collection.load(lOCSPCertificates); + RUNNER_ASSERT(collection.sort()); + CertificateList sorted = collection.getChain(); + + ocsp.setTrustedStore(sorted); + VerificationStatusSet status = ocsp.validateCertificateList(sorted); + + RUNNER_ASSERT_MSG(!status.contains(VERIFICATION_STATUS_CONNECTION_FAILED), + "Caught OCSP connection error - check if " + "dpl-tests-vcore-ocsp-server.sh is running!"); + RUNNER_ASSERT_MSG(status.contains(VERIFICATION_STATUS_GOOD), + "Caught OCSP verification error exception"); + RUNNER_ASSERT_MSG(!status.contains(VERIFICATION_STATUS_VERIFICATION_ERROR), + "Caught OCSP verification error exception"); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test71_ocsp_local_validation_positive) +{ + CertificateCacheDAO::clearCertificateCache(); + + CertificateList lOCSPCertificates; + CertificatePtr certificatePtr; + CertificatePtr pCert0; + CertificatePtr pRootCert; + std::string caRootPath(cert_store_path + "cacert.pem"), + certLevel0Path(cert_store_path + "3second_level.pem"); + + pRootCert = RevocationCheckerBase::loadPEMFile(caRootPath.c_str()); + if (!pRootCert) { + RUNNER_ASSERT_MSG(false, "Couldn't load cacert.pem"); + } + lOCSPCertificates.push_back(pRootCert); + + pCert0 = RevocationCheckerBase::loadPEMFile(certLevel0Path.c_str()); + if (!pCert0) { + RUNNER_ASSERT_MSG(false, "Couldn't load 3second_level.pem"); + } + lOCSPCertificates.push_back(CertificatePtr(pCert0)); + + OCSP ocsp; + ocsp.setDigestAlgorithmForCertId(ValidationCore::OCSP::SHA1); + ocsp.setDigestAlgorithmForRequest(ValidationCore::OCSP::SHA1); + + CertificateCollection collection; + collection.load(lOCSPCertificates); + RUNNER_ASSERT(collection.sort()); + CertificateList sorted = collection.getChain(); + + ocsp.setTrustedStore(sorted); + VerificationStatusSet status = ocsp.validateCertificateList(sorted); + + RUNNER_ASSERT_MSG(!status.contains(VERIFICATION_STATUS_CONNECTION_FAILED), + "Caught OCSP connection error - check if " + "dpl-tests-vcore-ocsp-server.sh is running!"); + RUNNER_ASSERT_MSG(status.contains(VERIFICATION_STATUS_GOOD), + "Caught OCSP verification error exception"); + RUNNER_ASSERT_MSG(!status.contains(VERIFICATION_STATUS_VERIFICATION_ERROR), + "Caught OCSP verification error exception"); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test72_ocsp_local_validation_revoked) +{ + CertificateCacheDAO::clearCertificateCache(); + + CertificateList lOCSPCertificates; + CertificatePtr certificatePtr; + CertificatePtr pCert0; + CertificatePtr pRootCert; + std::string caRootPath(cert_store_path + "cacert.pem"), + certLevel0Path(cert_store_path + "2second_level.pem"); + + pRootCert = RevocationCheckerBase::loadPEMFile(caRootPath.c_str()); + if (!pRootCert) { + RUNNER_ASSERT_MSG(false, "Couldn't load cacert.pem"); + } + lOCSPCertificates.push_back(pRootCert); + + pCert0 = RevocationCheckerBase::loadPEMFile(certLevel0Path.c_str()); + if (!pCert0) { + RUNNER_ASSERT_MSG(false, "Couldn't load 2second_level.pem"); + } + lOCSPCertificates.push_back(CertificatePtr(pCert0)); + + OCSP ocsp; + ocsp.setDigestAlgorithmForCertId(ValidationCore::OCSP::SHA1); + ocsp.setDigestAlgorithmForRequest(ValidationCore::OCSP::SHA1); + + CertificateCollection collection; + collection.load(lOCSPCertificates); + RUNNER_ASSERT(collection.sort()); + CertificateList sorted = collection.getChain(); + + ocsp.setTrustedStore(sorted); + VerificationStatusSet status = ocsp.validateCertificateList(sorted); + + RUNNER_ASSERT_MSG(!status.contains(VERIFICATION_STATUS_CONNECTION_FAILED), + "Caught OCSP connection error - check if " + "dpl-tests-vcore-ocsp-server.sh is running!"); + RUNNER_ASSERT_MSG(!status.contains(VERIFICATION_STATUS_GOOD), + "Caught OCSP verification error exception"); + RUNNER_ASSERT_MSG(status.contains(VERIFICATION_STATUS_REVOKED), + "Caught OCSP verification error exception"); + RUNNER_ASSERT_MSG(!status.contains(VERIFICATION_STATUS_UNKNOWN), + "Caught OCSP verification error exception"); + RUNNER_ASSERT_MSG(!status.contains(VERIFICATION_STATUS_VERIFICATION_ERROR), + "Caught OCSP verification error exception"); + + CertificateCacheDAO::clearCertificateCache(); +} + +RUNNER_TEST(test73_ocsp_local_validation_error_unknown_cert) +{ + CertificateCacheDAO::clearCertificateCache(); + + CertificateList lOCSPCertificates; + CertificatePtr certificatePtr; + CertificatePtr pCert0; + CertificatePtr pCert1; + CertificatePtr pRootCert; + std::string caRootPath(cert_store_path + "cacert.pem"), + certLevel0Path(cert_store_path + "1second_level.pem"), + certLevel1Path(cert_store_path + "1third_level.pem"); + + pRootCert = RevocationCheckerBase::loadPEMFile(caRootPath.c_str()); + if (!pRootCert) { + RUNNER_ASSERT_MSG(false, "Couldn't load cacerr.pem"); + } + lOCSPCertificates.push_back(pRootCert); + + pCert0 = RevocationCheckerBase::loadPEMFile(certLevel0Path.c_str()); + if (!pCert0) { + RUNNER_ASSERT_MSG(false, "Couldn't load 1second_level.pem"); + } + lOCSPCertificates.push_back(CertificatePtr(pCert0)); + + pCert1 = RevocationCheckerBase::loadPEMFile(certLevel1Path.c_str()); + if (!pCert1) { + RUNNER_ASSERT_MSG(false, "Couldn't load 1third_level.pem"); + } + lOCSPCertificates.push_back(CertificatePtr(pCert1)); + + OCSP ocsp; + ocsp.setDigestAlgorithmForCertId(ValidationCore::OCSP::SHA1); + ocsp.setDigestAlgorithmForRequest(ValidationCore::OCSP::SHA1); + + CertificateCollection collection; + collection.load(lOCSPCertificates); + RUNNER_ASSERT(collection.sort()); + CertificateList sorted = collection.getChain(); + + ocsp.setTrustedStore(sorted); + VerificationStatusSet status = ocsp.validateCertificateList(sorted); + + RUNNER_ASSERT_MSG(!status.contains(VERIFICATION_STATUS_CONNECTION_FAILED), + "Caught OCSP connection error - check if " + "dpl-tests-vcore-ocsp-server.sh is running!"); + RUNNER_ASSERT_MSG(status.contains(VERIFICATION_STATUS_GOOD), + "Caught OCSP verification error exception"); + RUNNER_ASSERT_MSG(!status.contains(VERIFICATION_STATUS_REVOKED), + "Caught OCSP verification error exception"); + RUNNER_ASSERT_MSG(status.contains(VERIFICATION_STATUS_VERIFICATION_ERROR), + "Caught OCSP verification error exception"); + RUNNER_ASSERT_MSG(!status.contains(VERIFICATION_STATUS_UNKNOWN), + "Caught OCSP verification error exception"); + + CertificateCacheDAO::clearCertificateCache(); +} + diff --git a/tests/vcore/TestEnv.cpp b/tests/vcore/TestEnv.cpp new file mode 100644 index 0000000..b12c3e1 --- /dev/null +++ b/tests/vcore/TestEnv.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include + +#include "TestEnv.h" + +const char *storePath = "code-signing_wac"; + +int addCertToStore(const char* name){ + int result = cert_svc_add_certificate_to_store(name, storePath); + + if (CERT_SVC_ERR_NO_ERROR != result) { + LogError("Error adding certificate: " << name << " To: " << storePath); + } + return result; +} + +//long int removeCertFromStore(const char* subjectName, const char* issuerName) +//{ +// long int result = OPERATION_SUCCESS; +// /* Retrieve all the certificates from store */ +// certmgr_cert_id certId; +// certmgr_mem_buff certRetrieved; +// certmgr_ctx context; +// char storeId[CERTMGR_MAX_PLUGIN_ID_SIZE]; +// char type[CERTMGR_MAX_CERT_TYPE_SIZE]; +// unsigned char certBuff[CERTMGR_MAX_BUFFER_SIZE * 2]; +// +// certmgr_cert_descriptor descriptor; +// certId.storeId = storeId; +// certId.type = type; +// +// CERTMGR_INIT_CONTEXT((&context), (sizeof(certmgr_ctx))) +// std::string storeName("Operator"); +// strncpy(context.storeId, storeName.c_str(), storeName.size()); +// +// certRetrieved.data = certBuff; +// certRetrieved.size = CERTMGR_MAX_BUFFER_SIZE * 2; +// +// certRetrieved.firstFree = 0; +// +// for(certRetrieved.firstFree = 0; +// OPERATION_SUCCESS == +// (result = certmgr_retrieve_certificate_from_store( &context, &certRetrieved, &certId)); +// certRetrieved.firstFree = 0) +// { +// if(OPERATION_SUCCESS == +// certmgr_extract_certificate_data(&certRetrieved, &descriptor)){ +// LogDebug("The subject of this certificate is " << descriptor.mandatory.subject); +// LogDebug("The issuer of this certificate is " << descriptor.mandatory.issuer); +// } +// +// if(strcmp(descriptor.mandatory.subject, subjectName) == 0 && +// strcmp(descriptor.mandatory.issuer,issuerName) == 0 && +// OPERATION_SUCCESS == certmgr_remove_certificate_from_store(&certId)) +// { +// LogDebug("***Certificate has been REMOVED***"); +// return OPERATION_SUCCESS; +// } +// } +// +// if(ERR_NO_MORE_CERTIFICATES == result){ +// LogDebug("***THIS CERT IS NOT IN STORE***"); +// return OPERATION_SUCCESS; +// } +// +// return result; +//} + +int removeCertGivenByFilename(const char* name){ + int result = cert_svc_delete_certificate_from_store(name, storePath); + + if (CERT_SVC_ERR_NO_ERROR != result) { + LogError("Error removing certificate: " << name << " From: " << storePath); + } + + return result; +} + diff --git a/tests/vcore/TestEnv.h b/tests/vcore/TestEnv.h new file mode 100644 index 0000000..88c5d3a --- /dev/null +++ b/tests/vcore/TestEnv.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _TESTENV_H_ +#define _TESTENV_H_ + +int addCertToStore(const char *name); +long int removeCertFromStore(const char *subjectName, const char *issuerName); +int removeCertGivenByFilename(const char *name); + +#endif diff --git a/tests/vcore/certificate-generator/create_certs.sh b/tests/vcore/certificate-generator/create_certs.sh new file mode 100755 index 0000000..4d03927 --- /dev/null +++ b/tests/vcore/certificate-generator/create_certs.sh @@ -0,0 +1,94 @@ +#!/bin/bash +# Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#Prerequisite to run script is to +#create root certificate and directory structure (demoCA) with command: +#/usr/lib/ssl/misc/CA.sh -newca +#for automated tests default structure is created in demoCA.init + +#make sure current dir has no files from previous generation +rm -r 1* 2* 3* ca* respcert* demoCA +cp -R demoCA.init demoCA +echo "01" > ./demoCA/crlnumber + +for index in 1 2 3 +do + #create certificate A + openssl genrsa -out ${index}second_level.key 1024 -passin pass:1234 -config ./openssl.cnf + openssl req -new -key ${index}second_level.key -out ${index}second_level.csr -passin pass:1234 -config ./openssl.cnf <zhl|$R*|yDUoYQ zkz1R>HeqskLxkdO=&bWO=XBmbzMt>q_j{hdp6839Lf1h+Aq*AT0)eR~+9a-v1ED}V z6tcri2mmWI1qwCfJM&BD~4WRvkE{U z5dfJA1}x~ge^hdObiup=OrB$+uuU?D7bi?@7H2XF6nvZ-@4NQ(<)Qk4%>{$eAw)1p zbp)4e?ai#M9wJPk4FjUkM-Xn2)^JbHS3&V~SI-}t=lHGCCuyRYUVh5_(O?S+d)M00 z;oG!_?!q_M3PbuCu`rYRI}?(~U@aZIT65*h_{l_-k%q&|8H)D84@ow_v_HP@sz(7m{*$UXGfYN=eYx+)2EC<1npfGi=h)75Y?b75x7E;}t}v*!vlwrd zb(1*aX&Zzelf^LM{5)-kcE^_}%93V{^0}jMn=>=TABgZjv3cDk6B_|6Mm8aHZ8iMv z^HdzJ3nxzzy3$0OWE=^pdlE4BaFo8eooh0(xw3%4wV%nJ$&#A9&PtYxRlj=U$VQhB zjXkGnvamGX7#3|Gb+2yivSW4LV^RvTR5YKETkN&ff8$#Poz#%Nm=S0wx)LNkI1+xs zSJOx2@Zf``@a?aM2Sr!Armhg;?}x=Ck7f-jqJJJ8BFzm3BaFAgKi{WUkV^$`}{LYqldXnf+4sCNzCB4-U!cbrNXBX>sc` zVkEwkvgvi?)VZP0wwYGPlenbdAfFX7fp&5~C!oz~rsYoF&rkcV8*1zVKVukXeRhD`DB7W9sQbO?E(EvR zlipM>RVZrvh*xLVO1@uXltO#q6@4H)BuRGMD5s+f#Z6$qgH@C%5$GHpf2N+stpQ~( zrI&ECLJfD(!jgV_YMd|kk(-cwLEs7xEX@U0zMedyUb-vrTcCJn1^oKH?GMBLlYaZ> z)Un1{nh~bik%b0nxASekusB_9ld~DjEJ9oJ$JORb+mK2fcxqurx9&v|t$Fw3e2V7{ z6KLf$T*7!vTIIP~l%1U}xl21o2qMHD+d14TX_^>f}S!zdjiE*zz) zhEJMa=Hlmol&qpD_tuz%CcOrePh*!PE0!_qu!?WD?$xv@@Lt*k0_Bq*96XL39=r1; zXnFp6y4NU7zaCa6lz4eNNFCgf^C8uZKm&3;?L2u#d;o@sp$fVGUt!RxLN;Kkkfk6# zEPx3_?7#IP3Ix%qAVKwjivH0mLcdzY6R}_FU8i~OuT}w5L1bbZ>MipRvrF3ZWq=em z&7xzVBVCh5a_N+-Wb`~oF86{h-7<(PX(yu3?7kT9ed*24D^dGOtJPVLBmFhd-=lRR z_)z^#_A0@QjI3)N#Pu5KFB>H8cBZwWo6i@HErTaigf*Wr3+`O?@2RTeh4QW`HjInB znP&KD$GnSiK5fNo2+`3t&ye{R{MKySzpAv{8)LAt6(Tk8`YQ4)?1wDXCilQ7a!9rP za{t*sSvyLG#Z3+E>QRMC#!oA55Mm_S0v^y8OIu=!)s-Rs3Q8)oe>u0X6Y&=PHIO%! z>8P(bZnwM^t*KX5*4pC+(xRuc2UCzCxDl} zYf?rt#}OMLoeCXesv}A5szn-$#_#3cz2@hxeor4RuMuN^Ntox6*&sx5qctZd=#=-q zLtvul2;+2;h_GE2G%-tK?PHznXO4r%T>{kjZ}rhW{Pd2ksB^65)>kQPp`nSaKKvm{ z@tY^+dzst=(tBSx;%dtoq%!UI2aRsBof4KhG?8be8uvB-v8%u|(xTaXHl^GZw zZ#*|WL6^V>Me?{+-w*46=nJt_;OK=O>^P@M*%K%v3UaNXYFIW37@ppTXswa}e5|#kp zD2rGM6|?_;&l)C;-3^vP>)^!PQL`n^Ao13MpMh5Ka(ikqr+zO0Gt590AD~T z;4B~<5F}s}AWY!T3v?jBN8kbkE>eI1fj%MNFu@)Qa8#f|1&Ro`Ai(*5pZ3LIF^U)% vL|6kM1dXx>p?B1Lw&a#HwUJ0;BIjpvXX_Yeb>+k#vvdo-i literal 0 HcmV?d00001 diff --git a/tests/vcore/test-cases/keys/root_cacert.pem b/tests/vcore/test-cases/keys/root_cacert.pem new file mode 100644 index 0000000..7aa429f --- /dev/null +++ b/tests/vcore/test-cases/keys/root_cacert.pem @@ -0,0 +1,64 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + c7:4a:82:f6:9d:1b:f6:7d + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=PL, ST=Maz, O=Samsung, OU=SPRC, CN=Samsung/emailAddress=samsung@samsung.com + Validity + Not Before: Oct 5 11:52:36 2011 GMT + Not After : Oct 4 11:52:36 2014 GMT + Subject: C=PL, ST=Maz, O=Samsung, OU=SPRC, CN=Samsung/emailAddress=samsung@samsung.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:93:c2:12:8b:3e:b1:69:fe:c8:7e:f1:fa:b0:03: + d7:bd:25:03:bb:14:70:ab:65:ff:8f:e9:38:14:2b: + 92:02:d9:e7:b4:78:60:a0:ce:b1:b8:b6:78:c5:af: + b3:83:3c:47:58:3d:1e:a0:78:69:4d:56:dd:8c:d8: + 20:27:b2:0d:9f:bf:f1:d4:e1:39:0f:1b:6f:b8:cd: + ca:f4:0b:fd:d7:cb:64:09:c7:6d:1e:e8:dd:89:43: + 7f:72:85:3d:9a:54:6e:7c:55:a0:da:f5:e9:28:01: + ec:3a:da:5a:18:45:fc:28:b1:0e:43:2c:4c:26:5c: + ca:bc:44:d9:ce:7d:5a:f2:f3 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 82:08:7F:DB:00:02:86:E8:53:2A:A5:FA:58:AE:67:7F:14:38:C8:60 + X509v3 Authority Key Identifier: + keyid:82:08:7F:DB:00:02:86:E8:53:2A:A5:FA:58:AE:67:7F:14:38:C8:60 + DirName:/C=PL/ST=Maz/O=Samsung/OU=SPRC/CN=Samsung/emailAddress=samsung@samsung.com + serial:C7:4A:82:F6:9D:1B:F6:7D + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 0f:cb:a3:cd:25:02:00:17:a9:c5:21:4a:6e:bb:ce:d9:14:74: + 23:29:c5:47:ff:02:91:5a:ee:a1:53:a7:e4:69:6f:f2:00:bc: + 09:87:80:f8:3b:a5:51:59:e9:20:1f:1d:5d:cb:91:eb:91:1e: + f4:79:bf:35:68:a5:ed:24:e5:28:dd:c9:1f:bf:53:f7:75:77: + 6c:fe:94:0c:de:9c:d9:8e:42:c6:7d:61:6b:5d:5d:ad:a7:6a: + e4:9b:53:2a:f7:85:9c:51:1d:72:5d:5c:2f:eb:f9:ff:80:4c: + 6d:46:e8:a0:2c:8a:6f:94:13:b2:00:47:2c:b0:b0:1c:12:fc: + a0:65 +-----BEGIN CERTIFICATE----- +MIIDOjCCAqOgAwIBAgIJAMdKgvadG/Z9MA0GCSqGSIb3DQEBBQUAMHIxCzAJBgNV +BAYTAlBMMQwwCgYDVQQIEwNNYXoxEDAOBgNVBAoTB1NhbXN1bmcxDTALBgNVBAsT +BFNQUkMxEDAOBgNVBAMTB1NhbXN1bmcxIjAgBgkqhkiG9w0BCQEWE3NhbXN1bmdA +c2Ftc3VuZy5jb20wHhcNMTExMDA1MTE1MjM2WhcNMTQxMDA0MTE1MjM2WjByMQsw +CQYDVQQGEwJQTDEMMAoGA1UECBMDTWF6MRAwDgYDVQQKEwdTYW1zdW5nMQ0wCwYD +VQQLEwRTUFJDMRAwDgYDVQQDEwdTYW1zdW5nMSIwIAYJKoZIhvcNAQkBFhNzYW1z +dW5nQHNhbXN1bmcuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCTwhKL +PrFp/sh+8fqwA9e9JQO7FHCrZf+P6TgUK5IC2ee0eGCgzrG4tnjFr7ODPEdYPR6g +eGlNVt2M2CAnsg2fv/HU4TkPG2+4zcr0C/3Xy2QJx20e6N2JQ39yhT2aVG58VaDa +9ekoAew62loYRfwosQ5DLEwmXMq8RNnOfVry8wIDAQABo4HXMIHUMB0GA1UdDgQW +BBSCCH/bAAKG6FMqpfpYrmd/FDjIYDCBpAYDVR0jBIGcMIGZgBSCCH/bAAKG6FMq +pfpYrmd/FDjIYKF2pHQwcjELMAkGA1UEBhMCUEwxDDAKBgNVBAgTA01hejEQMA4G +A1UEChMHU2Ftc3VuZzENMAsGA1UECxMEU1BSQzEQMA4GA1UEAxMHU2Ftc3VuZzEi +MCAGCSqGSIb3DQEJARYTc2Ftc3VuZ0BzYW1zdW5nLmNvbYIJAMdKgvadG/Z9MAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAD8ujzSUCABepxSFKbrvO2RR0 +IynFR/8CkVruoVOn5Glv8gC8CYeA+DulUVnpIB8dXcuR65Ee9Hm/NWil7STlKN3J +H79T93V3bP6UDN6c2Y5Cxn1ha11dradq5JtTKveFnFEdcl1cL+v5/4BMbUbooCyK +b5QTsgBHLLCwHBL8oGU= +-----END CERTIFICATE----- diff --git a/tests/vcore/test-cases/keys/root_cakey.pem b/tests/vcore/test-cases/keys/root_cakey.pem new file mode 100644 index 0000000..ff33c13 --- /dev/null +++ b/tests/vcore/test-cases/keys/root_cakey.pem @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,D2942E015452A445 + +0M/tC+qVDqyhNsxZZB/dGLuNxsStSUA7TRDRiSBee9JsvcFZjq03D/VjBakNIeET +6efmKfAngomfvrWtxhped3RI4vasX05swfnr4qiUKjLPyftmNEepbNGeI8MaiD7a +fEOZuAacstyS5RMHRBXrktq+jtXW2meQTuvEMBJf1AeAIuaFNvr3OvvpCtDwRffi +1ijUVuG0ZQ6MpP7cGeqLYUZ9StTMxeiEesAPM4YrG7HIY13KaHwMr1ZRtENe7qZ8 +R8vwgW188FYkSSrcQjCVEuj/ztTg9eVuSKdTNgjfKzTWnlrjAzi8CKBsrkYoarwS +6Rv3TqVVnx4HHdo9RIUKZPeLOdcMD1OPK7aOUedPTAcht3Y7SQQphBQypLf6PLKB +DCo79B4TUA1W9MijT2d2GN7oJHqHax8zO2j+yCLkEcHF32JZsEFE63Bwss72FXMg +mTmpCwyzR+oN93687JDUBAP9zNVd76ZnpzwlMZirB6QTY/lrH+iXLH3R3PO6cl2R +0Jei4IQ1oB+SX6GOPt4tKGTqktUFhsJYbXyifj4O1ZyDVYTp0JafOLxJfU33oYTm +278yshFdyHRgfKIHvqctZ1xJN2ioVcWf+9DprHc5kGb6wUKRVfQpipTS2hgbMBz+ +UWRZWq+CUD5QkTz4cSQfhPWQF6TNWpTQc0dvAlo3Cmxrro1PriDItsCOeydeNWvn +Dyynx7ODp/F1rX5ekaXkVxsdGgD/HuNF+c7tEytD7U4/CmevytuXRIrFM0alj2OE +aBFTqKicBoKgDV9VUOTKwuFeNV7MSuVDUnngEBeYrinwGa7wuV7tzA== +-----END RSA PRIVATE KEY----- diff --git a/tests/vcore/test-cases/widget/author-signature.xml b/tests/vcore/test-cases/widget/author-signature.xml new file mode 100644 index 0000000..ff82da8 --- /dev/null +++ b/tests/vcore/test-cases/widget/author-signature.xml @@ -0,0 +1,66 @@ + + + + + + + + xUKQbov3HL7JD2/zVUKpPEVGc5C6VWDXwxoDHzDs9y0= + + + + cIE41PzyhMnF++EmhJ3Ptnd4ZqXyBlRJgiIqxlutbV8= + + + + + + + MH34nIMXxv0fMQQ8bTV1wZUNLOrXTmpnxpADlNzmQ/4= + + + fhh+VQq76Uodq4upHhvcC2tgbVY8bL9DiiSe9wn1O4YrIFKMnEEYqYmpQbL1puWU +Zbht0hXpvEFXg1010q5kOZQxknqcyFg3hyVUpFDPARkJs1XhRNbFWJJF7qNXVgt5 +NyFrdXFv4lVFjkv+chSykaWu6V22z43E8kJcg+zGVU8= + + + MIIETTCCA7agAwIBAgIJANaOuOCRgiz3MA0GCSqGSIb3DQEBBQUAMIG8MQswCQYD +VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3Vy +aXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEeMBwG +A1UECxMVVGVzdCBSb290IENlcnRpZmljYXRlMRYwFAYDVQQDEw1BbGVrc2V5IFNh +bmluMSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5jb20wHhcNMDUwNzEw +MDIyOTAxWhcNMTUwNzA4MDIyOTAxWjCBvDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +CkNhbGlmb3JuaWExPTA7BgNVBAoTNFhNTCBTZWN1cml0eSBMaWJyYXJ5IChodHRw +Oi8vd3d3LmFsZWtzZXkuY29tL3htbHNlYykxHjAcBgNVBAsTFVRlc3QgUm9vdCBD +ZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxla3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJ +ARYSeG1sc2VjQGFsZWtzZXkuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB +gQDayaFajJxOdVU+8EjwO31S2XqNmYxxbHfiUJO3w2h57OPUkKAcKe5Gvt9hJbPT +b3C4blPScOke2RexKnXS7pAXXbxFlgUlZ0QK0K2pdl559OSmrtH3mPP9BJvvDMlx +kcNj9/EeD+yGd8GN/yT6PTDh8G/4lszOXL+tyKIkC4Ys/wIDAQABo4IBUzCCAU8w +DAYDVR0TBAUwAwEB/zAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQg +Q2VydGlmaWNhdGUwHQYDVR0OBBYEFNpG6Wvmr9M9quUhS1LtymYo4P6FMIHxBgNV +HSMEgekwgeaAFNpG6Wvmr9M9quUhS1LtymYo4P6FoYHCpIG/MIG8MQswCQYDVQQG +EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3VyaXR5 +IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEeMBwGA1UE +CxMVVGVzdCBSb290IENlcnRpZmljYXRlMRYwFAYDVQQDEw1BbGVrc2V5IFNhbmlu +MSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5jb22CCQDWjrjgkYIs9zAN +BgkqhkiG9w0BAQUFAAOBgQBUXbdOTQwArcNrbxavzARp2JGOnzo6WzTm+OFSXC0F +08YwT8jWbht97e8lNNVOBU4Y/38ReZqYC9OqFofG1/O9AdQ58WL/FWg8DgP5MJPT +T9kRU3FU01jUiX2+kbdnghZAOJm0ziRNxfNPwIIWPKYXyXEKQQzrnxyFey1hP7cg +6A== + + + + + + + + + + + + + + + + diff --git a/tests/vcore/test-cases/widget/config.xml b/tests/vcore/test-cases/widget/config.xml new file mode 100755 index 0000000..82b077b --- /dev/null +++ b/tests/vcore/test-cases/widget/config.xml @@ -0,0 +1,6 @@ + + Widget Name OK + 1.2.3.4 + A short description of widget + Author Name + diff --git a/tests/vcore/test-cases/widget/index.html b/tests/vcore/test-cases/widget/index.html new file mode 100755 index 0000000..c47b20a --- /dev/null +++ b/tests/vcore/test-cases/widget/index.html @@ -0,0 +1,4 @@ + +Not tested + +

None

diff --git a/tests/vcore/test-cases/widget/signature1.xml b/tests/vcore/test-cases/widget/signature1.xml new file mode 100644 index 0000000..71a100b --- /dev/null +++ b/tests/vcore/test-cases/widget/signature1.xml @@ -0,0 +1,62 @@ + + + + + + + + ZLhd8X2rzCIDGHkIvpDbCXq+dwq+DK7ZZaDD/fII8RU= + + + + xUKQbov3HL7JD2/zVUKpPEVGc5C6VWDXwxoDHzDs9y0= + + + + cIE41PzyhMnF++EmhJ3Ptnd4ZqXyBlRJgiIqxlutbV8= + + + + + + + ZxnfFPi1rAoxfpN98xSP3lv5tZg9ymJElAFdg3ejrXE= + + + Dwm15jQbvUxe7fa7p4RVRAUzYY6eGQmDJSWXnv2LBbouch163OMaXgjKXWOLU+ZA +MwwuUUXG44QvOIv5M3Kd/Pc6kwvyb9+xm8zqmFF/mhttmAHc7VjY5sfB+bYFt9/3 +8+upSqxiUGLXYzMD/9u4W9ociwAcLiOQytBF1/TCv/4= + + + MIIC4zCCAkygAwIBAgIJAMdKgvadG/Z+MA0GCSqGSIb3DQEBBQUAMHIxCzAJBgNV +BAYTAlBMMQwwCgYDVQQIEwNNYXoxEDAOBgNVBAoTB1NhbXN1bmcxDTALBgNVBAsT +BFNQUkMxEDAOBgNVBAMTB1NhbXN1bmcxIjAgBgkqhkiG9w0BCQEWE3NhbXN1bmdA +c2Ftc3VuZy5jb20wHhcNMTExMDA1MTIwMDUxWhcNMjExMDAyMTIwMDUxWjB4MQsw +CQYDVQQGEwJQTDEMMAoGA1UECBMDTUFaMQwwCgYDVQQHEwNMZWcxDDAKBgNVBAoT +A1NhbTENMAsGA1UECxMEU1BSQzEOMAwGA1UEAxMFRmlsaXAxIDAeBgkqhkiG9w0B +CQEWEWZpbGlwQHNhbXN1bmcuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB +gQDS/sS0wXSCb34ojN8bWFd4Pl9eTLHh18UNGsPpLpp4itdfuc/OgyqaSoDwBzVh +EWAVLCTxexUa4Ncva+41NbkW4RCsFzeGs0ktpu1+8Q+v0QEOGqVF2rQkgilzDF/o +O56Fxw9vG1OA+qdQd3yOAV2EqLNBPrEYB9K5GFyffrakSQIDAQABo3sweTAJBgNV +HRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZp +Y2F0ZTAdBgNVHQ4EFgQUeyy3iV75KtOkpPFd6mnR9dFGZMwwHwYDVR0jBBgwFoAU +ggh/2wAChuhTKqX6WK5nfxQ4yGAwDQYJKoZIhvcNAQEFBQADgYEADtv0CBrQ1QCM +H9jKFjpSpq7zFKMXQeVtb/Zie823//woicg8kxnP5sS4dJWNXNb1iMLdhgV80g1y +t3gTWPxTtFzprQyNiJHTmrbNWXLX1roRVGUE/I8Q4xexqpbNlJIW2Jjm/kqoKfnK +xORG6HNPXZV29NY2fDRPPOIYoFQzrXI= + + + + + + + + + + + + + + + + diff --git a/tests/vcore/test-cases/widget/signature22.xml b/tests/vcore/test-cases/widget/signature22.xml new file mode 100644 index 0000000..715a7cc --- /dev/null +++ b/tests/vcore/test-cases/widget/signature22.xml @@ -0,0 +1,66 @@ + + + + + + + + ZLhd8X2rzCIDGHkIvpDbCXq+dwq+DK7ZZaDD/fII8RU= + + + + xUKQbov3HL7JD2/zVUKpPEVGc5C6VWDXwxoDHzDs9y0= + + + + cIE41PzyhMnF++EmhJ3Ptnd4ZqXyBlRJgiIqxlutbV8= + + + + + + + ZxnfFPi1rAoxfpN98xSP3lv5tZg9ymJElAFdg3ejrXE= + + + fV1J/120GG5L7qsxEkyH6fBvQh2atlpiGMbVM1+pb8Q6pHib5beV6A== + + + MIIEDzCCA3igAwIBAgIJAMdKgvadG/Z/MA0GCSqGSIb3DQEBBQUAMHIxCzAJBgNV +BAYTAlBMMQwwCgYDVQQIEwNNYXoxEDAOBgNVBAoTB1NhbXN1bmcxDTALBgNVBAsT +BFNQUkMxEDAOBgNVBAMTB1NhbXN1bmcxIjAgBgkqhkiG9w0BCQEWE3NhbXN1bmdA +c2Ftc3VuZy5jb20wHhcNMTExMDA1MTIxMTMzWhcNMjExMDAyMTIxMTMzWjCBijEL +MAkGA1UEBhMCUEwxFDASBgNVBAgTC01hem93aWVja2llMRIwEAYDVQQHEwlsZWdp +b25vd28xEDAOBgNVBAoTB3NhbXN1bmcxDTALBgNVBAsTBHNwcmMxDjAMBgNVBAMT +BW1hZ2RhMSAwHgYJKoZIhvcNAQkBFhFtYWdkYUBzYW1zdW5nLmNvbTCCAbcwggEr +BgcqhkjOOAQBMIIBHgKBgQC1PCOasFhlfMc1yjdcp7zkzXGiW+MpVuFlsdYwkAa9 +sIvNrQLi2ulxcnNBeCHKDbk7U+J3/QwO2XanapQMUqvfjfjL1QQ5Vf7ENUWPNP7c +Evx82Nb5jWdHyRfV//TciBZN8GLNEbfhtWlhI6CbDW1AaY0nPZ879rSIk7/aNKZ3 +FQIVALcr8uQAmnV+3DLIA5nTo0Bg0bjLAoGAJG7meUtQbMulRMdjzeCoya2FXdm+ +4acvInE9/+MybXTB3bFANMyw6WTvk4K9RK8tm52N95cykTjpAbxqTMaXwkdWbOFd +VKAKnyxi/UKtY9Q6NmwJB2hbA1GUzhPko8rEda66CGl0VbyM1lKMJjA+wp9pG110 +L0ov19Q9fvqKp5UDgYUAAoGBAKxAQg7MqCgkC0MJftYjNaKM5n1iZv4j1li49zKf +Y5nTLP+vYAvg0owLNYvJ5ncKfY1DACPU4/+tC7TTua95wgj5rwvAXnzgSyOGuSr0 +fK9DyrH6E0LfXT+WuIQHahm2iSbxqPrChlnp5/EXDTBaO6Qfdpq0BP48ClZebxcA ++TYFo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVy +YXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUmSpShswvWtEABd+l3WxccRcCydUw +HwYDVR0jBBgwFoAUggh/2wAChuhTKqX6WK5nfxQ4yGAwDQYJKoZIhvcNAQEFBQAD +gYEAgfnAu/gMJRC/BFwkgvrHL0TV4ffPVAf7RSnZS6ib4IHGgrvXJvL+Qh7vHykv +ZIqD2L96nY2EaSNr0yXrT81YROndOQUJNx4Y/W8m6asu4hzANNZqWCbApPDIMK6V +cPA1wrKgZqbWp218WBqI2v9pXV0O+jpzxq1+GeQV2UsbRwc= + + + + + + + + + + + + + + + + diff --git a/tests/vcore/vcore_tests.cpp b/tests/vcore/vcore_tests.cpp new file mode 100644 index 0000000..02517fc --- /dev/null +++ b/tests/vcore/vcore_tests.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * @file main.cpp + * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of main + */ +#include +#include + +#include // includes headers with g_type_init + +int main (int argc, char *argv[]) +{ + g_type_init(); + g_thread_init(NULL); + ValidationCore::AttachToThread(); + int status = DPL::Test::TestRunnerSingleton::Instance().ExecTestRunner(argc, argv); + ValidationCore::DetachFromThread(); + + return status; +} + -- 2.7.4