From 2a844d3205382edf65592938643b155cdb63f43d Mon Sep 17 00:00:00 2001 From: fengweihao Date: Thu, 6 Sep 2018 19:51:23 +0800 Subject: [PATCH] =?UTF-8?q?1.=E6=B7=BB=E5=8A=A0=E6=89=AB=E6=8F=8F=E6=A1=86?= =?UTF-8?q?=E6=9E=B6maat,=E6=A0=B9=E6=8D=AEjson=E6=96=87=E4=BB=B6=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96keyring=E9=93=BE=202.=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=BA=90=E8=AF=81=E4=B9=A6=E6=97=B6=E7=AD=BE=E5=8F=91=E6=B5=81?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ca/ca.cer | 15 - ca/mesalab-ca-cert.cer | 22 + ca/mesalab-ca-cert.key | 28 + ca/private.key | 15 - conf/cert_store.ini | 6 +- conf/pxy_obj_keyring.json | 85 ++ conf/table_info.conf | 20 + src/Makefile | 11 +- src/{cert_init.c => cert_conf.c} | 23 +- src/{cert_init.h => cert_conf.h} | 43 +- src/cert_session.c | 738 ++++++++++++----- src/cert_store.c | 11 +- src/components/json/arraylist.c | 101 +++ src/components/json/arraylist.h | 56 ++ src/components/json/bits.h | 28 + src/components/json/config.h | 175 ++++ src/components/json/debug.c | 83 ++ src/components/json/debug.h | 71 ++ src/components/json/json.h | 34 + src/components/json/json.mk | 45 ++ src/components/json/json_c_version.c | 20 + src/components/json/json_c_version.h | 22 + src/components/json/json_checker.c | 421 ++++++++++ src/components/json/json_checker.h | 39 + src/components/json/json_config.h | 4 + src/components/json/json_inttypes.h | 28 + src/components/json/json_object.c | 868 ++++++++++++++++++++ src/components/json/json_object.h | 617 ++++++++++++++ src/components/json/json_object_iterator.c | 168 ++++ src/components/json/json_object_iterator.h | 239 ++++++ src/components/json/json_object_private.h | 47 ++ src/components/json/json_tokener.c | 888 +++++++++++++++++++++ src/components/json/json_tokener.h | 208 +++++ src/components/json/json_util.c | 300 +++++++ src/components/json/json_util.h | 41 + src/components/json/libjson.c | 26 + src/components/json/linkhash.c | 602 ++++++++++++++ src/components/json/linkhash.h | 292 +++++++ src/components/json/math_compat.h | 28 + src/components/json/parse_flags.c | 50 ++ src/components/json/parse_flags.h | 4 + src/components/json/printbuf.c | 192 +++++ src/components/json/printbuf.h | 77 ++ src/components/json/random_seed.c | 237 ++++++ src/components/json/random_seed.h | 25 + src/inc/Maat_command.h | 165 ++++ src/inc/Maat_rule.h | 244 ++++++ src/inc/gram_index_engine.h | 68 ++ src/inc/inc.mk | 1 + src/inc/moodycamel_maat_rule.cpp | 66 ++ src/inc/moodycamel_maat_rule.h | 67 ++ src/inc/stream_fuzzy_hash.h | 78 ++ src/lib/libMESA_htable.a | Bin 138390 -> 0 bytes src/lib/libMESA_htable.so | Bin 0 -> 155176 bytes 54 files changed, 7468 insertions(+), 274 deletions(-) delete mode 100644 ca/ca.cer create mode 100644 ca/mesalab-ca-cert.cer create mode 100644 ca/mesalab-ca-cert.key delete mode 100644 ca/private.key create mode 100644 conf/pxy_obj_keyring.json create mode 100644 conf/table_info.conf rename src/{cert_init.c => cert_conf.c} (77%) rename src/{cert_init.h => cert_conf.h} (64%) create mode 100644 src/components/json/arraylist.c create mode 100644 src/components/json/arraylist.h create mode 100644 src/components/json/bits.h create mode 100644 src/components/json/config.h create mode 100644 src/components/json/debug.c create mode 100644 src/components/json/debug.h create mode 100644 src/components/json/json.h create mode 100644 src/components/json/json.mk create mode 100644 src/components/json/json_c_version.c create mode 100644 src/components/json/json_c_version.h create mode 100644 src/components/json/json_checker.c create mode 100644 src/components/json/json_checker.h create mode 100644 src/components/json/json_config.h create mode 100644 src/components/json/json_inttypes.h create mode 100644 src/components/json/json_object.c create mode 100644 src/components/json/json_object.h create mode 100644 src/components/json/json_object_iterator.c create mode 100644 src/components/json/json_object_iterator.h create mode 100644 src/components/json/json_object_private.h create mode 100644 src/components/json/json_tokener.c create mode 100644 src/components/json/json_tokener.h create mode 100644 src/components/json/json_util.c create mode 100644 src/components/json/json_util.h create mode 100644 src/components/json/libjson.c create mode 100644 src/components/json/linkhash.c create mode 100644 src/components/json/linkhash.h create mode 100644 src/components/json/math_compat.h create mode 100644 src/components/json/parse_flags.c create mode 100644 src/components/json/parse_flags.h create mode 100644 src/components/json/printbuf.c create mode 100644 src/components/json/printbuf.h create mode 100644 src/components/json/random_seed.c create mode 100644 src/components/json/random_seed.h create mode 100644 src/inc/Maat_command.h create mode 100644 src/inc/Maat_rule.h create mode 100644 src/inc/gram_index_engine.h create mode 100644 src/inc/moodycamel_maat_rule.cpp create mode 100644 src/inc/moodycamel_maat_rule.h create mode 100644 src/inc/stream_fuzzy_hash.h delete mode 100644 src/lib/libMESA_htable.a create mode 100644 src/lib/libMESA_htable.so diff --git a/ca/ca.cer b/ca/ca.cer deleted file mode 100644 index b176567..0000000 --- a/ca/ca.cer +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICVTCCAb4CCQCUgj5wb1+b4DANBgkqhkiG9w0BAQsFADBvMQswCQYDVQQGEwJD -TjETMBEGA1UECAwKbXlwcm92aW5jZTEPMA0GA1UEBwwGbXljaXR5MRcwFQYDVQQK -DA5teW9yZ2FuaXphdGlvbjEQMA4GA1UECwwHbXlncm91cDEPMA0GA1UEAwwGbXlu -YW1lMB4XDTE4MDcyMzExMDI1OVoXDTE5MDcyMzExMDI1OVowbzELMAkGA1UEBhMC -Q04xEzARBgNVBAgMCm15cHJvdmluY2UxDzANBgNVBAcMBm15Y2l0eTEXMBUGA1UE -CgwObXlvcmdhbml6YXRpb24xEDAOBgNVBAsMB215Z3JvdXAxDzANBgNVBAMMBm15 -bmFtZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA46NKH1YIJaXcdfXbOv7U -zF1G0DM51378idVy3N3DgtXqgY999zFEHPDfqyRhkWVj7JxrEcv8cN3P9o8YizNH -rwNehv2XKYco0aShuXdC1cimtsdUvPcTN0xQHC0TJSML35JjVqgyJPAEIny295Yb -mK2UEwCsK4ZSJ84eZnuO/6kCAwEAATANBgkqhkiG9w0BAQsFAAOBgQAs0VlruE+Q -85oBnojVnBRxLWg2O+FR/FZ1v3+trRg4LwnyEwIKHNTn781WPqYIDbuOG1SSQNPV -zN9VJLSmEpFXawqJa42+2RLG8dAtWngC06oILRE9PODJO8xwoskdJBsy2+RGidMg -2w46j6BiP+d/DL+8NcJRmISQybhQ/Rn00A== ------END CERTIFICATE----- diff --git a/ca/mesalab-ca-cert.cer b/ca/mesalab-ca-cert.cer new file mode 100644 index 0000000..d0d32af --- /dev/null +++ b/ca/mesalab-ca-cert.cer @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDpTCCAo2gAwIBAgIGDdgTuLYiMA0GCSqGSIb3DQEBCwUAMCoxEzARBgNVBAMM +Cm1lc2FsYWItY2ExEzARBgNVBAoMCm1lc2FsYWItY2EwHhcNMTgwMzI1MTY1MTM2 +WhcNMjEwMzI2MTY1MTM2WjAqMRMwEQYDVQQDDAptZXNhbGFiLWNhMRMwEQYDVQQK +DAptZXNhbGFiLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwnLT +pA+2Xef0VwKohbmr7ETuYcBm5YypXuANlEYApxhSdCvJZaGWznlDPL4EANTzM3g/ +3S3w8ms5p4B+uZnUE44EfmGl/UmmoL09k2/mj8/auOgdp0LTEOIpVzco8eIF2iGP +G3jYwscDvOAjxv/k6l/YBohbG8oH+wCVz0bI1j97VxiBx5M/frrZtLqRTIedtOAB +5S8VgtCa/rhik9aC8YA14UAnQSmVMsAZfuThSlCPb8h1ZnCfb1xJ7joHvbWh+L8O +29oiWzBEN/uIw/qjiWQ1aVCES8kJk93+gpwG5qNbq8DGupJzTugWztzCZogMMotF +L/QroMoFaPScBx6yewIDAQABo4HQMIHNMA8GA1UdEwEB/wQFMAMBAf8wEQYJYIZI +AYb4QgEBBAQDAgIEMHgGA1UdJQRxMG8GCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYB +BQUHAwQGCCsGAQUFBwMIBgorBgEEAYI3AgEVBgorBgEEAYI3AgEWBgorBgEEAYI3 +CgMBBgorBgEEAYI3CgMDBgorBgEEAYI3CgMEBglghkgBhvhCBAEwDgYDVR0PAQH/ +BAQDAgEGMB0GA1UdDgQWBBSQJL1m7FTdhYC0Odubg/8ebnloLTANBgkqhkiG9w0B +AQsFAAOCAQEAaVPocMiqwZK/0tROUz/W23DwGC+npZOyhAuGRze5YBV+zpVBhPv5 +8MeDkUr5jcoN8Papt5uq+6EHv+8fbVPTWBQRNuJD/WZ+CLkWTmDCyc+vbdXfsrRD +i135Q+Q72oyEsLUbZMaYvNQ2tJ4Pb0Qjwcc5GSDXJJFhwqIPa9eYiZwRcg/cUvps +ATgdZ5mZl1AfaINtXO1Y9Ic8PJcUotPSJ+YoG08dkAYrvo9Jc/n63ZOvnj0HVqBA +JgWKjwoxNv1BiU2vEI6KBGO76hBidvcBHSnpvKSfiKwbMSp3Kai/+MHnVBfgp3yo +WgeGkqyqiYEAZImAh/ps02XqtPWj9Sl2zQ== +-----END CERTIFICATE----- diff --git a/ca/mesalab-ca-cert.key b/ca/mesalab-ca-cert.key new file mode 100644 index 0000000..9522943 --- /dev/null +++ b/ca/mesalab-ca-cert.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDCctOkD7Zd5/RX +AqiFuavsRO5hwGbljKle4A2URgCnGFJ0K8lloZbOeUM8vgQA1PMzeD/dLfDyazmn +gH65mdQTjgR+YaX9SaagvT2Tb+aPz9q46B2nQtMQ4ilXNyjx4gXaIY8beNjCxwO8 +4CPG/+TqX9gGiFsbygf7AJXPRsjWP3tXGIHHkz9+utm0upFMh5204AHlLxWC0Jr+ +uGKT1oLxgDXhQCdBKZUywBl+5OFKUI9vyHVmcJ9vXEnuOge9taH4vw7b2iJbMEQ3 ++4jD+qOJZDVpUIRLyQmT3f6CnAbmo1urwMa6knNO6BbO3MJmiAwyi0Uv9CugygVo +9JwHHrJ7AgMBAAECggEAQ/ZSVpNPUD8UPZ0mPacJmgj1sKDI1g513D0/QcW90KlF +mGj9eVIjHYNwprhbOdc0MZcj6zB1eKVVf1//6usDHtqSY4HJvF+Tp7a84N1JnpYt +gldOlflbQBlsDZmv6+rt1LHKDPYN/PYGLmvA1Xr3DZv2K0JZZbsVUvt/YPUCmS72 +/Br1keFlvKeKdFRxFHznkLgE/5ZjtcxrwFc6pbp4LFyG1SzbQ655+XeXR/08Khi4 +Lsj2Xf9P7Yk8hgOVhx8+GRiR33Zoi5SiKvvnhXkR6QWzUvvnp6pqNbTDy2os9OFH +nmlyMbTSbm9gk1JHw3xMbrPLtxx9T3tkZGhox33UoQKBgQDgLbK6vp9eZZQyF2is +42YFYVgC2g3QDd+e34pN+1q581DkTJ75t3e/CosX1R6ApDnDmkfQDhLRlPmkCrH0 +Z+M7cjDzhPbDGcwgO/ag21osre6zWWJsDK6e64T1a3RmA4W13Nmyu+UCZSp/k0ng +Te+jzdar8HZpeCu7FtxXrfdmIwKBgQDeDMmPueeF1WMa6KMJknA1CrwUgYlZ4nc8 +wYNjSYAq0b2k73M9OR4oxYEm843HrXpOIXFMpA38M7yMSBIVURMYtrd4TUvBwwVY +/GBA94d1g91xKAMTiPRDRYpCvB1R56xLQ0ddXULAm2Xvt6QxrC+1/TZNzJOAn0z/ +JwNauVQLyQKBgQC5J+VT6jeU7s8M5Fq3WQYdcX4QtOrtqVfGT5lauT0BEp8AQOyZ +EdiceGfTolmUJI/1J4sio00VvzbFL3Q1ikya/8DAkVSCZd87zGryBtoexvW9OhlZ +ZswfRCVH0p2L2GLqh2NjBV+rr8T/I7bDxXslTtB8qJoUmIV9++63mF8bAwKBgQC+ +GKBuZS8qSlZ/8O2zAiUBo+EEhSk7RD/kSZ7b307UWZ9LlptHrKB/MyawXA3jBkcQ +oFzIyiIW6YvfZMvmZ/Q7UiGb4kCa7wSi+9zDgaX8Gxn3B9QqYzMKbHxDSZyoQ/gi +rsRnz7GYBvGr2cG9rLVjzhUxYZRdpwNZ5OJgRw0G0QKBgQC0nQiEHJ+c5PV2JcSB +S5ux2yjbB0TcM7iOLvCy5tpd8w5paGsJHqNR93o/lB82A6nn7QO1vj6M3CU04SwC +X74noxaiys0huVTMfJ3PrAm2AEE9jWlkI2X2F7s6sraSdcKKHlRQv+SI5X936nxF +2W/lCSj77xdiebatxFUlJT7O7g== +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/ca/private.key b/ca/private.key deleted file mode 100644 index 75fc6b2..0000000 --- a/ca/private.key +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXgIBAAKBgQDjo0ofVgglpdx19ds6/tTMXUbQMznXfvyJ1XLc3cOC1eqBj333 -MUQc8N+rJGGRZWPsnGsRy/xw3c/2jxiLM0evA16G/ZcphyjRpKG5d0LVyKa2x1S8 -9xM3TFAcLRMlIwvfkmNWqDIk8AQifLb3lhuYrZQTAKwrhlInzh5me47/qQIDAQAB -AoGBAKXM61IDoY96TScF2ZYQwgHP9qHyjbCt51alRzIjvCFxmYqgbwk6sve5YdAP -gZkbFjriewHNZ6L1jGFzPFc3FH++8WF1ThhGs4rAfe4rexA2gx1XZLqy+UPLECiK -/xebOwarLSQoB9V6A+quLU1CD/rNt2IeQL3N5LNBlDlwn2LhAkEA8+R2Ib+xZ+hn -CrWAdiEONfOVdNWMwfyAaMC3DlHjMAYuWEIBcTXQui8L6ddv5JkxPw3Z8Aae72ff -09OtnjGrFQJBAO7wQTKYycETzzkCOqYPiT70Mg1gnk/9cIjcRWhWhNXofxIZ9PaQ -kP71+z47ydAB/0Wq5Xe7DgHficUoVCnZF0UCQEjY6WwFLMEjinuJYPhnwS3eNrrx -+hwLBnPDdCnjzZ8PiZI1DOc6ssCZws4y2ioGk84Inhryb1CEzzcfF9GTdk0CQQDo -6XHkbGNevnylSbL55PMYVtnjiGdJ+fcUsgNGbfAWxAf6EStkng95OTart4RGK2w2 -8Ru11rUUxl55vZItKN0xAkEAsLEqmoX/hl2PO807nQEAsDlWCsTRGawl/hz2Gq+n -boD5yf2eW3n51Rn60cGgrInu1VifVamlQJq4zwdvJ2zjcg== ------END RSA PRIVATE KEY----- diff --git a/conf/cert_store.ini b/conf/cert_store.ini index a40f20a..fccbe77 100644 --- a/conf/cert_store.ini +++ b/conf/cert_store.ini @@ -6,11 +6,13 @@ DEBUG_SWITCH = 1 RUN_LOG_LEVEL = 20 RUN_LOG_PATH = ./logs [CONFIG] -thread-nu = 1 +thread-nu = 4 +table_info = ../conf/table_info.conf +pxy_obj_keyring = ../conf/pxy_obj_keyring.json ca-path = ../ca valid-days = 30 [LIBEVENT] -port = 9995 +port = 9991 [REDIS] ip = 127.0.0.1 port = 6379 diff --git a/conf/pxy_obj_keyring.json b/conf/pxy_obj_keyring.json new file mode 100644 index 0000000..b81507e --- /dev/null +++ b/conf/pxy_obj_keyring.json @@ -0,0 +1,85 @@ +{ + "compile_table": "COMPILE", + "group_table": "GROUP", + "rules": [ + { + "compile_id": 123, + "service": 1, + "action": 1, + "do_blacklist": 1, + "do_log": 1, + "effective_range": 0, + "user_region": "escaped\\bdata:have\\ba\\bspace\\band\\ba\\b\\&\\bsymbol.", + "is_valid": "yes", + "groups": [ + { + "group_name": "IP_group", + "regions": [ + { + "table_name": "IP_CONFIG", + "table_type": "ip", + "table_content": { + "addr_type": "ipv4", + "src_ip": "10.0.6.201", + "mask_src_ip": "255.255.0.0", + "src_port": "0", + "mask_src_port": "65535", + "dst_ip": "0.0.0.0", + "mask_dst_ip": "255.255.255.255", + "dst_port": "0", + "mask_dst_port": "65535", + "protocol": 6, + "direction": "double" + } + }, + { + "table_name": "IP_CONFIG", + "table_type": "ip", + "table_content": { + "addr_type": "ipv6", + "src_ip": "2001:da8:205:1::101", + "mask_src_ip": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:0000", + "src_port": "0", + "mask_src_port": "65535", + "dst_ip": "0::0", + "mask_dst_ip": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + "dst_port": "0", + "mask_dst_port": "65535", + "protocol": 6, + "direction": "double" + } + } + ] + }, + { + "group_name": "Untitled", + "regions": [ + { + "table_name": "HTTP_URL", + "table_type": "string", + "table_content": { + "keywords": "abckkk&123", + "expr_type": "and", + "match_method": "sub", + "format": "uncase plain" + } + } + ] + } + ] + } + ], + "plugin_table": [ + { + "table_name": "PXY_OBJ_KEYRING", + "table_content": [ + "1\t1\tname_01\troot\t/home/fengweihao/workspace/cert_store/ca/mesalab-ca-cert.key\t/home/fengweihao/workspace/cert_store/ca/mesalab-ca-cert.cer\t30\trsa2048\tnull\t1", + "2\t1\tname_02\troot\t/home/fengweihao/workspace/cert_store/ca/mesalab-ca-cert.key\t/home/fengweihao/workspace/cert_store/ca/mesalab-ca-cert.cer\t30\trsa2048\tnull\t1", + "3\t1\tname_03\troot\t/home/fengweihao/workspace/cert_store/ca/mesalab-ca-cert.key\t/home/fengweihao/workspace/cert_store/ca/mesalab-ca-cert.cer\t30\trsa2048\tnull\t1", + "4\t1\tname_04\tintermediate\t/home/fengweihao/workspace/cert_store/ca/mesalab-ca-cert.key\t/home/fengweihao/workspace/cert_store/ca/mesalab-ca-cert.cer\t30\trsa2048\tnull\t1", + "5\t1\tname_05\tintermediate\t/home/fengweihao/workspace/cert_store/ca/mesalab-ca-cert.key\t/home/fengweihao/workspace/cert_store/ca/mesalab-ca-cert.cer\t30\trsa2048\tnull\t1", + "6\t1\tname_06\tintermediate\t/home/fengweihao/workspace/cert_store/ca/mesalab-ca-cert.key\t/home/fengweihao/workspace/cert_store/ca/mesalab-ca-cert.cer\t30\trsa2048\tnull\t1" + ] + } + ] +} diff --git a/conf/table_info.conf b/conf/table_info.conf new file mode 100644 index 0000000..c04b50c --- /dev/null +++ b/conf/table_info.conf @@ -0,0 +1,20 @@ +#each collumn seperate with '\t' +#id (0~65535) +#name string +#type one of ip,expr,expr_plus,digest,intval,compile or plugin +#src_charset one of GBK,BIG5,UNICODE,UTF8 +#dst_charset combined by GBK,BIG5,UNICODE,UTF8,seperate with '/' +#do_merege [yes/no] +#cross cache [number] +#quick mode [quickon/quickoff], default [quickoff] +#For ip/intval/digest/compile/group +#id name type +# +#For plugin table +#id name type valid_column +# +#For expr/expr_plus Table +#id name type src_charset dst_charset do_merge cross_cache quick_mode +1 COMPILE compile +2 GROUP group +3 PXY_OBJ_KEYRING plugin 8 diff --git a/src/Makefile b/src/Makefile index 3757432..85c015b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -28,7 +28,7 @@ LIB_PATH := $(CERT_ROOT)/libs dir := . OBJS := \ $(OBJ_DIR)/cert_store.o \ - $(OBJ_DIR)/cert_init.o \ + $(OBJ_DIR)/cert_conf.o \ $(OBJ_DIR)/cert_daemon.o\ $(OBJ_DIR)/cert_session.o\ @@ -36,6 +36,10 @@ dir := ./components/syslogd include $(dir)/syslog.mk OBJS += $(OBJS_$(dir)) +dir := ./components/json +include $(dir)/json.mk +OBJS += $(OBJS_$(dir)) + dir := ./rt include $(dir)/rt.mk OBJS += $(OBJS_$(dir)) @@ -44,10 +48,10 @@ dir := ./inc include $(dir)/inc.mk OBJS += $(OBJS_$(dir)) -LDFLAGS_GLOBAL += -L ./lib -lapps -lcrypto -lssl -levent -lhiredis -lMESA_htable +LDFLAGS_GLOBAL += -L ./lib -lapps -lcrypto -lssl -levent -lhiredis LDFLAGS_GLOBAL += -L ./lib -lMESA_htable -lMESA_field_stat2 -lMESA_handle_logger -lMESA_prof_load LDFLAGS_GLOBAL += \ - -lpthread -lcrypt -lm -lz -ldl -lstdc++ + -lpthread -lcrypt -lmaatframe -lm -lz -ldl -lstdc++ CFLAGS_LOCAL = -std=gnu99 -g -O3 -W -Wall \ -I.\ @@ -55,6 +59,7 @@ CFLAGS_LOCAL = -std=gnu99 -g -O3 -W -Wall \ -I./components/libevent\ -I./components/redis\ -I./components/openssl\ + -I./components/json\ -I./rt \ -I./inc \ diff --git a/src/cert_init.c b/src/cert_conf.c similarity index 77% rename from src/cert_init.c rename to src/cert_conf.c index bf614bd..fabe6cb 100644 --- a/src/cert_init.c +++ b/src/cert_conf.c @@ -8,18 +8,19 @@ #include #include #include +#include #include "rt_string.h" #include "rt_common.h" #include "rt_file.h" -#include "cert_init.h" +#include "cert_conf.h" #include "logging.h" + #include "MESA_prof_load.h" struct config_bucket_t certConfig = { .thread_nu = 1, .days = 30, - .ca_path = "/usr/local/bin/", .e_port = 9995, .r_ip = "0.0.0.0", .r_port = 3366, @@ -41,19 +42,21 @@ static int load_system_config(char *config) mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Reading the number of running threads failed"); } - xret = MESA_load_profile_uint_nodef(config, "CONFIG", "valid-days", &(rte->days)); + xret = MESA_load_profile_string_nodef(config, "CONFIG", "table_info", rte->info_path, 128); if (xret < 0){ - mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Reading the number of valid time failed"); + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Reading the table_info path failed"); } - - xret = MESA_load_profile_string_nodef(config, "CONFIG", "ca-path", rte->ca_path, 128); - if (xret < 0){ - mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Reading the CA path failure"); + if(!rt_file_exsit(rte->info_path)) { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "The table_info(%s) does not exist", rte->info_path); goto finish; } - if(!rt_dir_exsit(rte->ca_path)) { - mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "The signature certificate(%s) does not exist", rte->ca_path); + xret = MESA_load_profile_string_nodef(config, "CONFIG", "pxy_obj_keyring", rte->pxy_path, 128); + if (xret < 0){ + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Reading the pxy_obj_keyring path failed"); + } + if(!rt_file_exsit(rte->pxy_path)) { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "The pxy_obj_keyring(%s) does not exist", rte->pxy_path); goto finish; } finish: diff --git a/src/cert_init.h b/src/cert_conf.h similarity index 64% rename from src/cert_init.h rename to src/cert_conf.h index 0e89a72..36455b6 100644 --- a/src/cert_init.h +++ b/src/cert_conf.h @@ -1,5 +1,5 @@ /************************************************************************* - > File Name: cert_init.h + > File Name: cert_conf.h > Author: > Mail: > Created Time: Fri 01 Jun 2018 12:06:26 AM PDT @@ -15,37 +15,54 @@ #include #include -#include "rd_lock.h" +#include "MESA_htable.h" struct request_t{ #define DATALEN 64 - char host[DATALEN]; - int t_id; - int flag; - - int valid; - - char *sendbuf; - - struct rd_lock_scb mtx; - + X509 *origin; + int kering_id; + char host[DATALEN]; + char *odata; struct evhttp_request *evh_req; }; +struct pxy_obj_keyring{ + int id; + int service; + EVP_PKEY *key; + X509 *root; + char name[128]; + char type[128]; + char ctl[256]; + char public_algo[256]; + uint64_t expire_after; + int is_valid; +}; + +struct key_ring_list +{ + uint64_t sum_cnt; + MESA_htable_handle htable; +}; + struct config_bucket_t{ unsigned int thread_nu; unsigned int days; - char ca_path[128]; + char info_path[128]; + + char pxy_path[128]; uint16_t e_port; /* libevent prot*/ char r_ip[16]; /* redis ip */ uint16_t r_port; /* redis port*/ + + struct key_ring_list keyring; }; extern struct config_bucket_t *cert_default_config(); diff --git a/src/cert_session.c b/src/cert_session.c index 709890b..9c47c26 100644 --- a/src/cert_session.c +++ b/src/cert_session.c @@ -26,7 +26,9 @@ #include "rt_stdlib.h" #include "rt_file.h" #include "rt_time.h" -#include "cert_init.h" +#include "json.h" + +#include "cert_conf.h" #include "async.h" #include "read.h" #include "bufferevent.h" @@ -36,14 +38,18 @@ #include "event_compat.h" #include "http.h" #include "buffer.h" +#include "MESA_htable.h" #include "util-internal.h" +#include "moodycamel_maat_rule.h" #include "moodycamel_field_stat2.h" #include "logging.h" -#define SG_DATA_SIZE 2048 +#define WAIT_FOR_EFFECTIVE_US 1000*1000 -#define DEFAULT_PRIVATEKEY_NAME "private.key" -#define DEFAULT_CA_CERTIFICATE "ca.cer" +#define SG_DATA_SIZE 4096 + +#define DEFAULT_PRIVATEKEY_NAME "mesalab-ca-cert.key" +#define DEFAULT_CA_CERTIFICATE "mesalab-ca-cert.cer" static libevent_thread *threads; @@ -59,10 +65,6 @@ static struct fs_stats_t SGstats = { #define sizeof_seconds(x) (x * 24 * 60 * 60) -rt_mutex entries_mtx = PTHREAD_MUTEX_INITIALIZER; - -#define RD_SYNC - void connectCallback(const struct redisAsyncContext *c, int status) { if (status != REDIS_OK) { mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Redis connect error : %s\n", c->errstr); @@ -79,6 +81,110 @@ void disconnectCallback(const struct redisAsyncContext *c, int status) { mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Redis server disconnected...\n"); } +static int +MESA_internal_htable_set_opt(MESA_htable_handle table, enum MESA_htable_opt opt_type, unsigned value) +{ + int ret = MESA_htable_set_opt(table, opt_type, &value, (int)(sizeof(value))); + return ret; +} + +static int +key_ring_list_create(struct key_ring_list *keyring) +{ + int ret = 0; + + keyring->htable = MESA_htable_born(); + assert(keyring->htable != NULL); + keyring->sum_cnt = 0; + + MESA_internal_htable_set_opt(keyring->htable, MHO_SCREEN_PRINT_CTRL, 0); + MESA_internal_htable_set_opt(keyring->htable, MHO_THREAD_SAFE, 1); + + MESA_internal_htable_set_opt(keyring->htable, MHO_MUTEX_NUM, 16); + MESA_internal_htable_set_opt(keyring->htable, MHO_HASH_SLOT_SIZE, 1024); + MESA_internal_htable_set_opt(keyring->htable, MHO_HASH_MAX_ELEMENT_NUM, 2048); + MESA_internal_htable_set_opt(keyring->htable, MHO_EXPIRE_TIME, 0); + + MESA_internal_htable_set_opt(keyring->htable, MHO_ELIMIMINATE_TYPE, + HASH_ELIMINATE_ALGO_LRU); + ret = MESA_htable_mature(keyring->htable); + if(ret != 0){ + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "MESA_htable_mature error!\n"); + goto finish; + } + +finish: + return ret; +} + +void x509_get_private_key(EVP_PKEY *pkey, char *pubkey) +{ + BIO *bp = NULL; + int len = 0; + + if ( (bp=BIO_new(BIO_s_mem())) == NULL){ + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "unable to create BIO for output\n"); + goto finish; + } + + PEM_write_bio_PrivateKey(bp, pkey, NULL, NULL, 0, NULL, NULL); + len = BIO_read(bp, pubkey, SG_DATA_SIZE); + if(len <= 0) { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Error reading signature file"); + goto free_err; + } + pubkey[len] = '\0'; + +free_err: + BIO_free(bp); +finish: + return; +} + +static +int create_client_key(EVP_PKEY** pkey, char *pubkey, int bits) +{ + RSA *rsa = NULL; + EVP_PKEY *pk = NULL; + + if((pk = EVP_PKEY_new()) == NULL){ + mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "create_client_key, gen new key failed!"); + goto err; + } + + rsa = RSA_generate_key(bits, RSA_F4, NULL, NULL); + if(!EVP_PKEY_assign_RSA(pk, rsa)){ + mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "create_client_key, assign key failed!"); + EVP_PKEY_free(pk); + goto err; + } + x509_get_private_key(pk, pubkey); + rsa = NULL; + + *pkey = pk; + return 1; + +err: + return 0; +} + +static void key_ring_free(void *data) +{ + struct pxy_obj_keyring *pxy_obj = NULL; + pxy_obj = (struct pxy_obj_keyring *)data; + + X509_free(pxy_obj->root); + EVP_PKEY_free(pxy_obj->key); +} + +void key_ring_list_destroy(struct key_ring_list *keyring) +{ + keyring->sum_cnt = 0; + MESA_htable_destroy(keyring->htable, key_ring_free); + keyring->htable = NULL; + return; +} + int ssl_rand(void *p, size_t sz) { @@ -159,15 +265,37 @@ ssl_x509_v3ext_copy_by_nid(X509 *crt, X509 *origcrt, int nid) return 1; } -X509 * -x509_modify_by_cert_bak(X509 *cacrt, EVP_PKEY *cakey, X509 *origcrt, EVP_PKEY *key, - int days, const char *extraname, const char *crlurl) +int x509_get_cn_name(X509 *origcrt, char *cn_name) +{ + int len = 0; + X509_NAME *subject = NULL; + + subject = X509_get_subject_name(origcrt); + if (!subject){ + goto finish; + } + len = X509_NAME_get_text_by_NID(subject, NID_commonName, cn_name, 256); + if (len > 0){ + printf("cn_name = %s\n", cn_name); + } +finish: + return 0; +} + +X509 * +x509_modify_by_cert_bak(X509 *cacrt, EVP_PKEY *cakey, X509 *origcrt, char *pkey, + int days, const char *extraname, const char *crlurl) { - X509_NAME *subject, *issuer; - GENERAL_NAMES *names; - GENERAL_NAME *gn; - X509 *crt; int rv; + X509 *crt = NULL; + EVP_PKEY* key = NULL; + GENERAL_NAME *gn = NULL; + GENERAL_NAMES *names = NULL; + X509_NAME *subject = NULL, *issuer = NULL; + + if(!create_client_key(&key, pkey, 2048)){ + goto err; + } //subjectname,issuername subject = X509_get_subject_name(origcrt); issuer = X509_get_subject_name(cacrt); @@ -384,10 +512,11 @@ errout2: sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); errout: X509_free(crt); +err: return NULL; } -void x509_get_msg_from_ca(X509 *x509, char *ca_s) +void x509_get_msg_from_ca(X509 *x509, char *root) { BIO *bp = NULL; int len = 0; @@ -397,40 +526,41 @@ void x509_get_msg_from_ca(X509 *x509, char *ca_s) goto finish; } PEM_write_bio_X509(bp, x509); - len = BIO_read(bp, ca_s, SG_DATA_SIZE * 2); + len = BIO_read(bp, root, SG_DATA_SIZE * 2); if(len <= 0) { mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Error reading signature file"); goto err; } - ca_s[len] ='\0'; + root[len] ='\0'; err: BIO_free(bp); finish: return; } -void x509_get_private_key(EVP_PKEY *pkey, char *pubkey) +X509 * +x509_get_ca_from_msg(const char *cert, int len) { - BIO *bp = NULL; - int len = 0; + BIO *bp; + X509* x509 = NULL; + char in[SG_DATA_SIZE] = {0}; + + strncpy(in, cert, len); if ( (bp=BIO_new(BIO_s_mem())) == NULL){ mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "unable to create BIO for output\n"); goto finish; } - - PEM_write_bio_PrivateKey(bp, pkey, NULL, NULL, 0, NULL, NULL); - len = BIO_read(bp, pubkey, SG_DATA_SIZE); - if(len <= 0) { - mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Error reading signature file"); - goto free_err; + BIO_printf(bp, "%s", in); + x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL); + if(NULL == x509) { + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to read pem file\n"); + goto err; } - pubkey[len] = '\0'; - -free_err: - BIO_free(bp); +err: + BIO_free(bp); finish: - return; + return x509; } /* @@ -540,86 +670,11 @@ redis_reget_callback(redisAsyncContext __attribute__((__unused__))*cl_ctx, evhttp_socket_send(evh_req, reply->str); - kfree(request->sendbuf); + kfree(request->odata); kfree(request); return; } -static void __attribute__((__unused__)) -redis_set_callback(redisAsyncContext *cl_ctx, void *r, - void *privdata) -{ - struct request_t *request = (struct request_t *)privdata; - struct evhttp_request *evh_req = request->evh_req; - - redisReply *reply = (redisReply*)r; - - if(reply->type == REDIS_REPLY_ERROR){ - mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Set redis data(key = %s) returns failed", request->host); - goto finish; - } - /* - Synchronous reader data is +OK - Asynchronous redis data is $-1\r\n+OK - */ - - libevent_thread *thread = threads + request->t_id; - redisReader *reader = cl_ctx->c.reader; - - switch (reader->buf[5]) { - case '+' : - mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Writing data(key = %s) to redis successfully", request->host); - FS_internal_operate(SGstats.handle, thread->column_ids, SGstats.line_ids[2], FS_OP_ADD, 1); - - evhttp_socket_send(evh_req, request->sendbuf); - goto free; - case '\0': - case '$' : - mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Writing data(key = %s) to redis failed", request->host); - FS_internal_operate(SGstats.handle, thread->column_ids, SGstats.line_ids[1], FS_OP_ADD, 1); - - redisAsyncCommand(cl_ctx, redis_reget_callback, request, "GET %s", request->host); - goto finish; - default : - mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Read redis data(key = %s) return code failed", request->host); - evhttp_send_error(request->evh_req, HTTP_NOTFOUND, 0); - goto free; - } - -free: - kfree(request->sendbuf); - kfree(request); -finish: - return; -} - -static -int create_client_key(EVP_PKEY** pkey, char *pubkey, int bits) -{ - RSA *rsa = NULL; - EVP_PKEY *pk = NULL; - - if((pk = EVP_PKEY_new()) == NULL){ - mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "create_client_key, gen new key failed!"); - goto err; - } - - rsa = RSA_generate_key(bits, RSA_F4, NULL, NULL); - if(!EVP_PKEY_assign_RSA(pk, rsa)){ - mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "create_client_key, assign key failed!"); - EVP_PKEY_free(pk); - goto err; - } - x509_get_private_key(pk, pubkey); - rsa = NULL; - - *pkey = pk; - return 1; - -err: - return 0; -} - int add_cert_ctx(X509_NAME* name, char* ctx[], int num) { int i = 0; @@ -729,19 +784,45 @@ err: return NULL; } -int x509_online_append(char *host, EVP_PKEY *key, X509 *root, char *ca_s, char *pubkey) +static uint64_t +x509_online_append(X509 *origin, int id, char *root, char *sign, char *pkey) { - struct config_bucket_t *rte = cert_default_config(); + void *res = NULL; + uint64_t expire_after = 0; - X509* x509 = x509_modify_by_cert(root, key, host, pubkey, rte->days); - if (!x509){ + struct key_ring_list *keyring = &cert_default_config()->keyring; + + res = MESA_htable_search(keyring->htable, (const uchar *)&id, sizeof(int)); + if (!res){ + mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "The table where keyringid = %d was not found", 1); goto finish; } - x509_get_msg_from_ca(x509, ca_s); - X509_free(x509); + struct pxy_obj_keyring *pxy_obj = (struct pxy_obj_keyring *)res; + if (pxy_obj->is_valid != 1){ + mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Table information is invalid"); + goto finish; + } + if (STRCMP(pxy_obj->type, "root") == 0 || + STRCMP(pxy_obj->type, "intermediate") == 0){ + //X509* x509 = x509_modify_by_cert(pxy_obj->root, pxy_obj->key, host, pkey, pxy_obj->expire_after); + X509* x509 = x509_modify_by_cert_bak(pxy_obj->root, pxy_obj->key, origin, pkey, + pxy_obj->expire_after, NULL, NULL); + + if (!x509){ + goto finish; + } + expire_after = pxy_obj->expire_after; + x509_get_msg_from_ca(x509, sign); + x509_get_msg_from_ca(pxy_obj->root, root); + + X509_free(x509); + } + if (STRCMP(pxy_obj->type, "end-entity")){ + } + finish: - return 0; + return expire_after; } static char readBytes(char *str) @@ -757,7 +838,8 @@ static char readBytes(char *str) } static int -rediSyncCommand(redisAsyncContext *cl_ctx, struct request_t *request, char *sendbuf) +rediSyncCommand(redisAsyncContext *cl_ctx, struct request_t *request, + char *odata, uint64_t expire_after) { int xret = -1; redisReply *reply; @@ -765,8 +847,8 @@ rediSyncCommand(redisAsyncContext *cl_ctx, struct request_t *request, char *send libevent_thread *thread = threads + request->t_id; struct evhttp_request *evh_req = request->evh_req; - reply = (redisReply *)redisCommand(thread->sync, "set %s %s ex %d nx", request->host, sendbuf, - sizeof_seconds(cert_default_config()->days)); + reply = (redisReply *)redisCommand(thread->sync, "set %s %s ex %d nx", request->host, odata, + sizeof_seconds(expire_after)); if (NULL == reply) goto free; @@ -775,7 +857,7 @@ rediSyncCommand(redisAsyncContext *cl_ctx, struct request_t *request, char *send mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Writing data(key = %s) to redis successfully", request->host); FS_internal_operate(SGstats.handle, thread->column_ids, SGstats.line_ids[2], FS_OP_ADD, 1); - evhttp_socket_send(evh_req, request->sendbuf); + evhttp_socket_send(evh_req, request->odata); goto free; case '$' : mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Writing data(key = %s) to redis failed", request->host); @@ -793,66 +875,123 @@ rediSyncCommand(redisAsyncContext *cl_ctx, struct request_t *request, char *send free: freeReplyObject(reply); - kfree(request->sendbuf); + kfree(request->odata); kfree(request); finish: return xret; } +static inline json_object * +web_json_record_array_add_string(char **chain) +{ + int i; + json_object *sample_array; + + sample_array = json_object_new_array(); + if (sample_array == NULL) + goto finish; + + for(i = 0; chain[i] != '\0'; i++){ + json_object_array_add(sample_array, json_object_new_string(chain[i])); + } +finish: + return sample_array; +} + +static inline int +json_data_rebuild(const char *data, + size_t size, + char **odata, + size_t *osize) +{ + size_t real_size = size + 1; /** 2, '\n' + '\0' */ + + if (!data || !size) + return -1; + + *odata = malloc (real_size); + if (!*odata) + return -1; + memset (*odata, 0, real_size); + snprintf(*odata, real_size, "%s", data); + + *osize = real_size; + + return 0; +} + static int -redis_encode_sendbuf(struct request_t *request, redisAsyncContext *c) +web_json_table_add(char *privatekey, char *sign, + char **chain, char **data) +{ + size_t osize = 0; + const char *jstr = NULL; + struct json_object *outline = json_object_new_object(); + + json_object_object_add(outline, "CERTIFICATE_CHAIN", web_json_record_array_add_string(chain)); + json_object_object_add(outline, "PRIVATE_KEY", json_object_new_string(privatekey)); + json_object_object_add(outline, "CERTIFICATE", json_object_new_string(sign)); + + jstr = json_object_to_json_string (outline); + + json_data_rebuild(jstr, strlen(jstr), data, &osize); + + json_object_put(outline); + + return 0; +} + +static int +redis_clnt_pdu_send(struct request_t *request, redisAsyncContext *c) { int xret = -1; + uint64_t expire_after; uint64_t startTime = 0, endTime = 0; - libevent_thread *thread = threads + request->t_id; - char cert[SG_DATA_SIZE] = {0}, pubkey[SG_DATA_SIZE] = {0}; - - char *sendbuf = (char *)kmalloc(SG_DATA_SIZE * 2, MPF_CLR, -1); + libevent_thread *thread = threads + request->t_id; + char sign[SG_DATA_SIZE] = {0}, pkey[SG_DATA_SIZE] = {0}; + char root[SG_DATA_SIZE] = {0}; startTime = rt_time_ns(); - - x509_online_append(request->host, thread->key, thread->root, cert, pubkey); - if (cert[0] == '\0' && pubkey[0] == '\0'){ + expire_after = x509_online_append(request->origin, request->kering_id, root, sign, pkey); + if (sign[0] == '\0' && pkey[0] == '\0'){ mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to issue certificate"); evhttp_send_error(request->evh_req, HTTP_NOTFOUND, 0); goto finish; } - endTime = rt_time_ns(); thread->diffTime += (endTime - startTime); mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "%lu - %lu = %lu\n", startTime, endTime, endTime - startTime); FS_internal_operate(SGstats.handle, thread->column_ids, SGstats.line_ids[3], FS_OP_SET, thread->diffTime); - FS_internal_operate(SGstats.handle, thread->field_ids, 0, FS_OP_ADD, 1); - snprintf(sendbuf, SG_DATA_SIZE * 2 - 1, "%s%s", pubkey, cert); - - request->sendbuf = sendbuf; -#ifdef RD_SYNC - xret = rediSyncCommand(c, request, sendbuf); +#if 0 + char *chain[6] ={0}; + chain[0] = root; + chain[1] = sign; + web_json_table_add(pkey, sign, chain, &request->odata); #else - xret = redisAsyncCommand(c, redis_set_callback, request, "set %s %s ex %d nx", - request->host, sendbuf, sizeof_seconds(cert_default_config()->days)); + request->odata = (char *)malloc(SG_DATA_SIZE * 2); + snprintf(request->odata, SG_DATA_SIZE * 2 - 1, "%s%s%s", pkey, sign, root); #endif + xret = rediSyncCommand(c, request, request->odata, expire_after); if (xret < 0){ mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to set information to redis server"); goto finish; } xret = 0; - finish: return xret; } static int -redis_decode_sendbuf(struct request_t *request, redisReply *reply) +redis_clnt_send(struct request_t *request, redisReply *reply) { int xret = -1; - char sendbuf[SG_DATA_SIZE * 2] = {0}; + char odata[SG_DATA_SIZE * 2] = {0}; libevent_thread *thread = threads + request->t_id; if (!reply && !reply->str){ @@ -864,9 +1003,9 @@ redis_decode_sendbuf(struct request_t *request, redisReply *reply) FS_internal_operate(SGstats.handle, thread->field_ids, 0, FS_OP_ADD, 1); - snprintf(sendbuf, SG_DATA_SIZE * 2, "%s", reply->str); + snprintf(odata, SG_DATA_SIZE * 2, "%s", reply->str); - evhttp_socket_send(request->evh_req, sendbuf); + evhttp_socket_send(request->evh_req, odata); finish: kfree(request); @@ -884,13 +1023,13 @@ void redis_get_callback(redisAsyncContext *c, void *r, void *privdata) case REDIS_REPLY_STRING: mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "Sends the certificate information to the requestor"); - xret = redis_decode_sendbuf(request, reply); + xret = redis_clnt_send(request, reply); break; case REDIS_REPLY_NIL: mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "Generating certificate information"); - xret = redis_encode_sendbuf(request, c); + xret = redis_clnt_pdu_send(request, c); break; default: break; @@ -899,15 +1038,11 @@ void redis_get_callback(redisAsyncContext *c, void *r, void *privdata) return; } -int x509_privatekey_init(EVP_PKEY **key, X509 **root) +int x509_privatekey_init(char *private_file, char *public_file, + EVP_PKEY **key, X509 **root) { int xret = -1; FILE *fp; RSA *rsa = NULL; - char key_path[128] = {0}, cert_path[128] = {0}; - struct config_bucket_t *rte = cert_default_config(); - - snprintf(key_path, sizeof(key_path), "%s/%s", rte->ca_path, DEFAULT_PRIVATEKEY_NAME); - snprintf(cert_path, sizeof(cert_path), "%s/%s", rte->ca_path, DEFAULT_CA_CERTIFICATE); *key = EVP_PKEY_new(); if (NULL == *key){ @@ -919,9 +1054,9 @@ int x509_privatekey_init(EVP_PKEY **key, X509 **root) goto pkey_free; } - fp = fopen(key_path, "r"); + fp = fopen(private_file, "r"); if (NULL == fp){ - mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to open file(%s)", key_path); + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to open file(%s)", private_file); RSA_free(rsa); goto pkey_free; } @@ -933,9 +1068,9 @@ int x509_privatekey_init(EVP_PKEY **key, X509 **root) fclose(fp); BIO *in; - in = BIO_new_file(cert_path, "r"); + in = BIO_new_file(public_file, "r"); if (!in){ - mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to open file(%s)", cert_path); + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to open file(%s)", public_file); goto pkey_free; } @@ -955,37 +1090,114 @@ finish: return xret; } -static int -ev_decode_uri(const char *uri, char *host, - int *flag, int *valid) +#define BURSIZE 4096 + +int hex2dec(char c) { - const char *fg = NULL, *vl = NULL, *hst = NULL; - char *decoded_uri = NULL; + if ('0' <= c && c <= '9') { + return c - '0'; + } else if ('a' <= c && c <= 'f') { + return c - 'a' + 10; + } else if ('A' <= c && c <= 'F') { + return c - 'A' + 10; + } else { + return -1; + } +} + +void _urldecode(char url[]) +{ + int i = 0; + int len = strlen(url); + int res_len = 0; + char res[BURSIZE]; + + if(!strchr(url, '%')) + return; + + for (i = 0; i < len; ++i) { + char c = url[i]; + if (c != '%') { + res[res_len++] = c; + } else { + char c1 = url[++i]; + char c0 = url[++i]; + int num = 0; + num = hex2dec(c1) * 16 + hex2dec(c0); + res[res_len++] = num; + } + } + res[res_len] = '\0'; + strcpy(url, res); +} + +static char* +decode_capture(const char *uri, const char *key) +{ + int size = 0; + char *origin_uri = NULL; + + char *urlecode = STRSTR(uri, key); + if (!urlecode){ + size = 0; + }else{ + size = strlen(urlecode); + } + int len = strlen(uri) - size; + origin_uri = (char *)malloc(len + 1); + memcpy(origin_uri, uri, len); + + return origin_uri; +} + +static char* +decode_origin_cert(const char *uri, const char *key) +{ + char *origin = NULL; + + char *urlecode = STRSTR(uri, key); + if (!urlecode){ + goto finish; + } + origin = urlecode + 12; + _urldecode(origin); +finish: + return origin; +} + +static int +thread_decode_uri(const char *uri, char *host, + int *flag, X509 **origin, int *keyring_id) +{ + const char *fg = NULL, *cert = NULL; + const char *ht = NULL, *id = NULL; + char *decoded_uri = NULL, *ecode_uri = NULL; struct evkeyvalq params; - decoded_uri = evhttp_decode_uri(uri); + decoded_uri = evhttp_decode_uri(ecode_uri = decode_capture(uri, "origin_cert")); if (!decoded_uri){ goto finish; } + evhttp_parse_query(uri, ¶ms); - evhttp_parse_query(decoded_uri, ¶ms); - - hst = evhttp_find_header(¶ms, "host"); - if (hst[0] != '\0') - memcpy(host, hst, strlen(hst)); - + ht = evhttp_find_header(¶ms, "host"); + if (ht != NULL) + memcpy(host, ht, strlen(ht)); fg = evhttp_find_header(¶ms, "flag"); if (fg) *flag = atoi(fg); - - vl = evhttp_find_header(¶ms, "valid"); - if (vl) - *valid = atoi(vl); + id = evhttp_find_header(¶ms, "kering_id"); + if (id) + *keyring_id = atoi(id); + cert = decode_origin_cert(uri, "origin_cert"); + if (cert) + *origin = x509_get_ca_from_msg(cert, STRLEN(cert)); evhttp_clear_headers(¶ms); free(decoded_uri); finish: + free(ecode_uri); return 0; } @@ -1026,20 +1238,20 @@ pthread_work_proc(struct evhttp_request *evh_req, void *arg) request->t_id = t->id; request->evh_req = evh_req; } - switch (evhttp_request_get_command(evh_req)) { case EVHTTP_REQ_GET: cmdtype = "GET"; break; default: cmdtype = "unknown"; break; } - FS_internal_operate(SGstats.handle, t->column_ids, SGstats.line_ids[0], FS_OP_ADD, 1); - ev_decode_uri(uri, request->host, &request->flag, &request->valid); - mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "[Thread %d]Received a %s request for %s, host:%s, flag:%d, valid:%d\nHeaders:", + thread_decode_uri(uri, request->host, &request->flag, &request->origin, &request->kering_id); + mesa_runtime_log(RLOG_LV_DEBUG, MODULE_NAME, "[Thread %d]Received a %s request for %s, host:%s, flag:%d, valid:%p\nHeaders:", request->t_id, cmdtype, uri, request->host, - request->flag, request->valid); + request->flag, request->origin); - if (request->host[0] == '\0' || !request->evh_req){ + if (request->host[0] == '\0' || + request->origin == NULL || + !request->evh_req){ mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to resolve the request url"); kfree(request); evhttp_uri_free(decoded); @@ -1100,12 +1312,6 @@ cert_task_private_init(struct event_base *base, libevent_thread *me) goto finish; } - /* Initialize the X509 CA*/ - xret = x509_privatekey_init(&me->key, &me->root); - if (xret < 0 || !me->key || !me->root){ - mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to initialize the x509 certificate"); - goto finish; - } finish: return xret; } @@ -1169,23 +1375,19 @@ evhttp_listen_socket_byuser(const struct sockaddr *sa, int socklen, if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on))<0) goto err; - if (flags & LEV_OPT_REUSEABLE) { if (evutil_make_listen_socket_reuseable(fd) < 0) goto err; } - if (flags & LEV_OPT_REUSEABLE_PORT) { if (evutil_make_listen_socket_reuseable_port(fd) < 0){ goto err; } } - if (sa) { if (bind(fd, sa, socklen)<0) goto err; } - if (listen(fd, backlog) == -1) { goto err; } @@ -1230,7 +1432,6 @@ libevent_socket_init() mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Could not create a listen!\n"); goto finish; } - threads = calloc(thread_nu, sizeof(libevent_thread)); if (! threads) { mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Can't allocate thread descriptors"); @@ -1247,12 +1448,10 @@ libevent_socket_init() thread->routine = pthread_worker_libevent; fs_screen_preview(thread); - if (pthread_create(&thread->pid, thread->attr, thread->routine, &threads[tid])){ mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "%s", strerror(errno)); goto finish; } - if (pthread_detach(thread->pid)){ mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "%s", strerror(errno)); goto finish; @@ -1296,17 +1495,22 @@ void sigproc(int __attribute__((__unused__))sig) redisAsyncDisconnect(thread->cl_ctx); redisFree(thread->sync); } - X509_free(thread->root); - EVP_PKEY_free(thread->key); + key_ring_list_destroy(rte->keyring.htable); } kfree(threads); exit(1); } -static int cert_screen_init() +static int +MESA_internal_set_para(screen_stat_handle_t handle, enum FS_option type, unsigned value) +{ + int ret = FS_internal_set_para(handle, type, &value, (int)(sizeof(value))); + return ret; +} + +static int mesa_fiel_stat_init() { - int value=0; char stat_path[63] = {0}; char pname[32]= {0}, buff[128] = {0}; @@ -1314,32 +1518,24 @@ static int cert_screen_init() rt_get_pname_by_pid(getpid(), &pname[0]); FS_internal_set_para(SGstats.handle, APP_NAME, pname, strlen(pname)+1); - value=0; - FS_internal_set_para(SGstats.handle, FLUSH_BY_DATE, &value, sizeof(value)); - snprintf(stat_path, 63, "%s/fs2_%s.status", logging_sc_lid.run_log_path, pname); FS_internal_set_para(SGstats.handle, OUTPUT_DEVICE, stat_path, strlen(stat_path)+1); - value=1; - FS_internal_set_para(SGstats.handle, PRINT_MODE, &value, sizeof(value)); - value=1; - FS_internal_set_para(SGstats.handle, CREATE_THREAD, &value, sizeof(value)); - value=3; - FS_internal_set_para(SGstats.handle, STAT_CYCLE, &value, sizeof(value)); + + MESA_internal_set_para(SGstats.handle, FLUSH_BY_DATE, 0); + MESA_internal_set_para(SGstats.handle, PRINT_MODE, 1); + MESA_internal_set_para(SGstats.handle, CREATE_THREAD, 1); + MESA_internal_set_para(SGstats.handle, STAT_CYCLE, 3); snprintf(buff,sizeof(buff),"%s", "Req"); SGstats.line_ids[0] = FS_internal_register(SGstats.handle, FS_STYLE_COLUMN, FS_CALC_CURRENT, buff); - snprintf(buff,sizeof(buff),"%s", "DB"); SGstats.line_ids[1] = FS_internal_register(SGstats.handle, FS_STYLE_COLUMN, FS_CALC_CURRENT, buff); - snprintf(buff,sizeof(buff),"%s", "Local"); SGstats.line_ids[2] = FS_internal_register(SGstats.handle, FS_STYLE_COLUMN, FS_CALC_CURRENT, buff); - snprintf(buff,sizeof(buff),"%s", "take-time"); SGstats.line_ids[3] = FS_internal_register(SGstats.handle, FS_STYLE_COLUMN, FS_CALC_CURRENT, buff); - value=SGstats.line_ids[3]; - FS_internal_set_para(SGstats.handle, ID_INVISBLE, &value, sizeof(value)); + MESA_internal_set_para(SGstats.handle, ID_INVISBLE, SGstats.line_ids[3]); snprintf(buff,sizeof(buff),"Cert/Nsec"); FS_internal_register_ratio(SGstats.handle, SGstats.line_ids[3], SGstats.line_ids[2], 1, @@ -1350,14 +1546,134 @@ static int cert_screen_init() return 0; } -int cert_session_init() +void Maat_read_entry_start_cb(int update_type, void* u_para) { int xret = 0; - cert_screen_init(); + struct key_ring_list *keyring = (struct key_ring_list *)u_para; - libevent_socket_init(); + if (update_type != 1) + goto finish; - return xret; + /** The current behavior is full, and the original keyring chain is deleted */ + if (keyring->htable){ + key_ring_list_destroy(keyring); + } + + xret = key_ring_list_create(keyring); + if (xret == 0){ + mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "The initial keyring list was successful, addr is %p\n", + keyring->htable); + } +finish: + return; +} + +static void +Maat_read_entry_cb(int __attribute__((__unused__))table_id, const char* table_line, + void *u_para) +{ + int xret = 0; + struct pxy_obj_keyring *pxy_obj = NULL; + char private_file[512] = {0}, public_file[512] = {0}; + + struct key_ring_list *keyring = (struct key_ring_list *)u_para; + + pxy_obj = (struct pxy_obj_keyring *)malloc(sizeof(struct pxy_obj_keyring)); + if (!pxy_obj){ + mesa_runtime_log(RLOG_LV_INFO, MODULE_NAME, "Can not alloc, %s\n", strerror(errno)); + goto finish; + } + memset(pxy_obj, 0, sizeof(struct pxy_obj_keyring)); + sscanf(table_line, "%d\t%d\t%s\t%s\t%s\t%s\t%lu\t%s\t%s\t%d", &pxy_obj->id, &pxy_obj->service, pxy_obj->name, + pxy_obj->type, private_file, public_file, &pxy_obj->expire_after, pxy_obj->public_algo, + pxy_obj->ctl, &pxy_obj->is_valid); + xret = x509_privatekey_init(private_file, public_file, &pxy_obj->key, &pxy_obj->root); + if (xret < 0 || !pxy_obj->key || !pxy_obj->root){ + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Failed to initialize the x509 certificate, the keyring id is %d", + pxy_obj->id); + goto finish; + } + + MESA_htable_add(keyring->htable, (const uchar *)(&(pxy_obj->id)), sizeof(int), pxy_obj); + keyring->sum_cnt++; + +finish: + return; +} + +void Maat_read_entry_finish_cb(void* u_para) +{ + long long version=0; + Maat_feather_t feather = u_para; + int ret = 0, is_last_updating_table = 0; + + ret = Maat_inter_read_state(feather,MAAT_STATE_VERSION, &version, sizeof(version)); + assert(ret==0); + + ret = Maat_inter_read_state(feather,MAAT_STATE_LAST_UPDATING_TABLE, &is_last_updating_table, sizeof(is_last_updating_table)); + assert(ret==0); + //printf("Maat Version %lld at plugin finish callback, is_last_update=%d.\n",version,is_last_updating_table); + + return; +} + +int sample_plugin_table(Maat_feather_t feather,const char* table_name, + Maat_start_callback_t *start,Maat_update_callback_t *update,Maat_finish_callback_t *finish, + void *u_para, + void __attribute__((__unused__))*logger) +{ + int table_id = 0,ret = 0; + table_id = Maat_inter_table_register(feather,table_name); + if(table_id == -1){ + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Database table %s register failed.\n",table_name); + }else{ + ret = Maat_inter_table_callback_register(feather, table_id, start, + update, finish, u_para); + if(ret < 0){ + mesa_runtime_log(RLOG_LV_FATAL, MODULE_NAME, "Maat callback register table %s error.\n",table_name); + } + } + + return ret; +} + +int maat_feather_init() +{ + Maat_feather_t feather = NULL; + int scan_interval_ms = 1, effective_interval_ms = 0; + + struct config_bucket_t *rte = cert_default_config(); + + feather = Maat_inter_feather(rte->thread_nu, rte->info_path, logging_sc_lid.run_log_handle); + + Maat_inter_set_feather_opt(feather, MAAT_OPT_INSTANCE_NAME, "certstore", strlen("certstore") + 1); + + + Maat_inter_set_feather_opt(feather, MAAT_OPT_JSON_FILE_PATH, rte->pxy_path, strlen(rte->pxy_path)+1); + Maat_inter_set_feather_opt(feather, MAAT_OPT_SCANDIR_INTERVAL_MS,&scan_interval_ms, sizeof(scan_interval_ms)); + Maat_inter_set_feather_opt(feather, MAAT_OPT_EFFECT_INVERVAL_MS,&effective_interval_ms, sizeof(effective_interval_ms)); + Maat_inter_initiate_feather(feather); + + /*Keyring list initialization **/ + key_ring_list_create(&rte->keyring); + + sample_plugin_table(feather, "PXY_OBJ_KEYRING", + Maat_read_entry_start_cb, + Maat_read_entry_cb, + Maat_read_entry_finish_cb, + &rte->keyring, + NULL); + return 0; +} + +int cert_session_init() +{ + mesa_fiel_stat_init(); + + maat_feather_init(); + + libevent_socket_init(); + return 0; } diff --git a/src/cert_store.c b/src/cert_store.c index f95816f..3b525f2 100644 --- a/src/cert_store.c +++ b/src/cert_store.c @@ -13,7 +13,7 @@ #include "rt_string.h" #include "rt_common.h" #include "cert_daemon.h" -#include "cert_init.h" +#include "cert_conf.h" #include "cert_session.h" #include "logging.h" #include "MESA_prof_load.h" @@ -84,8 +84,8 @@ void cert_preview () printf("%30s:%45s\n", "Redis Ip", rte->r_ip); printf("%30s:%45d\n", "Redis Port", rte->r_port); printf("%30s:%45d\n", "Libevent Port", rte->e_port); - printf("%30s:%45d\n", "Ca Valid time", rte->days); - printf("%30s:%45s\n", "Ca Directory", rte->ca_path); + printf("%30s:%45s\n", "Table Info", rte->info_path); + printf("%30s:%45s\n", "Pxy Obj Keyring", rte->pxy_path); printf("%30s:%45s\n", "Log Directory", logging_sc_lid.run_log_path); printf("\r\n"); @@ -104,11 +104,12 @@ int main(int argc, char **argv) if (MODE_TYPE(0x20)){ daemonize(); } - signal(SIGINT, sigproc); - cert_preview(); + cert_session_init(); + signal(SIGINT, sigproc); + return 0; } diff --git a/src/components/json/arraylist.c b/src/components/json/arraylist.c new file mode 100644 index 0000000..81b6fa2 --- /dev/null +++ b/src/components/json/arraylist.c @@ -0,0 +1,101 @@ +/* + * $Id: arraylist.c,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#ifdef STDC_HEADERS +# include +# include +#endif /* STDC_HEADERS */ + +#if defined(HAVE_STRINGS_H) && !defined(_STRING_H) && !defined(__USE_BSD) +# include +#endif /* HAVE_STRINGS_H */ + +#include "bits.h" +#include "arraylist.h" + +struct array_list* +array_list_new(array_list_free_fn *free_fn) +{ + struct array_list *arr; + + arr = (struct array_list*)calloc(1, sizeof(struct array_list)); + if(!arr) return NULL; + arr->size = ARRAY_LIST_DEFAULT_SIZE; + arr->length = 0; + arr->free_fn = free_fn; + if(!(arr->array = (void**)calloc(sizeof(void*), arr->size))) { + free(arr); + return NULL; + } + return arr; +} + +extern void +array_list_free(struct array_list *arr) +{ + int i; + for(i = 0; i < arr->length; i++) + if(arr->array[i]) arr->free_fn(arr->array[i]); + free(arr->array); + free(arr); +} + +void* +array_list_get_idx(struct array_list *arr, int i) +{ + if(i >= arr->length) return NULL; + return arr->array[i]; +} + +static int array_list_expand_internal(struct array_list *arr, int max) +{ + void *t; + int new_size; + + if(max < arr->size) return 0; + new_size = json_max(arr->size << 1, max); + if(!(t = realloc(arr->array, new_size*sizeof(void*)))) return -1; + arr->array = (void**)t; + (void)memset(arr->array + arr->size, 0, (new_size-arr->size)*sizeof(void*)); + arr->size = new_size; + return 0; +} + +int +array_list_put_idx(struct array_list *arr, int idx, void *data) +{ + if(array_list_expand_internal(arr, idx+1)) return -1; + if(arr->array[idx]) arr->free_fn(arr->array[idx]); + arr->array[idx] = data; + if(arr->length <= idx) arr->length = idx + 1; + return 0; +} + +int +array_list_add(struct array_list *arr, void *data) +{ + return array_list_put_idx(arr, arr->length, data); +} + +void +array_list_sort(struct array_list *arr, int(*sort_fn)(const void *, const void *)) +{ + qsort(arr->array, arr->length, sizeof(arr->array[0]), + (int (*)(const void *, const void *))sort_fn); +} + +int +array_list_length(struct array_list *arr) +{ + return arr->length; +} diff --git a/src/components/json/arraylist.h b/src/components/json/arraylist.h new file mode 100644 index 0000000..4f3113c --- /dev/null +++ b/src/components/json/arraylist.h @@ -0,0 +1,56 @@ +/* + * $Id: arraylist.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _arraylist_h_ +#define _arraylist_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ARRAY_LIST_DEFAULT_SIZE 32 + +typedef void (array_list_free_fn) (void *data); + +struct array_list +{ + void **array; + int length; + int size; + array_list_free_fn *free_fn; +}; + +extern struct array_list* +array_list_new(array_list_free_fn *free_fn); + +extern void +array_list_free(struct array_list *al); + +extern void* +array_list_get_idx(struct array_list *al, int i); + +extern int +array_list_put_idx(struct array_list *al, int i, void *data); + +extern int +array_list_add(struct array_list *al, void *data); + +extern int +array_list_length(struct array_list *al); + +extern void +array_list_sort(struct array_list *arr, int(*compar)(const void *, const void *)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/components/json/bits.h b/src/components/json/bits.h new file mode 100644 index 0000000..c8cbbc8 --- /dev/null +++ b/src/components/json/bits.h @@ -0,0 +1,28 @@ +/* + * $Id: bits.h,v 1.10 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _bits_h_ +#define _bits_h_ + +#ifndef json_min +#define json_min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef json_max +#define json_max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#define hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) +#define error_ptr(error) ((void*)error) +#define error_description(error) (json_tokener_errors[error]) +#define is_error(ptr) (ptr == NULL) + +#endif diff --git a/src/components/json/config.h b/src/components/json/config.h new file mode 100644 index 0000000..018a17f --- /dev/null +++ b/src/components/json/config.h @@ -0,0 +1,175 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Enable RDRANR Hardware RNG Hash Seed */ +/* #undef ENABLE_RDRAND */ + +/* Define if .gnu.warning accepts long strings. */ +/* #undef HAS_GNU_WARNING_LONG */ + +/* Define to 1 if you have the declaration of `INFINITY', and to 0 if you + don't. */ +#define HAVE_DECL_INFINITY 1 + +/* Define to 1 if you have the declaration of `isinf', and to 0 if you don't. + */ +#define HAVE_DECL_ISINF 1 + +/* Define to 1 if you have the declaration of `isnan', and to 0 if you don't. + */ +#define HAVE_DECL_ISNAN 1 + +/* Define to 1 if you have the declaration of `nan', and to 0 if you don't. */ +#define HAVE_DECL_NAN 1 + +/* Define to 1 if you have the declaration of `_finite', and to 0 if you + don't. */ +#define HAVE_DECL__FINITE 0 + +/* Define to 1 if you have the declaration of `_isnan', and to 0 if you don't. + */ +#define HAVE_DECL__ISNAN 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ENDIAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `open' function. */ +#define HAVE_OPEN 1 + +/* Define to 1 if your system has a GNU libc compatible `realloc' function, + and to 0 otherwise. */ +#define HAVE_REALLOC 1 + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strncasecmp' function. */ +#define HAVE_STRNCASECMP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_CDEFS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vasprintf' function. */ +#define HAVE_VASPRINTF 1 + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `vsyslog' function. */ +#define HAVE_VSYSLOG 1 + +/* Public define for json_inttypes.h */ +#define JSON_C_HAVE_INTTYPES_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "json-c" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "json-c@googlegroups.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "json-c" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "json-c 0.12" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "json-c" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.12" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "0.12" + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ + +/* Define to rpl_realloc if the replacement function should be used. */ +/* #undef realloc */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ diff --git a/src/components/json/debug.c b/src/components/json/debug.c new file mode 100644 index 0000000..3b64b59 --- /dev/null +++ b/src/components/json/debug.c @@ -0,0 +1,83 @@ +/* + * $Id: debug.c,v 1.5 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#if HAVE_SYSLOG_H +# include +#endif /* HAVE_SYSLOG_H */ + +#if HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ + +#if HAVE_SYS_PARAM_H +#include +#endif /* HAVE_SYS_PARAM_H */ + +#include "debug.h" + +static int _syslog = 0; +static int _debug = 0; + +void mc_set_debug(int debug) { _debug = debug; } +int mc_get_debug(void) { return _debug; } + +extern void mc_set_syslog(int syslog) +{ + _syslog = syslog; +} + +void mc_debug(const char *msg, ...) +{ + va_list ap; + if(_debug) { + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_DEBUG, msg, ap); + } else +#endif + vprintf(msg, ap); + va_end(ap); + } +} + +void mc_error(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_ERR, msg, ap); + } else +#endif + vfprintf(stderr, msg, ap); + va_end(ap); +} + +void mc_info(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_INFO, msg, ap); + } else +#endif + vfprintf(stderr, msg, ap); + va_end(ap); +} diff --git a/src/components/json/debug.h b/src/components/json/debug.h new file mode 100644 index 0000000..80ca3e4 --- /dev/null +++ b/src/components/json/debug.h @@ -0,0 +1,71 @@ +/* + * $Id: debug.h,v 1.5 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void mc_set_debug(int debug); +extern int mc_get_debug(void); + +extern void mc_set_syslog(int syslog); + +extern void mc_debug(const char *msg, ...); +extern void mc_error(const char *msg, ...); +extern void mc_info(const char *msg, ...); + +#ifndef __STRING +#define __STRING(x) #x +#endif + +#ifndef PARSER_BROKEN_FIXED + +#define JASSERT(cond) do {} while(0) + +#else + +#define JASSERT(cond) do { \ + if (!(cond)) { \ + mc_error("cjson assert failure %s:%d : cond \"" __STRING(cond) "failed\n", __FILE__, __LINE__); \ + *(int *)0 = 1;\ + abort(); \ + }\ + } while(0) + +#endif + +#define MC_ERROR(x, ...) mc_error(x, ##__VA_ARGS__) + +#ifdef MC_MAINTAINER_MODE +#define MC_SET_DEBUG(x) mc_set_debug(x) +#define MC_GET_DEBUG() mc_get_debug() +#define MC_SET_SYSLOG(x) mc_set_syslog(x) +#define MC_DEBUG(x, ...) mc_debug(x, ##__VA_ARGS__) +#define MC_INFO(x, ...) mc_info(x, ##__VA_ARGS__) +#else +#define MC_SET_DEBUG(x) if (0) mc_set_debug(x) +#define MC_GET_DEBUG() (0) +#define MC_SET_SYSLOG(x) if (0) mc_set_syslog(x) +#define MC_DEBUG(x, ...) if (0) mc_debug(x, ##__VA_ARGS__) +#define MC_INFO(x, ...) if (0) mc_info(x, ##__VA_ARGS__) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/components/json/json.h b/src/components/json/json.h new file mode 100644 index 0000000..4339b20 --- /dev/null +++ b/src/components/json/json.h @@ -0,0 +1,34 @@ +/* + * $Id: json.h,v 1.6 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_h_ +#define _json_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bits.h" +#include "debug.h" +#include "linkhash.h" +#include "arraylist.h" +#include "json_util.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_object_iterator.h" +#include "json_c_version.h" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/components/json/json.mk b/src/components/json/json.mk new file mode 100644 index 0000000..82d82ab --- /dev/null +++ b/src/components/json/json.mk @@ -0,0 +1,45 @@ + + +# standard component Makefile header +sp := $(sp).x +dirstack_$(sp) := $(d) +d := $(dir) + +# component specification + +OBJS_$(d) :=\ + $(OBJ_DIR)/arraylist.o\ + $(OBJ_DIR)/debug.o\ + $(OBJ_DIR)/json_c_version.o\ + $(OBJ_DIR)/json_object.o\ + $(OBJ_DIR)/json_object_iterator.o\ + $(OBJ_DIR)/json_tokener.o\ + $(OBJ_DIR)/json_util.o\ + $(OBJ_DIR)/libjson.o\ + $(OBJ_DIR)/linkhash.o\ + $(OBJ_DIR)/parse_flags.o\ + $(OBJ_DIR)/printbuf.o\ + $(OBJ_DIR)/json_checker.o\ + $(OBJ_DIR)/random_seed.o + + +CFLAGS_LOCAL += -I$(d) +$(OBJS_$(d)): CFLAGS_LOCAL := -std=gnu99 -g -O3\ + -I$(d) + + +# standard component Makefile rules + +DEPS_$(d) := $(OBJS_$(d):.o=.d) + +CLEAN_LIST := $(CLEAN_LIST) $(OBJS_$(d)) $(DEPS_$(d)) + +$(OBJ_DIR)/%.o: $(d)/%.c + $(COMPILE) + +-include $(DEPS_$(d)) + +# standard component Makefile footer + +d := $(dirstack_$(sp)) +sp := $(basename $(sp)) diff --git a/src/components/json/json_c_version.c b/src/components/json/json_c_version.c new file mode 100644 index 0000000..13eb188 --- /dev/null +++ b/src/components/json/json_c_version.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2012 Eric Haszlakiewicz + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ +#include "config.h" + +#include "json_c_version.h" + +const char *json_c_version(void) +{ + return JSON_C_VERSION; +} + +int json_c_version_num(void) +{ + return JSON_C_VERSION_NUM; +} + diff --git a/src/components/json/json_c_version.h b/src/components/json/json_c_version.h new file mode 100644 index 0000000..eed98a4 --- /dev/null +++ b/src/components/json/json_c_version.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2012 Eric Haszlakiewicz + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ + +#ifndef _json_c_version_h_ +#define _json_c_version_h_ + +#define JSON_C_MAJOR_VERSION 0 +#define JSON_C_MINOR_VERSION 12 +#define JSON_C_MICRO_VERSION 0 +#define JSON_C_VERSION_NUM ((JSON_C_MAJOR_VERSION << 16) | \ + (JSON_C_MINOR_VERSION << 8) | \ + JSON_C_MICRO_VERSION) +#define JSON_C_VERSION "0.12" + +const char *json_c_version(void); /* Returns JSON_C_VERSION */ +int json_c_version_num(void); /* Returns JSON_C_VERSION_NUM */ + +#endif diff --git a/src/components/json/json_checker.c b/src/components/json/json_checker.c new file mode 100644 index 0000000..33491ed --- /dev/null +++ b/src/components/json/json_checker.c @@ -0,0 +1,421 @@ +/* JSON_checker.c */ + +/* 2016-11-11 */ + +/* +Copyright (c) 2005 JSON.org + +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 shall be used for Good, not Evil. + +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 "json_checker.h" + +#define TRUE 1 +#define FALSE 0 +#define GOOD 0xBABAB00E +#define __ -1 /* the universal error code */ + +/* + Characters are mapped into these 31 character classes. This allows for + a significant reduction in the size of the state transition table. +*/ + +enum classes { + C_SPACE, /* space */ + C_WHITE, /* other whitespace */ + C_LCURB, /* { */ + C_RCURB, /* } */ + C_LSQRB, /* [ */ + C_RSQRB, /* ] */ + C_COLON, /* : */ + C_COMMA, /* , */ + C_QUOTE, /* " */ + C_BACKS, /* \ */ + C_SLASH, /* / */ + C_PLUS, /* + */ + C_MINUS, /* - */ + C_POINT, /* . */ + C_ZERO , /* 0 */ + C_DIGIT, /* 123456789 */ + C_LOW_A, /* a */ + C_LOW_B, /* b */ + C_LOW_C, /* c */ + C_LOW_D, /* d */ + C_LOW_E, /* e */ + C_LOW_F, /* f */ + C_LOW_L, /* l */ + C_LOW_N, /* n */ + C_LOW_R, /* r */ + C_LOW_S, /* s */ + C_LOW_T, /* t */ + C_LOW_U, /* u */ + C_ABCDF, /* ABCDF */ + C_E, /* E */ + C_ETC, /* everything else */ + NR_CLASSES +}; + +static int ascii_class[128] = { +/* + This array maps the 128 ASCII characters into character classes. + The remaining Unicode characters should be mapped to C_ETC. + Non-whitespace control characters are errors. +*/ + __, __, __, __, __, __, __, __, + __, C_WHITE, C_WHITE, __, __, C_WHITE, __, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + + C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH, + C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, + C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + + C_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC, + + C_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC, + C_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC +}; + + +/* + The state codes. +*/ +enum states { + GO, /* start */ + OK, /* ok */ + OB, /* object */ + KE, /* key */ + CO, /* colon */ + VA, /* value */ + AR, /* array */ + ST, /* string */ + ES, /* escape */ + U1, /* u1 */ + U2, /* u2 */ + U3, /* u3 */ + U4, /* u4 */ + MI, /* minus */ + ZE, /* zero */ + IN, /* integer */ + FR, /* fraction */ + FS, /* fraction */ + E1, /* e */ + E2, /* ex */ + E3, /* exp */ + T1, /* tr */ + T2, /* tru */ + T3, /* true */ + F1, /* fa */ + F2, /* fal */ + F3, /* fals */ + F4, /* false */ + N1, /* nu */ + N2, /* nul */ + N3, /* null */ + NR_STATES +}; + + +static int state_transition_table[NR_STATES][NR_CLASSES] = { +/* + The state transition table takes the current state and the current symbol, + and returns either a new state or an action. An action is represented as a + negative number. A JSON text is accepted if at the end of the text the + state is OK and if the mode is MODE_DONE. + + white 1-9 ABCDF etc + space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E |*/ +/*start GO*/ {GO,GO,-6,__,-5,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*ok OK*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*object OB*/ {OB,OB,__,-9,__,__,__,__,ST,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*key KE*/ {KE,KE,__,__,__,__,__,__,ST,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*colon CO*/ {CO,CO,__,__,__,__,-2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*value VA*/ {VA,VA,-6,__,-5,__,__,__,ST,__,__,__,MI,__,ZE,IN,__,__,__,__,__,F1,__,N1,__,__,T1,__,__,__,__}, +/*array AR*/ {AR,AR,-6,__,-5,-7,__,__,ST,__,__,__,MI,__,ZE,IN,__,__,__,__,__,F1,__,N1,__,__,T1,__,__,__,__}, +/*string ST*/ {ST,__,ST,ST,ST,ST,ST,ST,-4,ES,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST}, +/*escape ES*/ {__,__,__,__,__,__,__,__,ST,ST,ST,__,__,__,__,__,__,ST,__,__,__,ST,__,ST,ST,__,ST,U1,__,__,__}, +/*u1 U1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U2,U2,U2,U2,U2,U2,U2,U2,__,__,__,__,__,__,U2,U2,__}, +/*u2 U2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U3,U3,U3,U3,U3,U3,U3,U3,__,__,__,__,__,__,U3,U3,__}, +/*u3 U3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U4,U4,U4,U4,U4,U4,U4,U4,__,__,__,__,__,__,U4,U4,__}, +/*u4 U4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ST,ST,ST,ST,ST,ST,ST,ST,__,__,__,__,__,__,ST,ST,__}, +/*minus MI*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ZE,IN,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*zero ZE*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,FR,__,__,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__}, +/*int IN*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,FR,IN,IN,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__}, +/*frac FR*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,FS,FS,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*fracs FS*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,FS,FS,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__}, +/*e E1*/ {__,__,__,__,__,__,__,__,__,__,__,E2,E2,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*ex E2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*exp E3*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*tr T1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T2,__,__,__,__,__,__}, +/*tru T2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T3,__,__,__}, +/*true T3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__}, +/*fa F1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F2,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, +/*fal F2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F3,__,__,__,__,__,__,__,__}, +/*fals F3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F4,__,__,__,__,__}, +/*false F4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__}, +/*nu N1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N2,__,__,__}, +/*nul N2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N3,__,__,__,__,__,__,__,__}, +/*null N3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__} +}; + + +/* + These modes can be pushed on the stack. +*/ +enum modes { + MODE_ARRAY, + MODE_DONE, + MODE_KEY, + MODE_OBJECT +}; + +static void +destroy(JSON_checker jc) +{ +/* + Delete the JSON_checker object. +*/ + jc->valid = 0; + free((void*)jc->stack); + free((void*)jc); +} + + +static int +reject(JSON_checker jc) +{ +/* + Delete the JSON_checker object. +*/ + destroy(jc); + return FALSE; +} + + +static int +push(JSON_checker jc, int mode) +{ +/* + Push a mode onto the stack. Return false if there is overflow. +*/ + jc->top += 1; + if (jc->top >= jc->depth) { + return FALSE; + } + jc->stack[jc->top] = mode; + return TRUE; +} + + +static int +pop(JSON_checker jc, int mode) +{ +/* + Pop the stack, assuring that the current mode matches the expectation. + Return false if there is underflow or if the modes mismatch. +*/ + if (jc->top < 0 || jc->stack[jc->top] != mode) { + return FALSE; + } + jc->top -= 1; + return TRUE; +} + + +JSON_checker +new_JSON_checker(int depth) +{ +/* + new_JSON_checker starts the checking process by constructing a JSON_checker + object. It takes a depth parameter that restricts the level of maximum + nesting. + + To continue the process, call JSON_checker_char for each character in the + JSON text, and then call JSON_checker_done to obtain the final result. + These functions are fully reentrant. + + The JSON_checker object will be deleted by JSON_checker_done. + JSON_checker_char will delete the JSON_checker object if it sees an error. +*/ + JSON_checker jc = (JSON_checker)malloc(sizeof(struct JSON_checker_struct)); + jc->valid = GOOD; + jc->state = GO; + jc->depth = depth; + jc->top = -1; + jc->stack = (int*)calloc(depth, sizeof(int)); + push(jc, MODE_DONE); + return jc; +} + + +int +JSON_checker_char(JSON_checker jc, int next_char) +{ +/* + After calling new_JSON_checker, call this function for each character (or + partial character) in your JSON text. It can accept UTF-8, UTF-16, or + UTF-32. It returns TRUE if things are looking ok so far. If it rejects the + text, it destroys the JSON_checker object and returns false. +*/ + int next_class, next_state; +/* + Determine the character's class. +*/ + if (jc->valid != GOOD) { + return FALSE; + } + if (next_char < 0) { + return reject(jc); + } + if (next_char >= 128) { + next_class = C_ETC; + } else { + next_class = ascii_class[next_char]; + if (next_class <= __) { + return reject(jc); + } + } +/* + Get the next state from the state transition table. +*/ + next_state = state_transition_table[jc->state][next_class]; + if (next_state >= 0) { +/* + Change the state. +*/ + jc->state = next_state; +/* + Or perform one of the actions. +*/ + } else { + switch (next_state) { +/* empty } */ + case -9: + if (!pop(jc, MODE_KEY)) { + return reject(jc); + } + jc->state = OK; + break; + +/* } */ case -8: + if (!pop(jc, MODE_OBJECT)) { + return reject(jc); + } + jc->state = OK; + break; + +/* ] */ case -7: + if (!pop(jc, MODE_ARRAY)) { + return reject(jc); + } + jc->state = OK; + break; + +/* { */ case -6: + if (!push(jc, MODE_KEY)) { + return reject(jc); + } + jc->state = OB; + break; + +/* [ */ case -5: + if (!push(jc, MODE_ARRAY)) { + return reject(jc); + } + jc->state = AR; + break; + +/* " */ case -4: + switch (jc->stack[jc->top]) { + case MODE_KEY: + jc->state = CO; + break; + case MODE_ARRAY: + case MODE_OBJECT: + jc->state = OK; + break; + default: + return reject(jc); + } + break; + +/* , */ case -3: + switch (jc->stack[jc->top]) { + case MODE_OBJECT: +/* + A comma causes a flip from object mode to key mode. +*/ + if (!pop(jc, MODE_OBJECT) || !push(jc, MODE_KEY)) { + return reject(jc); + } + jc->state = KE; + break; + case MODE_ARRAY: + jc->state = VA; + break; + default: + return reject(jc); + } + break; + +/* : */ case -2: +/* + A colon causes a flip from key mode to object mode. +*/ + if (!pop(jc, MODE_KEY) || !push(jc, MODE_OBJECT)) { + return reject(jc); + } + jc->state = VA; + break; +/* + Bad action. +*/ + default: + return reject(jc); + } + } + return TRUE; +} + + +int +JSON_checker_done(JSON_checker jc) +{ +/* + The JSON_checker_done function should be called after all of the characters + have been processed, but only if every call to JSON_checker_char returned + true. This function deletes the JSON_checker and returns true if the JSON + text was accepted. +*/ + if (jc->valid != GOOD) { + return FALSE; + } + int result = jc->state == OK && pop(jc, MODE_DONE); + destroy(jc); + return result; +} diff --git a/src/components/json/json_checker.h b/src/components/json/json_checker.h new file mode 100644 index 0000000..7623c38 --- /dev/null +++ b/src/components/json/json_checker.h @@ -0,0 +1,39 @@ +/* JSON_checker.h */ + +/* 2016-11-11 */ + +/* + The JSON_checker_struct is used to hold the state of the JSON_checker + so that the code can be reentrant. +*/ + +typedef struct JSON_checker_struct { + int valid; + int state; + int depth; + int top; + int* stack; +} * JSON_checker; + + +extern JSON_checker new_JSON_checker(int depth); + +/* + Make a new JSON_checker. You indicate the maximum depth that is allowed. + It will return an object that you will pass to the other functions. + They will destroy the object for you. +*/ + +extern int JSON_checker_char(JSON_checker jc, int next_char); + +/* + You should call JSON_checker_char for each character of the JSON text. + It will return false if the text is not right. +*/ + +extern int JSON_checker_done(JSON_checker jc); + +/* + When there are no more JSON text characters, call JSON_checker_done. + It will return false if the text was not right. +*/ diff --git a/src/components/json/json_config.h b/src/components/json/json_config.h new file mode 100644 index 0000000..965ff1c --- /dev/null +++ b/src/components/json/json_config.h @@ -0,0 +1,4 @@ +/* json_config.h. Generated from json_config.h.in by configure. */ + +/* Define to 1 if you have the header file. */ +#define JSON_C_HAVE_INTTYPES_H 1 diff --git a/src/components/json/json_inttypes.h b/src/components/json/json_inttypes.h new file mode 100644 index 0000000..9de8d24 --- /dev/null +++ b/src/components/json/json_inttypes.h @@ -0,0 +1,28 @@ + +#ifndef _json_inttypes_h_ +#define _json_inttypes_h_ + +#include "json_config.h" + +#if defined(_MSC_VER) && _MSC_VER <= 1700 + +/* Anything less than Visual Studio C++ 10 is missing stdint.h and inttypes.h */ +typedef __int32 int32_t; +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX ((int32_t)_I32_MAX) +typedef __int64 int64_t; +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX ((int64_t)_I64_MAX) +#define PRId64 "I64d" +#define SCNd64 "I64d" + +#else + +#ifdef JSON_C_HAVE_INTTYPES_H +#include +#endif +/* inttypes.h includes stdint.h */ + +#endif + +#endif diff --git a/src/components/json/json_object.c b/src/components/json/json_object.c new file mode 100644 index 0000000..5083a9c --- /dev/null +++ b/src/components/json/json_object.c @@ -0,0 +1,868 @@ +/* + * $Id: json_object.c,v 1.17 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "printbuf.h" +#include "linkhash.h" +#include "arraylist.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_object_private.h" +#include "json_util.h" +#include "math_compat.h" + +#if !defined(HAVE_STRDUP) && defined(_MSC_VER) + /* MSC has the version as _strdup */ +# define strdup _strdup +#elif !defined(HAVE_STRDUP) +# error You do not have strdup on your system. +#endif /* HAVE_STRDUP */ + +#if !defined(HAVE_SNPRINTF) && defined(_MSC_VER) + /* MSC has the version as _snprintf */ +# define snprintf _snprintf +#elif !defined(HAVE_SNPRINTF) +# error You do not have snprintf on your system. +#endif /* HAVE_SNPRINTF */ + +// Don't define this. It's not thread-safe. +/* #define REFCOUNT_DEBUG 1 */ + +const char *json_number_chars = "0123456789.+-eE"; +const char *json_hex_chars = "0123456789abcdefABCDEF"; + +static void json_object_generic_delete(struct json_object* jso); +static struct json_object* json_object_new(enum json_type o_type); + +static json_object_to_json_string_fn json_object_object_to_json_string; +static json_object_to_json_string_fn json_object_boolean_to_json_string; +static json_object_to_json_string_fn json_object_int_to_json_string; +static json_object_to_json_string_fn json_object_double_to_json_string; +static json_object_to_json_string_fn json_object_string_to_json_string; +static json_object_to_json_string_fn json_object_array_to_json_string; + + +/* ref count debugging */ + +#ifdef REFCOUNT_DEBUG + +static struct lh_table *json_object_table; + +static void json_object_init(void) __attribute__ ((constructor)); +static void json_object_init(void) { + MC_DEBUG("json_object_init: creating object table\n"); + json_object_table = lh_kptr_table_new(128, "json_object_table", NULL); +} + +static void json_object_fini(void) __attribute__ ((destructor)); +static void json_object_fini(void) { + struct lh_entry *ent; + if(MC_GET_DEBUG()) { + if (json_object_table->count) { + MC_DEBUG("json_object_fini: %d referenced objects at exit\n", + json_object_table->count); + lh_foreach(json_object_table, ent) { + struct json_object* obj = (struct json_object*)ent->v; + MC_DEBUG("\t%s:%p\n", json_type_to_name(obj->o_type), obj); + } + } + } + MC_DEBUG("json_object_fini: freeing object table\n"); + lh_table_free(json_object_table); +} +#endif /* REFCOUNT_DEBUG */ + + +/* string escaping */ + +static int json_escape_str(struct printbuf *pb, char *str, int len) +{ + int pos = 0, start_offset = 0; + unsigned char c; + while (len--) { + c = str[pos]; + switch(c) { + case '\b': + case '\n': + case '\r': + case '\t': + case '\f': + case '"': + case '\\': + case '/': + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + if(c == '\b') printbuf_memappend(pb, "\\b", 2); + else if(c == '\n') printbuf_memappend(pb, "\\n", 2); + else if(c == '\r') printbuf_memappend(pb, "\\r", 2); + else if(c == '\t') printbuf_memappend(pb, "\\t", 2); + else if(c == '\f') printbuf_memappend(pb, "\\f", 2); + else if(c == '"') printbuf_memappend(pb, "\\\"", 2); + else if(c == '\\') printbuf_memappend(pb, "\\\\", 2); + else if(c == '/') printbuf_memappend(pb, "\\/", 2); + start_offset = ++pos; + break; + default: + if(c < ' ') { + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + sprintbuf(pb, "\\u00%c%c", + json_hex_chars[c >> 4], + json_hex_chars[c & 0xf]); + start_offset = ++pos; + } else pos++; + } + } + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + return 0; +} + + +/* reference counting */ + +extern struct json_object* json_object_get(struct json_object *jso) +{ + if(jso) { + jso->_ref_count++; + } + return jso; +} + +int json_object_put(struct json_object *jso) +{ + if(jso) + { + jso->_ref_count--; + if(!jso->_ref_count) + { + if (jso->_user_delete) + jso->_user_delete(jso, jso->_userdata); + jso->_delete(jso); + return 1; + } + } + return 0; +} + + +/* generic object construction and destruction parts */ + +static void json_object_generic_delete(struct json_object* jso) +{ +#ifdef REFCOUNT_DEBUG + MC_DEBUG("json_object_delete_%s: %p\n", + json_type_to_name(jso->o_type), jso); + lh_table_delete(json_object_table, jso); +#endif /* REFCOUNT_DEBUG */ + printbuf_free(jso->_pb); + free(jso); +} + +static struct json_object* json_object_new(enum json_type o_type) +{ + struct json_object *jso; + + jso = (struct json_object*)calloc(sizeof(struct json_object), 1); + if(!jso) return NULL; + jso->o_type = o_type; + jso->_ref_count = 1; + jso->_delete = &json_object_generic_delete; +#ifdef REFCOUNT_DEBUG + lh_table_insert(json_object_table, jso, jso); + MC_DEBUG("json_object_new_%s: %p\n", json_type_to_name(jso->o_type), jso); +#endif /* REFCOUNT_DEBUG */ + return jso; +} + + +/* type checking functions */ + +int json_object_is_type(struct json_object *jso, enum json_type type) +{ + if (!jso) + return (type == json_type_null); + return (jso->o_type == type); +} + +enum json_type json_object_get_type(struct json_object *jso) +{ + if (!jso) + return json_type_null; + return jso->o_type; +} + +/* set a custom conversion to string */ + +void json_object_set_serializer(json_object *jso, + json_object_to_json_string_fn to_string_func, + void *userdata, + json_object_delete_fn *user_delete) +{ + // First, clean up any previously existing user info + if (jso->_user_delete) + { + jso->_user_delete(jso, jso->_userdata); + } + jso->_userdata = NULL; + jso->_user_delete = NULL; + + if (to_string_func == NULL) + { + // Reset to the standard serialization function + switch(jso->o_type) + { + case json_type_null: + jso->_to_json_string = NULL; + break; + case json_type_boolean: + jso->_to_json_string = &json_object_boolean_to_json_string; + break; + case json_type_double: + jso->_to_json_string = &json_object_double_to_json_string; + break; + case json_type_int: + jso->_to_json_string = &json_object_int_to_json_string; + break; + case json_type_object: + jso->_to_json_string = &json_object_object_to_json_string; + break; + case json_type_array: + jso->_to_json_string = &json_object_array_to_json_string; + break; + case json_type_string: + jso->_to_json_string = &json_object_string_to_json_string; + break; + } + return; + } + + jso->_to_json_string = to_string_func; + jso->_userdata = userdata; + jso->_user_delete = user_delete; +} + + +/* extended conversion to string */ + +const char* json_object_to_json_string_ext(struct json_object *jso, int flags) +{ + if (!jso) + return "null"; + + if ((!jso->_pb) && !(jso->_pb = printbuf_new())) + return NULL; + + printbuf_reset(jso->_pb); + + if(jso->_to_json_string(jso, jso->_pb, 0, flags) < 0) + return NULL; + + return jso->_pb->buf; +} + +/* backwards-compatible conversion to string */ + +const char* json_object_to_json_string(struct json_object *jso) +{ + return json_object_to_json_string_ext(jso, JSON_C_TO_STRING_PLAIN); +} + +static void indent(struct printbuf *pb, int level, int flags) +{ + if (flags & JSON_C_TO_STRING_PRETTY) + { + printbuf_memset(pb, -1, ' ', level * 2); + } +} + +/* json_object_object */ + +static int json_object_object_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + int had_children = 0; + struct json_object_iter iter; + + sprintbuf(pb, "{" /*}*/); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + json_object_object_foreachC(jso, iter) + { + if (had_children) + { + sprintbuf(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED) + sprintbuf(pb, " "); + indent(pb, level+1, flags); + sprintbuf(pb, "\""); + json_escape_str(pb, iter.key, strlen(iter.key)); + if (flags & JSON_C_TO_STRING_SPACED) + sprintbuf(pb, "\": "); + else + sprintbuf(pb, "\":"); + if(iter.val == NULL) + sprintbuf(pb, "null"); + else + iter.val->_to_json_string(iter.val, pb, level+1,flags); + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + sprintbuf(pb, "\n"); + indent(pb,level,flags); + } + if (flags & JSON_C_TO_STRING_SPACED) + return sprintbuf(pb, /*{*/ " }"); + else + return sprintbuf(pb, /*{*/ "}"); +} + + +static void json_object_lh_entry_free(struct lh_entry *ent) +{ + free(ent->k); + json_object_put((struct json_object*)ent->v); +} + +static void json_object_object_delete(struct json_object* jso) +{ + lh_table_free(jso->o.c_object); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_object(void) +{ + struct json_object *jso = json_object_new(json_type_object); + if(!jso) return NULL; + jso->_delete = &json_object_object_delete; + jso->_to_json_string = &json_object_object_to_json_string; + jso->o.c_object = lh_kchar_table_new(JSON_OBJECT_DEF_HASH_ENTRIES, + NULL, &json_object_lh_entry_free); + return jso; +} + +struct lh_table* json_object_get_object(struct json_object *jso) +{ + if(!jso) return NULL; + switch(jso->o_type) { + case json_type_object: + return jso->o.c_object; + default: + return NULL; + } +} + +void json_object_object_add(struct json_object* jso, const char *key, + struct json_object *val) +{ + // We lookup the entry and replace the value, rather than just deleting + // and re-adding it, so the existing key remains valid. + json_object *existing_value = NULL; + struct lh_entry *existing_entry; + existing_entry = lh_table_lookup_entry(jso->o.c_object, (void*)key); + if (!existing_entry) + { + lh_table_insert(jso->o.c_object, strdup(key), val); + return; + } + existing_value = (void *)existing_entry->v; + if (existing_value) + json_object_put(existing_value); + existing_entry->v = val; +} + +int json_object_object_length(struct json_object *jso) +{ + return lh_table_length(jso->o.c_object); +} + +struct json_object* json_object_object_get(struct json_object* jso, const char *key) +{ + struct json_object *result = NULL; + json_object_object_get_ex(jso, key, &result); + return result; +} + +json_bool json_object_object_get_ex(struct json_object* jso, const char *key, struct json_object **value) +{ + if (value != NULL) + *value = NULL; + + if (NULL == jso) + return FALSE; + + switch(jso->o_type) + { + case json_type_object: + return lh_table_lookup_ex(jso->o.c_object, (void*)key, (void**)value); + default: + if (value != NULL) + *value = NULL; + return FALSE; + } +} + +void json_object_object_del(struct json_object* jso, const char *key) +{ + lh_table_delete(jso->o.c_object, key); +} + + +/* json_object_boolean */ + +static int json_object_boolean_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + level = level; + flags = flags; + if(jso->o.c_boolean) return sprintbuf(pb, "true"); + else return sprintbuf(pb, "false"); +} + +struct json_object* json_object_new_boolean(json_bool b) +{ + struct json_object *jso = json_object_new(json_type_boolean); + if(!jso) return NULL; + jso->_to_json_string = &json_object_boolean_to_json_string; + jso->o.c_boolean = b; + return jso; +} + +json_bool json_object_get_boolean(struct json_object *jso) +{ + if(!jso) return FALSE; + switch(jso->o_type) { + case json_type_boolean: + return jso->o.c_boolean; + case json_type_int: + return (jso->o.c_int64 != 0); + case json_type_double: + return (jso->o.c_double != 0); + case json_type_string: + return (jso->o.c_string.len != 0); + default: + return FALSE; + } +} + + +/* json_object_int */ + +static int json_object_int_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + level = level; + flags = flags; + return sprintbuf(pb, "%"PRId64, jso->o.c_int64); +} + +struct json_object* json_object_new_int(int32_t i) +{ + struct json_object *jso = json_object_new(json_type_int); + if(!jso) return NULL; + jso->_to_json_string = &json_object_int_to_json_string; + jso->o.c_int64 = i; + return jso; +} + +int32_t json_object_get_int(struct json_object *jso) +{ + int64_t cint64; + enum json_type o_type; + + if(!jso) return 0; + + o_type = jso->o_type; + cint64 = jso->o.c_int64; + + if (o_type == json_type_string) + { + /* + * Parse strings into 64-bit numbers, then use the + * 64-to-32-bit number handling below. + */ + if (json_parse_int64(jso->o.c_string.str, &cint64) != 0) + return 0; /* whoops, it didn't work. */ + o_type = json_type_int; + } + + switch(o_type) { + case json_type_int: + /* Make sure we return the correct values for out of range numbers. */ + if (cint64 <= INT32_MIN) + return INT32_MIN; + else if (cint64 >= INT32_MAX) + return INT32_MAX; + else + return (int32_t)cint64; + case json_type_double: + return (int32_t)jso->o.c_double; + case json_type_boolean: + return jso->o.c_boolean; + default: + return 0; + } +} + +struct json_object* json_object_new_int64(int64_t i) +{ + struct json_object *jso = json_object_new(json_type_int); + if(!jso) return NULL; + jso->_to_json_string = &json_object_int_to_json_string; + jso->o.c_int64 = i; + return jso; +} + +int64_t json_object_get_int64(struct json_object *jso) +{ + int64_t cint; + + if(!jso) return 0; + switch(jso->o_type) { + case json_type_int: + return jso->o.c_int64; + case json_type_double: + return (int64_t)jso->o.c_double; + case json_type_boolean: + return jso->o.c_boolean; + case json_type_string: + if (json_parse_int64(jso->o.c_string.str, &cint) == 0) return cint; + default: + return 0; + } +} + + +/* json_object_double */ + +static int json_object_double_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + level = level; + flags = flags; + char buf[128], *p, *q; + int size; + /* Although JSON RFC does not support + NaN or Infinity as numeric values + ECMA 262 section 9.8.1 defines + how to handle these cases as strings */ + if(isnan(jso->o.c_double)) + size = snprintf(buf, sizeof(buf), "NaN"); + else if(isinf(jso->o.c_double)) + if(jso->o.c_double > 0) + size = snprintf(buf, sizeof(buf), "Infinity"); + else + size = snprintf(buf, sizeof(buf), "-Infinity"); + else + size = snprintf(buf, sizeof(buf), "%.17g", jso->o.c_double); + + p = strchr(buf, ','); + if (p) { + *p = '.'; + } else { + p = strchr(buf, '.'); + } + if (p && (flags & JSON_C_TO_STRING_NOZERO)) { + /* last useful digit, always keep 1 zero */ + p++; + for (q=p ; *q ; q++) { + if (*q!='0') p=q; + } + /* drop trailing zeroes */ + *(++p) = 0; + size = p-buf; + } + printbuf_memappend(pb, buf, size); + return size; +} + +struct json_object* json_object_new_double(double d) +{ + struct json_object *jso = json_object_new(json_type_double); + if (!jso) + return NULL; + jso->_to_json_string = &json_object_double_to_json_string; + jso->o.c_double = d; + return jso; +} + +struct json_object* json_object_new_double_s(double d, const char *ds) +{ + struct json_object *jso = json_object_new_double(d); + if (!jso) + return NULL; + + json_object_set_serializer(jso, json_object_userdata_to_json_string, + strdup(ds), json_object_free_userdata); + return jso; +} + +int json_object_userdata_to_json_string(struct json_object *jso, + struct printbuf *pb, int level, int flags) +{ + level = level; + flags = flags; + + int userdata_len = strlen(jso->_userdata); + printbuf_memappend(pb, jso->_userdata, userdata_len); + return userdata_len; +} + +void json_object_free_userdata(struct json_object *jso, void *userdata) +{ + jso = jso; + free(userdata); +} + +double json_object_get_double(struct json_object *jso) +{ + double cdouble; + char *errPtr = NULL; + + if(!jso) return 0.0; + switch(jso->o_type) { + case json_type_double: + return jso->o.c_double; + case json_type_int: + return jso->o.c_int64; + case json_type_boolean: + return jso->o.c_boolean; + case json_type_string: + errno = 0; + cdouble = strtod(jso->o.c_string.str,&errPtr); + + /* if conversion stopped at the first character, return 0.0 */ + if (errPtr == jso->o.c_string.str) + return 0.0; + + /* + * Check that the conversion terminated on something sensible + * + * For example, { "pay" : 123AB } would parse as 123. + */ + if (*errPtr != '\0') + return 0.0; + + /* + * If strtod encounters a string which would exceed the + * capacity of a double, it returns +/- HUGE_VAL and sets + * errno to ERANGE. But +/- HUGE_VAL is also a valid result + * from a conversion, so we need to check errno. + * + * Underflow also sets errno to ERANGE, but it returns 0 in + * that case, which is what we will return anyway. + * + * See CERT guideline ERR30-C + */ + if ((HUGE_VAL == cdouble || -HUGE_VAL == cdouble) && + (ERANGE == errno)) + cdouble = 0.0; + return cdouble; + default: + return 0.0; + } +} + + +/* json_object_string */ + +static int json_object_string_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ +level = level; +flags = flags; + + sprintbuf(pb, "\""); + json_escape_str(pb, jso->o.c_string.str, jso->o.c_string.len); + sprintbuf(pb, "\""); + return 0; +} + +static void json_object_string_delete(struct json_object* jso) +{ + free(jso->o.c_string.str); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_string(const char *s) +{ + struct json_object *jso = json_object_new(json_type_string); + if(!jso) return NULL; + jso->_delete = &json_object_string_delete; + jso->_to_json_string = &json_object_string_to_json_string; + jso->o.c_string.str = strdup(s); + jso->o.c_string.len = strlen(s); + return jso; +} + +struct json_object* json_object_new_string_len(const char *s, int len) +{ + struct json_object *jso = json_object_new(json_type_string); + if(!jso) return NULL; + jso->_delete = &json_object_string_delete; + jso->_to_json_string = &json_object_string_to_json_string; + jso->o.c_string.str = (char*)malloc(len + 1); + memcpy(jso->o.c_string.str, (void *)s, len); + jso->o.c_string.str[len] = '\0'; + jso->o.c_string.len = len; + return jso; +} + +const char* json_object_get_string(struct json_object *jso) +{ + if(!jso) return NULL; + switch(jso->o_type) { + case json_type_string: + return jso->o.c_string.str; + default: + return json_object_to_json_string(jso); + } +} + +int json_object_get_string_len(struct json_object *jso) { + if(!jso) return 0; + switch(jso->o_type) { + case json_type_string: + return jso->o.c_string.len; + default: + return 0; + } +} + + +/* json_object_array */ + +static int json_object_array_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + int had_children = 0; + int ii; + sprintbuf(pb, "["); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + for(ii=0; ii < json_object_array_length(jso); ii++) + { + struct json_object *val; + if (had_children) + { + sprintbuf(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED) + sprintbuf(pb, " "); + indent(pb, level + 1, flags); + val = json_object_array_get_idx(jso, ii); + if(val == NULL) + sprintbuf(pb, "null"); + else + val->_to_json_string(val, pb, level+1, flags); + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + sprintbuf(pb, "\n"); + indent(pb,level,flags); + } + + if (flags & JSON_C_TO_STRING_SPACED) + return sprintbuf(pb, " ]"); + else + return sprintbuf(pb, "]"); +} + +static void json_object_array_entry_free(void *data) +{ + json_object_put((struct json_object*)data); +} + +static void json_object_array_delete(struct json_object* jso) +{ + array_list_free(jso->o.c_array); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_array(void) +{ + struct json_object *jso = json_object_new(json_type_array); + if(!jso) return NULL; + jso->_delete = &json_object_array_delete; + jso->_to_json_string = &json_object_array_to_json_string; + jso->o.c_array = array_list_new(&json_object_array_entry_free); + return jso; +} + +struct array_list* json_object_get_array(struct json_object *jso) +{ + if(!jso) return NULL; + switch(jso->o_type) { + case json_type_array: + return jso->o.c_array; + default: + return NULL; + } +} + +void json_object_array_sort(struct json_object *jso, int(*sort_fn)(const void *, const void *)) +{ + array_list_sort(jso->o.c_array, sort_fn); +} + +int json_object_array_length(struct json_object *jso) +{ + return array_list_length(jso->o.c_array); +} + +int json_object_array_add(struct json_object *jso,struct json_object *val) +{ + return array_list_add(jso->o.c_array, val); +} + +int json_object_array_put_idx(struct json_object *jso, int idx, + struct json_object *val) +{ + return array_list_put_idx(jso->o.c_array, idx, val); +} + +struct json_object* json_object_array_get_idx(struct json_object *jso, + int idx) +{ + return (struct json_object*)array_list_get_idx(jso->o.c_array, idx); +} + diff --git a/src/components/json/json_object.h b/src/components/json/json_object.h new file mode 100644 index 0000000..26e6b29 --- /dev/null +++ b/src/components/json/json_object.h @@ -0,0 +1,617 @@ +/* + * $Id: json_object.h,v 1.12 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_object_h_ +#define _json_object_h_ + +#ifdef __GNUC__ +#define THIS_FUNCTION_IS_DEPRECATED(func) func __attribute__ ((deprecated)) +#elif defined(_MSC_VER) +#define THIS_FUNCTION_IS_DEPRECATED(func) __declspec(deprecated) func +#else +#define THIS_FUNCTION_IS_DEPRECATED(func) func +#endif + +#include "json_inttypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_OBJECT_DEF_HASH_ENTRIES 16 + +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes the output + * to have no extra whitespace or formatting applied. + */ +#define JSON_C_TO_STRING_PLAIN 0 +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes the output to have + * minimal whitespace inserted to make things slightly more readable. + */ +#define JSON_C_TO_STRING_SPACED (1<<0) +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes + * the output to be formatted. + * + * See the "Two Space Tab" option at http://jsonformatter.curiousconcept.com/ + * for an example of the format. + */ +#define JSON_C_TO_STRING_PRETTY (1<<1) +/** + * A flag to drop trailing zero for float values + */ +#define JSON_C_TO_STRING_NOZERO (1<<2) + +#undef FALSE +#define FALSE ((json_bool)0) + +#undef TRUE +#define TRUE ((json_bool)1) + +extern const char *json_number_chars; +extern const char *json_hex_chars; + +/* CAW: added for ANSI C iteration correctness */ +struct json_object_iter +{ + char *key; + struct json_object *val; + struct lh_entry *entry; +}; + +/* forward structure definitions */ + +typedef int json_bool; +typedef struct printbuf printbuf; +typedef struct lh_table lh_table; +typedef struct array_list array_list; +typedef struct json_object json_object; +typedef struct json_object_iter json_object_iter; +typedef struct json_tokener json_tokener; + +/** + * Type of custom user delete functions. See json_object_set_serializer. + */ +typedef void (json_object_delete_fn)(struct json_object *jso, void *userdata); + +/** + * Type of a custom serialization function. See json_object_set_serializer. + */ +typedef int (json_object_to_json_string_fn)(struct json_object *jso, + struct printbuf *pb, + int level, + int flags); + +/* supported object types */ + +typedef enum json_type { + /* If you change this, be sure to update json_type_to_name() too */ + json_type_null, + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string, +} json_type; + +/* reference counting functions */ + +/** + * Increment the reference count of json_object, thereby grabbing shared + * ownership of obj. + * + * @param obj the json_object instance + */ +extern struct json_object* json_object_get(struct json_object *obj); + +/** + * Decrement the reference count of json_object and free if it reaches zero. + * You must have ownership of obj prior to doing this or you will cause an + * imbalance in the reference count. + * + * @param obj the json_object instance + * @returns 1 if the object was freed. + */ +int json_object_put(struct json_object *obj); + +/** + * Check if the json_object is of a given type + * @param obj the json_object instance + * @param type one of: + json_type_null (i.e. obj == NULL), + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string, + */ +extern int json_object_is_type(struct json_object *obj, enum json_type type); + +/** + * Get the type of the json_object. See also json_type_to_name() to turn this + * into a string suitable, for instance, for logging. + * + * @param obj the json_object instance + * @returns type being one of: + json_type_null (i.e. obj == NULL), + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string, + */ +extern enum json_type json_object_get_type(struct json_object *obj); + + +/** Stringify object to json format. + * Equivalent to json_object_to_json_string_ext(obj, JSON_C_TO_STRING_SPACED) + * @param obj the json_object instance + * @returns a string in JSON format + */ +extern const char* json_object_to_json_string(struct json_object *obj); + +/** Stringify object to json format + * @param obj the json_object instance + * @param flags formatting options, see JSON_C_TO_STRING_PRETTY and other constants + * @returns a string in JSON format + */ +extern const char* json_object_to_json_string_ext(struct json_object *obj, int +flags); + +/** + * Set a custom serialization function to be used when this particular object + * is converted to a string by json_object_to_json_string. + * + * If a custom serializer is already set on this object, any existing + * user_delete function is called before the new one is set. + * + * If to_string_func is NULL, the other parameters are ignored + * and the default behaviour is reset. + * + * The userdata parameter is optional and may be passed as NULL. If provided, + * it is passed to to_string_func as-is. This parameter may be NULL even + * if user_delete is non-NULL. + * + * The user_delete parameter is optional and may be passed as NULL, even if + * the userdata parameter is non-NULL. It will be called just before the + * json_object is deleted, after it's reference count goes to zero + * (see json_object_put()). + * If this is not provided, it is up to the caller to free the userdata at + * an appropriate time. (i.e. after the json_object is deleted) + * + * @param jso the object to customize + * @param to_string_func the custom serialization function + * @param userdata an optional opaque cookie + * @param user_delete an optional function from freeing userdata + */ +extern void json_object_set_serializer(json_object *jso, + json_object_to_json_string_fn to_string_func, + void *userdata, + json_object_delete_fn *user_delete); + +/** + * Simply call free on the userdata pointer. + * Can be used with json_object_set_serializer(). + * + * @param jso unused + * @param userdata the pointer that is passed to free(). + */ +json_object_delete_fn json_object_free_userdata; + +/** + * Copy the jso->_userdata string over to pb as-is. + * Can be used with json_object_set_serializer(). + * + * @param jso The object whose _userdata is used. + * @param pb The destination buffer. + * @param level Ignored. + * @param flags Ignored. + */ +json_object_to_json_string_fn json_object_userdata_to_json_string; + + +/* object type methods */ + +/** Create a new empty object with a reference count of 1. The caller of + * this object initially has sole ownership. Remember, when using + * json_object_object_add or json_object_array_put_idx, ownership will + * transfer to the object/array. Call json_object_get if you want to maintain + * shared ownership or also add this object as a child of multiple objects or + * arrays. Any ownerships you acquired but did not transfer must be released + * through json_object_put. + * + * @returns a json_object of type json_type_object + */ +extern struct json_object* json_object_new_object(void); + +/** Get the hashtable of a json_object of type json_type_object + * @param obj the json_object instance + * @returns a linkhash + */ +extern struct lh_table* json_object_get_object(struct json_object *obj); + +/** Get the size of an object in terms of the number of fields it has. + * @param obj the json_object whose length to return + */ +extern int json_object_object_length(struct json_object* obj); + +/** Add an object field to a json_object of type json_type_object + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object, independent of the lifetime of obj, you must wrap the + * passed object with json_object_get. + * + * Upon calling this, the ownership of val transfers to obj. Thus you must + * make sure that you do in fact have ownership over this object. For instance, + * json_object_new_object will give you ownership until you transfer it, + * whereas json_object_object_get does not. + * + * @param obj the json_object instance + * @param key the object field name (a private copy will be duplicated) + * @param val a json_object or NULL member to associate with the given field + */ +extern void json_object_object_add(struct json_object* obj, const char *key, + struct json_object *val); + +/** Get the json_object associate with a given object field + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of the returned value is retained + * by obj (do not do json_object_put unless you have done a json_object_get). + * If you delete the value from obj (json_object_object_del) and wish to access + * the returned reference afterwards, make sure you have first gotten shared + * ownership through json_object_get (& don't forget to do a json_object_put + * or transfer ownership to prevent a memory leak). + * + * @param obj the json_object instance + * @param key the object field name + * @returns the json_object associated with the given field name + * @deprecated Please use json_object_object_get_ex + */ + #if 0 + /** BY TSIHANG */ +THIS_FUNCTION_IS_DEPRECATED(extern struct json_object* json_object_object_get(struct json_object* obj, + const char *key)); +#else +extern struct json_object* json_object_object_get(struct json_object* obj, + const char *key); +#endif +/** Get the json_object associated with a given object field. + * + * This returns true if the key is found, false in all other cases (including + * if obj isn't a json_type_object). + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of value is retained by obj. + * + * @param obj the json_object instance + * @param key the object field name + * @param value a pointer where to store a reference to the json_object + * associated with the given field name. + * + * It is safe to pass a NULL value. + * @returns whether or not the key exists + */ +extern json_bool json_object_object_get_ex(struct json_object* obj, + const char *key, + struct json_object **value); + +/** Delete the given json_object field + * + * The reference count will be decremented for the deleted object. If there + * are no more owners of the value represented by this key, then the value is + * freed. Otherwise, the reference to the value will remain in memory. + * + * @param obj the json_object instance + * @param key the object field name + */ +extern void json_object_object_del(struct json_object* obj, const char *key); + +/** + * Iterate through all keys and values of an object. + * + * Adding keys to the object while iterating is NOT allowed. + * + * Deleting an existing key, or replacing an existing key with a + * new value IS allowed. + * + * @param obj the json_object instance + * @param key the local name for the char* key variable defined in the body + * @param val the local name for the json_object* object variable defined in + * the body + */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L + +# define json_object_object_foreach(obj,key,val) \ + char *key; \ + struct json_object *val __attribute__((__unused__)); \ + for(struct lh_entry *entry ## key = json_object_get_object(obj)->head, *entry_next ## key = NULL; \ + ({ if(entry ## key) { \ + key = (char*)entry ## key->k; \ + val = (struct json_object*)entry ## key->v; \ + entry_next ## key = entry ## key->next; \ + } ; entry ## key; }); \ + entry ## key = entry_next ## key ) + +#else /* ANSI C or MSC */ + +# define json_object_object_foreach(obj,key,val) \ + char *key;\ + struct json_object *val; \ + struct lh_entry *entry ## key; \ + struct lh_entry *entry_next ## key = NULL; \ + for(entry ## key = json_object_get_object(obj)->head; \ + (entry ## key ? ( \ + key = (char*)entry ## key->k, \ + val = (struct json_object*)entry ## key->v, \ + entry_next ## key = entry ## key->next, \ + entry ## key) : 0); \ + entry ## key = entry_next ## key) + +#endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L */ + +/** Iterate through all keys and values of an object (ANSI C Safe) + * @param obj the json_object instance + * @param iter the object iterator + */ +#define json_object_object_foreachC(obj,iter) \ + for(iter.entry = json_object_get_object(obj)->head; (iter.entry ? (iter.key = (char*)iter.entry->k, iter.val = (struct json_object*)iter.entry->v, iter.entry) : 0); iter.entry = iter.entry->next) + +/* Array type methods */ + +/** Create a new empty json_object of type json_type_array + * @returns a json_object of type json_type_array + */ +extern struct json_object* json_object_new_array(void); + +/** Get the arraylist of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an arraylist + */ +extern struct array_list* json_object_get_array(struct json_object *obj); + +/** Get the length of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an int + */ +extern int json_object_array_length(struct json_object *obj); + +/** Sorts the elements of jso of type json_type_array +* +* Pointers to the json_object pointers will be passed as the two arguments +* to @sort_fn +* +* @param obj the json_object instance +* @param sort_fn a sorting function +*/ +extern void json_object_array_sort(struct json_object *jso, int(*sort_fn)(const void *, const void *)); + +/** Add an element to the end of a json_object of type json_type_array + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * @param obj the json_object instance + * @param val the json_object to be added + */ +extern int json_object_array_add(struct json_object *obj, + struct json_object *val); + +/** Insert or replace an element at a specified index in an array (a json_object of type json_type_array) + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * The reference count of a replaced object will be decremented. + * + * The array size will be automatically be expanded to the size of the + * index if the index is larger than the current size. + * + * @param obj the json_object instance + * @param idx the index to insert the element at + * @param val the json_object to be added + */ +extern int json_object_array_put_idx(struct json_object *obj, int idx, + struct json_object *val); + +/** Get the element at specificed index of the array (a json_object of type json_type_array) + * @param obj the json_object instance + * @param idx the index to get the element at + * @returns the json_object at the specified index (or NULL) + */ +extern struct json_object* json_object_array_get_idx(struct json_object *obj, + int idx); + +/* json_bool type methods */ + +/** Create a new empty json_object of type json_type_boolean + * @param b a json_bool TRUE or FALSE (0 or 1) + * @returns a json_object of type json_type_boolean + */ +extern struct json_object* json_object_new_boolean(json_bool b); + +/** Get the json_bool value of a json_object + * + * The type is coerced to a json_bool if the passed object is not a json_bool. + * integer and double objects will return FALSE if there value is zero + * or TRUE otherwise. If the passed object is a string it will return + * TRUE if it has a non zero length. If any other object type is passed + * TRUE will be returned if the object is not NULL. + * + * @param obj the json_object instance + * @returns a json_bool + */ +extern json_bool json_object_get_boolean(struct json_object *obj); + + +/* int type methods */ + +/** Create a new empty json_object of type json_type_int + * Note that values are stored as 64-bit values internally. + * To ensure the full range is maintained, use json_object_new_int64 instead. + * @param i the integer + * @returns a json_object of type json_type_int + */ +extern struct json_object* json_object_new_int(int32_t i); + + +/** Create a new empty json_object of type json_type_int + * @param i the integer + * @returns a json_object of type json_type_int + */ +extern struct json_object* json_object_new_int64(int64_t i); + + +/** Get the int value of a json_object + * + * The type is coerced to a int if the passed object is not a int. + * double objects will return their integer conversion. Strings will be + * parsed as an integer. If no conversion exists then 0 is returned + * and errno is set to EINVAL. null is equivalent to 0 (no error values set) + * + * Note that integers are stored internally as 64-bit values. + * If the value of too big or too small to fit into 32-bit, INT32_MAX or + * INT32_MIN are returned, respectively. + * + * @param obj the json_object instance + * @returns an int + */ +extern int32_t json_object_get_int(struct json_object *obj); + +/** Get the int value of a json_object + * + * The type is coerced to a int64 if the passed object is not a int64. + * double objects will return their int64 conversion. Strings will be + * parsed as an int64. If no conversion exists then 0 is returned. + * + * NOTE: Set errno to 0 directly before a call to this function to determine + * whether or not conversion was successful (it does not clear the value for + * you). + * + * @param obj the json_object instance + * @returns an int64 + */ +extern int64_t json_object_get_int64(struct json_object *obj); + + +/* double type methods */ + +/** Create a new empty json_object of type json_type_double + * @param d the double + * @returns a json_object of type json_type_double + */ +extern struct json_object* json_object_new_double(double d); + +/** + * Create a new json_object of type json_type_double, using + * the exact serialized representation of the value. + * + * This allows for numbers that would otherwise get displayed + * inefficiently (e.g. 12.3 => "12.300000000000001") to be + * serialized with the more convenient form. + * + * Note: this is used by json_tokener_parse_ex() to allow for + * an exact re-serialization of a parsed object. + * + * An equivalent sequence of calls is: + * @code + * jso = json_object_new_double(d); + * json_object_set_serializer(d, json_object_userdata_to_json_string, + * strdup(ds), json_object_free_userdata) + * @endcode + * + * @param d the numeric value of the double. + * @param ds the string representation of the double. This will be copied. + */ +extern struct json_object* json_object_new_double_s(double d, const char *ds); + +/** Get the double floating point value of a json_object + * + * The type is coerced to a double if the passed object is not a double. + * integer objects will return their double conversion. Strings will be + * parsed as a double. If no conversion exists then 0.0 is returned and + * errno is set to EINVAL. null is equivalent to 0 (no error values set) + * + * If the value is too big to fit in a double, then the value is set to + * the closest infinity with errno set to ERANGE. If strings cannot be + * converted to their double value, then EINVAL is set & NaN is returned. + * + * Arrays of length 0 are interpreted as 0 (with no error flags set). + * Arrays of length 1 are effectively cast to the equivalent object and + * converted using the above rules. All other arrays set the error to + * EINVAL & return NaN. + * + * NOTE: Set errno to 0 directly before a call to this function to + * determine whether or not conversion was successful (it does not clear + * the value for you). + * + * @param obj the json_object instance + * @returns a double floating point number + */ +extern double json_object_get_double(struct json_object *obj); + + +/* string type methods */ + +/** Create a new empty json_object of type json_type_string + * + * A copy of the string is made and the memory is managed by the json_object + * + * @param s the string + * @returns a json_object of type json_type_string + */ +extern struct json_object* json_object_new_string(const char *s); + +extern struct json_object* json_object_new_string_len(const char *s, int len); + +/** Get the string value of a json_object + * + * If the passed object is not of type json_type_string then the JSON + * representation of the object is returned. + * + * The returned string memory is managed by the json_object and will + * be freed when the reference count of the json_object drops to zero. + * + * @param obj the json_object instance + * @returns a string + */ +extern const char* json_object_get_string(struct json_object *obj); + +/** Get the string length of a json_object + * + * If the passed object is not of type json_type_string then zero + * will be returned. + * + * @param obj the json_object instance + * @returns int + */ +extern int json_object_get_string_len(struct json_object *obj); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/components/json/json_object_iterator.c b/src/components/json/json_object_iterator.c new file mode 100644 index 0000000..7066649 --- /dev/null +++ b/src/components/json/json_object_iterator.c @@ -0,0 +1,168 @@ +/** +******************************************************************************* +* @file json_object_iterator.c +* +* Copyright (c) 2009-2012 Hewlett-Packard Development Company, L.P. +* +* This library is free software; you can redistribute it and/or modify +* it under the terms of the MIT license. See COPYING for details. +* +* @brief json-c forces clients to use its private data +* structures for JSON Object iteration. This API +* implementation corrects that by abstracting the +* private json-c details. +* +******************************************************************************* +*/ + +#include + +#include "json.h" +#include "json_object_private.h" + +#include "json_object_iterator.h" + +/** + * How It Works + * + * For each JSON Object, json-c maintains a linked list of zero + * or more lh_entry (link-hash entry) structures inside the + * Object's link-hash table (lh_table). + * + * Each lh_entry structure on the JSON Object's linked list + * represents a single name/value pair. The "next" field of the + * last lh_entry in the list is set to NULL, which terminates + * the list. + * + * We represent a valid iterator that refers to an actual + * name/value pair via a pointer to the pair's lh_entry + * structure set as the iterator's opaque_ field. + * + * We follow json-c's current pair list representation by + * representing a valid "end" iterator (one that refers past the + * last pair) with a NULL value in the iterator's opaque_ field. + * + * A JSON Object without any pairs in it will have the "head" + * field of its lh_table structure set to NULL. For such an + * object, json_object_iter_begin will return an iterator with + * the opaque_ field set to NULL, which is equivalent to the + * "end" iterator. + * + * When iterating, we simply update the iterator's opaque_ field + * to point to the next lh_entry structure in the linked list. + * opaque_ will become NULL once we iterate past the last pair + * in the list, which makes the iterator equivalent to the "end" + * iterator. + */ + +/// Our current representation of the "end" iterator; +/// +/// @note May not always be NULL +static const void* kObjectEndIterValue = NULL; + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_begin(struct json_object* obj) +{ + struct json_object_iterator iter; + struct lh_table* pTable; + + /// @note json_object_get_object will return NULL if passed NULL + /// or a non-json_type_object instance + pTable = json_object_get_object(obj); + JASSERT(NULL != pTable); + + /// @note For a pair-less Object, head is NULL, which matches our + /// definition of the "end" iterator + iter.opaque_ = pTable->head; + return iter; +} + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_end(const struct json_object* obj) +{ + struct json_object_iterator iter; + + JASSERT(NULL != obj); + JASSERT(json_object_is_type(obj, json_type_object)); + + iter.opaque_ = kObjectEndIterValue; + + return iter; +} + +/** + * **************************************************************************** + */ +void +json_object_iter_next(struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + iter->opaque_ = ((struct lh_entry *)iter->opaque_)->next; +} + + +/** + * **************************************************************************** + */ +const char* +json_object_iter_peek_name(const struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + return (const char*)(((struct lh_entry *)iter->opaque_)->k); +} + + +/** + * **************************************************************************** + */ +struct json_object* +json_object_iter_peek_value(const struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + return (struct json_object*)(((struct lh_entry *)iter->opaque_)->v); +} + + +/** + * **************************************************************************** + */ +json_bool +json_object_iter_equal(const struct json_object_iterator* iter1, + const struct json_object_iterator* iter2) +{ + JASSERT(NULL != iter1); + JASSERT(NULL != iter2); + + return (iter1->opaque_ == iter2->opaque_); +} + + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_init_default(void) +{ + struct json_object_iterator iter; + + /** + * @note Make this a negative, invalid value, such that + * accidental access to it would likely be trapped by the + * hardware as an invalid address. + */ + iter.opaque_ = NULL; + + return iter; +} diff --git a/src/components/json/json_object_iterator.h b/src/components/json/json_object_iterator.h new file mode 100644 index 0000000..44c9fb2 --- /dev/null +++ b/src/components/json/json_object_iterator.h @@ -0,0 +1,239 @@ +/** +******************************************************************************* +* @file json_object_iterator.h +* +* Copyright (c) 2009-2012 Hewlett-Packard Development Company, L.P. +* +* This library is free software; you can redistribute it and/or modify +* it under the terms of the MIT license. See COPYING for details. +* +* @brief json-c forces clients to use its private data +* structures for JSON Object iteration. This API +* corrects that by abstracting the private json-c +* details. +* +* API attributes:
+* * Thread-safe: NO
+* * Reentrant: NO +* +******************************************************************************* +*/ + + +#ifndef JSON_OBJECT_ITERATOR_H +#define JSON_OBJECT_ITERATOR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Forward declaration for the opaque iterator information. + */ +struct json_object_iter_info_; + +/** + * The opaque iterator that references a name/value pair within + * a JSON Object instance or the "end" iterator value. + */ +struct json_object_iterator { + const void* opaque_; +}; + + +/** + * forward declaration of json-c's JSON value instance structure + */ +struct json_object; + + +/** + * Initializes an iterator structure to a "default" value that + * is convenient for initializing an iterator variable to a + * default state (e.g., initialization list in a class' + * constructor). + * + * @code + * struct json_object_iterator iter = json_object_iter_init_default(); + * MyClass() : iter_(json_object_iter_init_default()) + * @endcode + * + * @note The initialized value doesn't reference any specific + * pair, is considered an invalid iterator, and MUST NOT + * be passed to any json-c API that expects a valid + * iterator. + * + * @note User and internal code MUST NOT make any assumptions + * about and dependencies on the value of the "default" + * iterator value. + * + * @return json_object_iterator + */ +struct json_object_iterator +json_object_iter_init_default(void); + +/** Retrieves an iterator to the first pair of the JSON Object. + * + * @warning Any modification of the underlying pair invalidates all + * iterators to that pair. + * + * @param obj JSON Object instance (MUST be of type json_object) + * + * @return json_object_iterator If the JSON Object has at + * least one pair, on return, the iterator refers + * to the first pair. If the JSON Object doesn't + * have any pairs, the returned iterator is + * equivalent to the "end" iterator for the same + * JSON Object instance. + * + * @code + * struct json_object_iterator it; + * struct json_object_iterator itEnd; + * struct json_object* obj; + * + * obj = json_tokener_parse("{'first':'george', 'age':100}"); + * it = json_object_iter_begin(obj); + * itEnd = json_object_iter_end(obj); + * + * while (!json_object_iter_equal(&it, &itEnd)) { + * printf("%s\n", + * json_object_iter_peek_name(&it)); + * json_object_iter_next(&it); + * } + * + * @endcode + */ +struct json_object_iterator +json_object_iter_begin(struct json_object* obj); + +/** Retrieves the iterator that represents the position beyond the + * last pair of the given JSON Object instance. + * + * @warning Do NOT write code that assumes that the "end" + * iterator value is NULL, even if it is so in a + * particular instance of the implementation. + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The "end" iterator and the + * equality test method, on the other hand, permit us to + * cleanly abstract pretty much any reasonable underlying + * representation without burdening the iterator + * structure with unnecessary data. + * + * @note For performance reasons, memorize the "end" iterator prior + * to any loop. + * + * @param obj JSON Object instance (MUST be of type json_object) + * + * @return json_object_iterator On return, the iterator refers + * to the "end" of the Object instance's pairs + * (i.e., NOT the last pair, but "beyond the last + * pair" value) + */ +struct json_object_iterator +json_object_iter_end(const struct json_object* obj); + +/** Returns an iterator to the next pair, if any + * + * @warning Any modification of the underlying pair + * invalidates all iterators to that pair. + * + * @param iter [IN/OUT] Pointer to iterator that references a + * name/value pair; MUST be a valid, non-end iterator. + * WARNING: bad things will happen if invalid or "end" + * iterator is passed. Upon return will contain the + * reference to the next pair if there is one; if there + * are no more pairs, will contain the "end" iterator + * value, which may be compared against the return value + * of json_object_iter_end() for the same JSON Object + * instance. + */ +void +json_object_iter_next(struct json_object_iterator* iter); + + +/** Returns a const pointer to the name of the pair referenced + * by the given iterator. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * + * @warning bad things will happen if an invalid or + * "end" iterator is passed. + * + * @return const char* Pointer to the name of the referenced + * name/value pair. The name memory belongs to the + * name/value pair, will be freed when the pair is + * deleted or modified, and MUST NOT be modified or + * freed by the user. + */ +const char* +json_object_iter_peek_name(const struct json_object_iterator* iter); + + +/** Returns a pointer to the json-c instance representing the + * value of the referenced name/value pair, without altering + * the instance's reference count. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * + * @warning bad things will happen if invalid or + * "end" iterator is passed. + * + * @return struct json_object* Pointer to the json-c value + * instance of the referenced name/value pair; the + * value's reference count is not changed by this + * function: if you plan to hold on to this json-c node, + * take a look at json_object_get() and + * json_object_put(). IMPORTANT: json-c API represents + * the JSON Null value as a NULL json_object instance + * pointer. + */ +struct json_object* +json_object_iter_peek_value(const struct json_object_iterator* iter); + + +/** Tests two iterators for equality. Typically used to test + * for end of iteration by comparing an iterator to the + * corresponding "end" iterator (that was derived from the same + * JSON Object instance). + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The equality test method, on + * the other hand, permits us to cleanly abstract pretty + * much any reasonable underlying representation. + * + * @param iter1 Pointer to first valid, non-NULL iterator + * @param iter2 POinter to second valid, non-NULL iterator + * + * @warning if a NULL iterator pointer or an uninitialized + * or invalid iterator, or iterators derived from + * different JSON Object instances are passed, bad things + * will happen! + * + * @return json_bool non-zero if iterators are equal (i.e., both + * reference the same name/value pair or are both at + * "end"); zero if they are not equal. + */ +json_bool +json_object_iter_equal(const struct json_object_iterator* iter1, + const struct json_object_iterator* iter2); + + +#ifdef __cplusplus +} +#endif + + +#endif /* JSON_OBJECT_ITERATOR_H */ diff --git a/src/components/json/json_object_private.h b/src/components/json/json_object_private.h new file mode 100644 index 0000000..5ed791b --- /dev/null +++ b/src/components/json/json_object_private.h @@ -0,0 +1,47 @@ +/* + * $Id: json_object_private.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_object_private_h_ +#define _json_object_private_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (json_object_private_delete_fn)(struct json_object *o); + +struct json_object +{ + enum json_type o_type; + json_object_private_delete_fn *_delete; + json_object_to_json_string_fn *_to_json_string; + int _ref_count; + struct printbuf *_pb; + union data { + json_bool c_boolean; + double c_double; + int64_t c_int64; + struct lh_table *c_object; + struct array_list *c_array; + struct { + char *str; + int len; + } c_string; + } o; + json_object_delete_fn *_user_delete; + void *_userdata; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/components/json/json_tokener.c b/src/components/json/json_tokener.c new file mode 100644 index 0000000..7cd3af3 --- /dev/null +++ b/src/components/json/json_tokener.c @@ -0,0 +1,888 @@ +/* + * $Id: json_tokener.c,v 1.20 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" +#include "arraylist.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" + +#ifdef HAVE_LOCALE_H +#include +#endif /* HAVE_LOCALE_H */ + +#if !HAVE_STRDUP && defined(_MSC_VER) + /* MSC has the version as _strdup */ +# define strdup _strdup +#elif !HAVE_STRDUP +# error You do not have strdup on your system. +#endif /* HAVE_STRDUP */ + +#if !HAVE_STRNCASECMP && defined(_MSC_VER) + /* MSC has the version as _strnicmp */ +# define strncasecmp _strnicmp +#elif !HAVE_STRNCASECMP +# error You do not have strncasecmp on your system. +#endif /* HAVE_STRNCASECMP */ + +/* Use C99 NAN by default; if not available, nan("") should work too. */ +#ifndef NAN +#define NAN nan("") +#endif /* !NAN */ + +static const char json_null_str[] = "null"; +static const int json_null_str_len = sizeof(json_null_str) - 1; +static const char json_inf_str[] = "Infinity"; +static const int json_inf_str_len = sizeof(json_inf_str) - 1; +static const char json_nan_str[] = "NaN"; +static const int json_nan_str_len = sizeof(json_nan_str) - 1; +static const char json_true_str[] = "true"; +static const int json_true_str_len = sizeof(json_true_str) - 1; +static const char json_false_str[] = "false"; +static const int json_false_str_len = sizeof(json_false_str) - 1; + +static const char* json_tokener_errors[] = { + "success", + "continue", + "nesting too deep", + "unexpected end of data", + "unexpected character", + "null expected", + "boolean expected", + "number expected", + "array value separator ',' expected", + "quoted object property name expected", + "object property name separator ':' expected", + "object value separator ',' expected", + "invalid string sequence", + "expected comment", + "buffer size overflow" +}; + +const char *json_tokener_error_desc(enum json_tokener_error jerr) +{ + int jerr_int = (int)jerr; + if (jerr_int < 0 || jerr_int >= (int)(sizeof(json_tokener_errors) / sizeof(json_tokener_errors[0]))) + return "Unknown error, invalid json_tokener_error value passed to json_tokener_error_desc()"; + return json_tokener_errors[jerr]; +} + +enum json_tokener_error json_tokener_get_error(json_tokener *tok) +{ + return tok->err; +} + +/* Stuff for decoding unicode sequences */ +#define IS_HIGH_SURROGATE(uc) (((uc) & 0xFC00) == 0xD800) +#define IS_LOW_SURROGATE(uc) (((uc) & 0xFC00) == 0xDC00) +#define DECODE_SURROGATE_PAIR(hi,lo) ((((hi) & 0x3FF) << 10) + ((lo) & 0x3FF) + 0x10000) +static unsigned char utf8_replacement_char[3] = { 0xEF, 0xBF, 0xBD }; + +struct json_tokener* json_tokener_new_ex(int depth) +{ + struct json_tokener *tok; + + tok = (struct json_tokener*)calloc(1, sizeof(struct json_tokener)); + if (!tok) return NULL; + tok->stack = (struct json_tokener_srec *)calloc(depth, sizeof(struct json_tokener_srec)); + if (!tok->stack) { + free(tok); + return NULL; + } + tok->pb = printbuf_new(); + tok->max_depth = depth; + json_tokener_reset(tok); + return tok; +} + +struct json_tokener* json_tokener_new(void) +{ + return json_tokener_new_ex(JSON_TOKENER_DEFAULT_DEPTH); +} + +void json_tokener_free(struct json_tokener *tok) +{ + json_tokener_reset(tok); + if (tok->pb) printbuf_free(tok->pb); + if (tok->stack) free(tok->stack); + free(tok); +} + +static void json_tokener_reset_level(struct json_tokener *tok, int depth) +{ + tok->stack[depth].state = json_tokener_state_eatws; + tok->stack[depth].saved_state = json_tokener_state_start; + json_object_put(tok->stack[depth].current); + tok->stack[depth].current = NULL; + free(tok->stack[depth].obj_field_name); + tok->stack[depth].obj_field_name = NULL; +} + +void json_tokener_reset(struct json_tokener *tok) +{ + int i; + if (!tok) + return; + + for(i = tok->depth; i >= 0; i--) + json_tokener_reset_level(tok, i); + tok->depth = 0; + tok->err = json_tokener_success; +} + +struct json_object* json_tokener_parse(const char *str) +{ + enum json_tokener_error jerr_ignored; + struct json_object* obj; + obj = json_tokener_parse_verbose(str, &jerr_ignored); + return obj; +} + +struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error) +{ + struct json_tokener* tok; + struct json_object* obj; + + tok = json_tokener_new(); + if (!tok) + return NULL; + obj = json_tokener_parse_ex(tok, str, -1); + *error = tok->err; + if(tok->err != json_tokener_success) { + if (obj != NULL) + json_object_put(obj); + obj = NULL; + } + + json_tokener_free(tok); + return obj; +} + +#define state tok->stack[tok->depth].state +#define saved_state tok->stack[tok->depth].saved_state +#define current tok->stack[tok->depth].current +#define obj_field_name tok->stack[tok->depth].obj_field_name + +/* Optimization: + * json_tokener_parse_ex() consumed a lot of CPU in its main loop, + * iterating character-by character. A large performance boost is + * achieved by using tighter loops to locally handle units such as + * comments and strings. Loops that handle an entire token within + * their scope also gather entire strings and pass them to + * printbuf_memappend() in a single call, rather than calling + * printbuf_memappend() one char at a time. + * + * PEEK_CHAR() and ADVANCE_CHAR() macros are used for code that is + * common to both the main loop and the tighter loops. + */ + +/* PEEK_CHAR(dest, tok) macro: + * Peeks at the current char and stores it in dest. + * Returns 1 on success, sets tok->err and returns 0 if no more chars. + * Implicit inputs: str, len vars + */ +#define PEEK_CHAR(dest, tok) \ + (((tok)->char_offset == len) ? \ + (((tok)->depth == 0 && state == json_tokener_state_eatws && saved_state == json_tokener_state_finish) ? \ + (((tok)->err = json_tokener_success), 0) \ + : \ + (((tok)->err = json_tokener_continue), 0) \ + ) : \ + (((dest) = *str), 1) \ + ) + +/* ADVANCE_CHAR() macro: + * Incrementes str & tok->char_offset. + * For convenience of existing conditionals, returns the old value of c (0 on eof) + * Implicit inputs: c var + */ +#define ADVANCE_CHAR(str, tok) \ + ( ++(str), ((tok)->char_offset)++, c) + + +/* End optimization macro defs */ + + +struct json_object* json_tokener_parse_ex(struct json_tokener *tok, + const char *str, int len) +{ + struct json_object *obj = NULL; + char c = '\1'; +#ifdef HAVE_SETLOCALE + char *oldlocale=NULL, *tmplocale; + + tmplocale = setlocale(LC_NUMERIC, NULL); + if (tmplocale) oldlocale = strdup(tmplocale); + setlocale(LC_NUMERIC, "C"); +#endif + + tok->char_offset = 0; + tok->err = json_tokener_success; + + /* this interface is presently not 64-bit clean due to the int len argument + and the internal printbuf interface that takes 32-bit int len arguments + so the function limits the maximum string size to INT32_MAX (2GB). + If the function is called with len == -1 then strlen is called to check + the string length is less than INT32_MAX (2GB) */ + if ((len < -1) || (len == -1 && strlen(str) > INT32_MAX)) { + tok->err = json_tokener_error_size; + return NULL; + } + + while (PEEK_CHAR(c, tok)) { + + redo_char: + switch(state) { + + case json_tokener_state_eatws: + /* Advance until we change state */ + while (isspace((int)c)) { + if ((!ADVANCE_CHAR(str, tok)) || (!PEEK_CHAR(c, tok))) + goto out; + } + if(c == '/' && !(tok->flags & JSON_TOKENER_STRICT)) { + printbuf_reset(tok->pb); + printbuf_memappend_fast(tok->pb, &c, 1); + state = json_tokener_state_comment_start; + } else { + state = saved_state; + goto redo_char; + } + break; + + case json_tokener_state_start: + switch(c) { + case '{': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_object_field_start; + current = json_object_new_object(); + break; + case '[': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_array; + current = json_object_new_array(); + break; + case 'I': + case 'i': + state = json_tokener_state_inf; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case 'N': + case 'n': + state = json_tokener_state_null; // or NaN + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case '\'': + if (tok->flags & JSON_TOKENER_STRICT) { + /* in STRICT mode only double-quote are allowed */ + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + case '"': + state = json_tokener_state_string; + printbuf_reset(tok->pb); + tok->quote_char = c; + break; + case 'T': + case 't': + case 'F': + case 'f': + state = json_tokener_state_boolean; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; +#if defined(__GNUC__) + case '0' ... '9': +#else + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': +#endif + case '-': + state = json_tokener_state_number; + printbuf_reset(tok->pb); + tok->is_double = 0; + goto redo_char; + default: + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + break; + + case json_tokener_state_finish: + if(tok->depth == 0) goto out; + obj = json_object_get(current); + json_tokener_reset_level(tok, tok->depth); + tok->depth--; + goto redo_char; + + case json_tokener_state_inf: /* aka starts with 'i' */ + { + int size; + int size_inf; + int is_negative = 0; + size = size; + printbuf_memappend_fast(tok->pb, &c, 1); + size = json_min(tok->st_pos+1, json_null_str_len); + size_inf = json_min(tok->st_pos+1, json_inf_str_len); + char *infbuf = tok->pb->buf; + if (*infbuf == '-') + { + infbuf++; + is_negative = 1; + } + if ((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_inf_str, infbuf, size_inf) == 0) || + (strncmp(json_inf_str, infbuf, size_inf) == 0) + ) + { + if (tok->st_pos == json_inf_str_len) + { + current = json_object_new_double(is_negative ? -INFINITY : INFINITY); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + tok->st_pos++; + } + break; + case json_tokener_state_null: /* aka starts with 'n' */ + { + int size; + int size_nan; + printbuf_memappend_fast(tok->pb, &c, 1); + size = json_min(tok->st_pos+1, json_null_str_len); + size_nan = json_min(tok->st_pos+1, json_nan_str_len); + if((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_null_str, tok->pb->buf, size) == 0) + || (strncmp(json_null_str, tok->pb->buf, size) == 0) + ) { + if (tok->st_pos == json_null_str_len) { + current = NULL; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } + else if ((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_nan_str, tok->pb->buf, size_nan) == 0) || + (strncmp(json_nan_str, tok->pb->buf, size_nan) == 0) + ) + { + if (tok->st_pos == json_nan_str_len) + { + current = json_object_new_double(NAN); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_null; + goto out; + } + tok->st_pos++; + } + break; + + case json_tokener_state_comment_start: + if(c == '*') { + state = json_tokener_state_comment; + } else if(c == '/') { + state = json_tokener_state_comment_eol; + } else { + tok->err = json_tokener_error_parse_comment; + goto out; + } + printbuf_memappend_fast(tok->pb, &c, 1); + break; + + case json_tokener_state_comment: + { + /* Advance until we change state */ + const char *case_start = str; + while(c != '*') { + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + printbuf_memappend_fast(tok->pb, case_start, 1+str-case_start); + state = json_tokener_state_comment_end; + } + break; + + case json_tokener_state_comment_eol: + { + /* Advance until we change state */ + const char *case_start = str; + while(c != '\n') { + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } + break; + + case json_tokener_state_comment_end: + printbuf_memappend_fast(tok->pb, &c, 1); + if(c == '/') { + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } else { + state = json_tokener_state_comment; + } + break; + + case json_tokener_state_string: + { + /* Advance until we change state */ + const char *case_start = str; + while(1) { + if(c == tok->quote_char) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + current = json_object_new_string_len(tok->pb->buf, tok->pb->bpos); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + break; + } else if(c == '\\') { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + saved_state = json_tokener_state_string; + state = json_tokener_state_string_escape; + break; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + } + break; + + case json_tokener_state_string_escape: + switch(c) { + case '"': + case '\\': + case '/': + printbuf_memappend_fast(tok->pb, &c, 1); + state = saved_state; + break; + case 'b': + case 'n': + case 'r': + case 't': + case 'f': + if(c == 'b') printbuf_memappend_fast(tok->pb, "\b", 1); + else if(c == 'n') printbuf_memappend_fast(tok->pb, "\n", 1); + else if(c == 'r') printbuf_memappend_fast(tok->pb, "\r", 1); + else if(c == 't') printbuf_memappend_fast(tok->pb, "\t", 1); + else if(c == 'f') printbuf_memappend_fast(tok->pb, "\f", 1); + state = saved_state; + break; + case 'u': + tok->ucs_char = 0; + tok->st_pos = 0; + state = json_tokener_state_escape_unicode; + break; + default: + tok->err = json_tokener_error_parse_string; + goto out; + } + break; + + case json_tokener_state_escape_unicode: + { + unsigned int got_hi_surrogate = 0; + + /* Handle a 4-byte sequence, or two sequences if a surrogate pair */ + while(1) { + if(strchr(json_hex_chars, c)) { + tok->ucs_char += ((unsigned int)hexdigit(c) << ((3-tok->st_pos++)*4)); + if(tok->st_pos == 4) { + unsigned char unescaped_utf[4]; + + if (got_hi_surrogate) { + if (IS_LOW_SURROGATE(tok->ucs_char)) { + /* Recalculate the ucs_char, then fall thru to process normally */ + tok->ucs_char = DECODE_SURROGATE_PAIR(got_hi_surrogate, tok->ucs_char); + } else { + /* Hi surrogate was not followed by a low surrogate */ + /* Replace the hi and process the rest normally */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + got_hi_surrogate = 0; + } + + if (tok->ucs_char < 0x80) { + unescaped_utf[0] = tok->ucs_char; + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 1); + } else if (tok->ucs_char < 0x800) { + unescaped_utf[0] = 0xc0 | (tok->ucs_char >> 6); + unescaped_utf[1] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 2); + } else if (IS_HIGH_SURROGATE(tok->ucs_char)) { + /* Got a high surrogate. Remember it and look for the + * the beginning of another sequence, which should be the + * low surrogate. + */ + got_hi_surrogate = tok->ucs_char; + /* Not at end, and the next two chars should be "\u" */ + if ((tok->char_offset+1 != len) && + (tok->char_offset+2 != len) && + (str[1] == '\\') && + (str[2] == 'u')) + { + /* Advance through the 16 bit surrogate, and move on to the + * next sequence. The next step is to process the following + * characters. + */ + if( !ADVANCE_CHAR(str, tok) || !ADVANCE_CHAR(str, tok) ) { + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + /* Advance to the first char of the next sequence and + * continue processing with the next sequence. + */ + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + goto out; + } + tok->ucs_char = 0; + tok->st_pos = 0; + continue; /* other json_tokener_state_escape_unicode */ + } else { + /* Got a high surrogate without another sequence following + * it. Put a replacement char in for the hi surrogate + * and pretend we finished. + */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + } else if (IS_LOW_SURROGATE(tok->ucs_char)) { + /* Got a low surrogate not preceded by a high */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } else if (tok->ucs_char < 0x10000) { + unescaped_utf[0] = 0xe0 | (tok->ucs_char >> 12); + unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + unescaped_utf[2] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 3); + } else if (tok->ucs_char < 0x110000) { + unescaped_utf[0] = 0xf0 | ((tok->ucs_char >> 18) & 0x07); + unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 12) & 0x3f); + unescaped_utf[2] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + unescaped_utf[3] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 4); + } else { + /* Don't know what we got--insert the replacement char */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + state = saved_state; + break; + } + } else { + tok->err = json_tokener_error_parse_string; + goto out; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + if (got_hi_surrogate) /* Clean up any pending chars */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + goto out; + } + } + } + break; + + case json_tokener_state_boolean: + { + int size1, size2; + printbuf_memappend_fast(tok->pb, &c, 1); + size1 = json_min(tok->st_pos+1, json_true_str_len); + size2 = json_min(tok->st_pos+1, json_false_str_len); + if((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_true_str, tok->pb->buf, size1) == 0) + || (strncmp(json_true_str, tok->pb->buf, size1) == 0) + ) { + if(tok->st_pos == json_true_str_len) { + current = json_object_new_boolean(1); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else if((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_false_str, tok->pb->buf, size2) == 0) + || (strncmp(json_false_str, tok->pb->buf, size2) == 0)) { + if(tok->st_pos == json_false_str_len) { + current = json_object_new_boolean(0); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_boolean; + goto out; + } + tok->st_pos++; + } + break; + + case json_tokener_state_number: + { + /* Advance until we change state */ + const char *case_start = str; + int case_len=0; + while(c && strchr(json_number_chars, c)) { + ++case_len; + if(c == '.' || c == 'e' || c == 'E') + tok->is_double = 1; + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, case_len); + goto out; + } + } + if (case_len>0) + printbuf_memappend_fast(tok->pb, case_start, case_len); + + // Check for -Infinity + if (tok->pb->buf[0] == '-' && case_len == 1 && + (c == 'i' || c == 'I')) + { + state = json_tokener_state_inf; + goto redo_char; + } + } + { + int64_t num64; + double numd; + if (!tok->is_double && json_parse_int64(tok->pb->buf, &num64) == 0) { + if (num64 && tok->pb->buf[0]=='0' && (tok->flags & JSON_TOKENER_STRICT)) { + /* in strict mode, number must not start with 0 */ + tok->err = json_tokener_error_parse_number; + goto out; + } + current = json_object_new_int64(num64); + } + else if(tok->is_double && json_parse_double(tok->pb->buf, &numd) == 0) + { + current = json_object_new_double_s(numd, tok->pb->buf); + } else { + tok->err = json_tokener_error_parse_number; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + break; + + case json_tokener_state_array_after_sep: + case json_tokener_state_array: + if(c == ']') { + if (state == json_tokener_state_array_after_sep && + (tok->flags & JSON_TOKENER_STRICT)) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else { + if(tok->depth >= tok->max_depth-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_array_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + } + break; + + case json_tokener_state_array_add: + json_object_array_add(current, obj); + saved_state = json_tokener_state_array_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_array_sep: + if(c == ']') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_array_after_sep; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_array; + goto out; + } + break; + + case json_tokener_state_object_field_start: + case json_tokener_state_object_field_start_after_sep: + if(c == '}') { + if (state == json_tokener_state_object_field_start_after_sep && + (tok->flags & JSON_TOKENER_STRICT)) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if (c == '"' || c == '\'') { + tok->quote_char = c; + printbuf_reset(tok->pb); + state = json_tokener_state_object_field; + } else { + tok->err = json_tokener_error_parse_object_key_name; + goto out; + } + break; + + case json_tokener_state_object_field: + { + /* Advance until we change state */ + const char *case_start = str; + while(1) { + if(c == tok->quote_char) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + obj_field_name = strdup(tok->pb->buf); + saved_state = json_tokener_state_object_field_end; + state = json_tokener_state_eatws; + break; + } else if(c == '\\') { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + saved_state = json_tokener_state_object_field; + state = json_tokener_state_string_escape; + break; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + } + break; + + case json_tokener_state_object_field_end: + if(c == ':') { + saved_state = json_tokener_state_object_value; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_key_sep; + goto out; + } + break; + + case json_tokener_state_object_value: + if(tok->depth >= tok->max_depth-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_object_value_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + + case json_tokener_state_object_value_add: + json_object_object_add(current, obj_field_name, obj); + free(obj_field_name); + obj_field_name = NULL; + saved_state = json_tokener_state_object_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_object_sep: + if(c == '}') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_object_field_start_after_sep; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_value_sep; + goto out; + } + break; + + } + if (!ADVANCE_CHAR(str, tok)) + goto out; + } /* while(POP_CHAR) */ + + out: + if (c && + (state == json_tokener_state_finish) && + (tok->depth == 0) && + (tok->flags & JSON_TOKENER_STRICT)) { + /* unexpected char after JSON data */ + tok->err = json_tokener_error_parse_unexpected; + } + if (!c) { /* We hit an eof char (0) */ + if(state != json_tokener_state_finish && + saved_state != json_tokener_state_finish) + tok->err = json_tokener_error_parse_eof; + } + +#ifdef HAVE_SETLOCALE + setlocale(LC_NUMERIC, oldlocale); + if (oldlocale) free(oldlocale); +#endif + + if (tok->err == json_tokener_success) + { + json_object *ret = json_object_get(current); + int ii; + + /* Partially reset, so we parse additional objects on subsequent calls. */ + for(ii = tok->depth; ii >= 0; ii--) + json_tokener_reset_level(tok, ii); + return ret; + } + + MC_DEBUG("json_tokener_parse_ex: error %s at offset %d\n", + json_tokener_errors[tok->err], tok->char_offset); + return NULL; +} + +void json_tokener_set_flags(struct json_tokener *tok, int flags) +{ + tok->flags = flags; +} diff --git a/src/components/json/json_tokener.h b/src/components/json/json_tokener.h new file mode 100644 index 0000000..a72d2bd --- /dev/null +++ b/src/components/json/json_tokener.h @@ -0,0 +1,208 @@ +/* + * $Id: json_tokener.h,v 1.10 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_tokener_h_ +#define _json_tokener_h_ + +#include +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum json_tokener_error { + json_tokener_success, + json_tokener_continue, + json_tokener_error_depth, + json_tokener_error_parse_eof, + json_tokener_error_parse_unexpected, + json_tokener_error_parse_null, + json_tokener_error_parse_boolean, + json_tokener_error_parse_number, + json_tokener_error_parse_array, + json_tokener_error_parse_object_key_name, + json_tokener_error_parse_object_key_sep, + json_tokener_error_parse_object_value_sep, + json_tokener_error_parse_string, + json_tokener_error_parse_comment, + json_tokener_error_size +}; + +enum json_tokener_state { + json_tokener_state_eatws, + json_tokener_state_start, + json_tokener_state_finish, + json_tokener_state_null, + json_tokener_state_comment_start, + json_tokener_state_comment, + json_tokener_state_comment_eol, + json_tokener_state_comment_end, + json_tokener_state_string, + json_tokener_state_string_escape, + json_tokener_state_escape_unicode, + json_tokener_state_boolean, + json_tokener_state_number, + json_tokener_state_array, + json_tokener_state_array_add, + json_tokener_state_array_sep, + json_tokener_state_object_field_start, + json_tokener_state_object_field, + json_tokener_state_object_field_end, + json_tokener_state_object_value, + json_tokener_state_object_value_add, + json_tokener_state_object_sep, + json_tokener_state_array_after_sep, + json_tokener_state_object_field_start_after_sep, + json_tokener_state_inf +}; + +struct json_tokener_srec +{ + enum json_tokener_state state, saved_state; + struct json_object *obj; + struct json_object *current; + char *obj_field_name; +}; + +#define JSON_TOKENER_DEFAULT_DEPTH 32 + +struct json_tokener +{ + char *str; + struct printbuf *pb; + int max_depth, depth, is_double, st_pos, char_offset; + enum json_tokener_error err; + unsigned int ucs_char; + char quote_char; + struct json_tokener_srec *stack; + int flags; +}; + +/** + * Be strict when parsing JSON input. Use caution with + * this flag as what is considered valid may become more + * restrictive from one release to the next, causing your + * code to fail on previously working input. + * + * This flag is not set by default. + * + * @see json_tokener_set_flags() + */ +#define JSON_TOKENER_STRICT 0x01 + +/** + * Given an error previously returned by json_tokener_get_error(), + * return a human readable description of the error. + * + * @return a generic error message is returned if an invalid error value is provided. + */ +const char *json_tokener_error_desc(enum json_tokener_error jerr); + +/** + * Retrieve the error caused by the last call to json_tokener_parse_ex(), + * or json_tokener_success if there is no error. + * + * When parsing a JSON string in pieces, if the tokener is in the middle + * of parsing this will return json_tokener_continue. + * + * See also json_tokener_error_desc(). + */ +enum json_tokener_error json_tokener_get_error(struct json_tokener *tok); + +extern struct json_tokener* json_tokener_new(void); +extern struct json_tokener* json_tokener_new_ex(int depth); +extern void json_tokener_free(struct json_tokener *tok); +extern void json_tokener_reset(struct json_tokener *tok); +extern struct json_object* json_tokener_parse(const char *str); +extern struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error); + +/** + * Set flags that control how parsing will be done. + */ +extern void json_tokener_set_flags(struct json_tokener *tok, int flags); + +/** + * Parse a string and return a non-NULL json_object if a valid JSON value + * is found. The string does not need to be a JSON object or array; + * it can also be a string, number or boolean value. + * + * A partial JSON string can be parsed. If the parsing is incomplete, + * NULL will be returned and json_tokener_get_error() will be return + * json_tokener_continue. + * json_tokener_parse_ex() can then be called with additional bytes in str + * to continue the parsing. + * + * If json_tokener_parse_ex() returns NULL and the error anything other than + * json_tokener_continue, a fatal error has occurred and parsing must be + * halted. Then tok object must not be re-used until json_tokener_reset() is + * called. + * + * When a valid JSON value is parsed, a non-NULL json_object will be + * returned. Also, json_tokener_get_error() will return json_tokener_success. + * Be sure to check the type with json_object_is_type() or + * json_object_get_type() before using the object. + * + * @b XXX this shouldn't use internal fields: + * Trailing characters after the parsed value do not automatically cause an + * error. It is up to the caller to decide whether to treat this as an + * error or to handle the additional characters, perhaps by parsing another + * json value starting from that point. + * + * Extra characters can be detected by comparing the tok->char_offset against + * the length of the last len parameter passed in. + * + * The tokener does \b not maintain an internal buffer so the caller is + * responsible for calling json_tokener_parse_ex with an appropriate str + * parameter starting with the extra characters. + * + * This interface is presently not 64-bit clean due to the int len argument + * so the function limits the maximum string size to INT32_MAX (2GB). + * If the function is called with len == -1 then strlen is called to check + * the string length is less than INT32_MAX (2GB) + * + * Example: + * @code +json_object *jobj = NULL; +const char *mystring = NULL; +int stringlen = 0; +enum json_tokener_error jerr; +do { + mystring = ... // get JSON string, e.g. read from file, etc... + stringlen = strlen(mystring); + jobj = json_tokener_parse_ex(tok, mystring, stringlen); +} while ((jerr = json_tokener_get_error(tok)) == json_tokener_continue); +if (jerr != json_tokener_success) +{ + fprintf(stderr, "Error: %s\n", json_tokener_error_desc(jerr)); + // Handle errors, as appropriate for your application. +} +if (tok->char_offset < stringlen) // XXX shouldn't access internal fields +{ + // Handle extra characters after parsed object as desired. + // e.g. issue an error, parse another object from that point, etc... +} +// Success, use jobj here. + +@endcode + * + * @param tok a json_tokener previously allocated with json_tokener_new() + * @param str an string with any valid JSON expression, or portion of. This does not need to be null terminated. + * @param len the length of str + */ +extern struct json_object* json_tokener_parse_ex(struct json_tokener *tok, + const char *str, int len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/components/json/json_util.c b/src/components/json/json_util.c new file mode 100644 index 0000000..531f9af --- /dev/null +++ b/src/components/json/json_util.c @@ -0,0 +1,300 @@ +/* + * $Id: json_util.c,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" +#undef realloc + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif /* HAVE_SYS_TYPES_H */ + +#ifdef HAVE_SYS_STAT_H +#include +#endif /* HAVE_SYS_STAT_H */ + +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ + +#ifdef WIN32 +# define WIN32_LEAN_AND_MEAN +# include +# include +#endif /* defined(WIN32) */ + +#if !defined(HAVE_OPEN) && defined(WIN32) +# define open _open +#endif + +#if !defined(HAVE_SNPRINTF) && defined(_MSC_VER) + /* MSC has the version as _snprintf */ +# define snprintf _snprintf +#elif !defined(HAVE_SNPRINTF) +# error You do not have snprintf on your system. +#endif /* HAVE_SNPRINTF */ + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" + +static int sscanf_is_broken = 0; +static int sscanf_is_broken_testdone = 0; +static void sscanf_is_broken_test(void); + +struct json_object* json_object_from_file(const char *filename) +{ + struct printbuf *pb; + struct json_object *obj; + char buf[JSON_FILE_BUF_SIZE]; + int fd, ret; + + if((fd = open(filename, O_RDONLY)) < 0) { + MC_ERROR("json_object_from_file: error opening file %s: %s\n", + filename, strerror(errno)); + return NULL; + } + if(!(pb = printbuf_new())) { + close(fd); + MC_ERROR("json_object_from_file: printbuf_new failed\n"); + return NULL; + } + while((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) { + printbuf_memappend(pb, buf, ret); + } + close(fd); + if(ret < 0) { + MC_ERROR("json_object_from_file: error reading file %s: %s\n", + filename, strerror(errno)); + printbuf_free(pb); + return NULL; + } + obj = json_tokener_parse(pb->buf); + printbuf_free(pb); + return obj; +} + +/* extended "format and write to file" function */ + +int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags) +{ + const char *json_str; + int fd, ret; + unsigned int wpos, wsize; + + if(!obj) { + MC_ERROR("json_object_to_file: object is null\n"); + return -1; + } + + if((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) { + MC_ERROR("json_object_to_file: error opening file %s: %s\n", + filename, strerror(errno)); + return -1; + } + + if(!(json_str = json_object_to_json_string_ext(obj,flags))) { + close(fd); + return -1; + } + + wsize = (unsigned int)(strlen(json_str) & UINT_MAX); /* CAW: probably unnecessary, but the most 64bit safe */ + wpos = 0; + while(wpos < wsize) { + if((ret = write(fd, json_str + wpos, wsize-wpos)) < 0) { + close(fd); + MC_ERROR("json_object_to_file: error writing file %s: %s\n", + filename, strerror(errno)); + return -1; + } + + /* because of the above check for ret < 0, we can safely cast and add */ + wpos += (unsigned int)ret; + } + + close(fd); + return 0; +} + +// backwards compatible "format and write to file" function + +int json_object_to_file(const char *filename, struct json_object *obj) +{ + return json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); +} + +int json_parse_double(const char *buf, double *retval) +{ + return (sscanf(buf, "%lf", retval)==1 ? 0 : 1); +} + +/* + * Not all implementations of sscanf actually work properly. + * Check whether the one we're currently using does, and if + * it's broken, enable the workaround code. + */ +static void sscanf_is_broken_test() +{ + int64_t num64; + int ret_errno, is_int64_min, ret_errno2, is_int64_max; + + (void)sscanf(" -01234567890123456789012345", "%" SCNd64, &num64); + ret_errno = errno; + is_int64_min = (num64 == INT64_MIN); + + (void)sscanf(" 01234567890123456789012345", "%" SCNd64, &num64); + ret_errno2 = errno; + is_int64_max = (num64 == INT64_MAX); + + if (ret_errno != ERANGE || !is_int64_min || + ret_errno2 != ERANGE || !is_int64_max) + { + MC_DEBUG("sscanf_is_broken_test failed, enabling workaround code\n"); + sscanf_is_broken = 1; + } +} + +int json_parse_int64(const char *buf, int64_t *retval) +{ + int64_t num64; + const char *buf_sig_digits; + int orig_has_neg; + int saved_errno; + + if (!sscanf_is_broken_testdone) + { + sscanf_is_broken_test(); + sscanf_is_broken_testdone = 1; + } + + // Skip leading spaces + while (isspace((int)*buf) && *buf) + buf++; + + errno = 0; // sscanf won't always set errno, so initialize + if (sscanf(buf, "%" SCNd64, &num64) != 1) + { + MC_DEBUG("Failed to parse, sscanf != 1\n"); + return 1; + } + + saved_errno = errno; + buf_sig_digits = buf; + orig_has_neg = 0; + if (*buf_sig_digits == '-') + { + buf_sig_digits++; + orig_has_neg = 1; + } + + // Not all sscanf implementations actually work + if (sscanf_is_broken && saved_errno != ERANGE) + { + char buf_cmp[100]; + char *buf_cmp_start = buf_cmp; + int recheck_has_neg = 0; + int buf_cmp_len; + + // Skip leading zeros, but keep at least one digit + while (buf_sig_digits[0] == '0' && buf_sig_digits[1] != '\0') + buf_sig_digits++; + if (num64 == 0) // assume all sscanf impl's will parse -0 to 0 + orig_has_neg = 0; // "-0" is the same as just plain "0" + + snprintf(buf_cmp_start, sizeof(buf_cmp), "%" PRId64, num64); + if (*buf_cmp_start == '-') + { + recheck_has_neg = 1; + buf_cmp_start++; + } + // No need to skip leading spaces or zeros here. + + buf_cmp_len = strlen(buf_cmp_start); + /** + * If the sign is different, or + * some of the digits are different, or + * there is another digit present in the original string + * then we have NOT successfully parsed the value. + */ + if (orig_has_neg != recheck_has_neg || + strncmp(buf_sig_digits, buf_cmp_start, strlen(buf_cmp_start)) != 0 || + ((int)strlen(buf_sig_digits) != buf_cmp_len && + isdigit((int)buf_sig_digits[buf_cmp_len]) + ) + ) + { + saved_errno = ERANGE; + } + } + + // Not all sscanf impl's set the value properly when out of range. + // Always do this, even for properly functioning implementations, + // since it shouldn't slow things down much. + if (saved_errno == ERANGE) + { + if (orig_has_neg) + num64 = INT64_MIN; + else + num64 = INT64_MAX; + } + *retval = num64; + return 0; +} + +#ifndef HAVE_REALLOC +void* rpl_realloc(void* p, size_t n) +{ + if (n == 0) + n = 1; + if (p == 0) + return malloc(n); + return realloc(p, n); +} +#endif + +#define NELEM(a) (sizeof(a) / sizeof(a[0])) +static const char* json_type_name[] = { + /* If you change this, be sure to update the enum json_type definition too */ + "null", + "boolean", + "double", + "int", + "object", + "array", + "string", +}; + +const char *json_type_to_name(enum json_type o_type) +{ + int o_type_int = (int)o_type; + if (o_type_int < 0 || o_type_int >= (int)NELEM(json_type_name)) + { + MC_ERROR("json_type_to_name: type %d is out of range [0,%d]\n", o_type, NELEM(json_type_name)); + return NULL; + } + return json_type_name[o_type]; +} + diff --git a/src/components/json/json_util.h b/src/components/json/json_util.h new file mode 100644 index 0000000..1005e58 --- /dev/null +++ b/src/components/json/json_util.h @@ -0,0 +1,41 @@ +/* + * $Id: json_util.h,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_util_h_ +#define _json_util_h_ + +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_FILE_BUF_SIZE 4096 + +/* utility functions */ +extern struct json_object* json_object_from_file(const char *filename); +extern int json_object_to_file(const char *filename, struct json_object *obj); +extern int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags); +extern int json_parse_int64(const char *buf, int64_t *retval); +extern int json_parse_double(const char *buf, double *retval); + + +/** + * Return a string describing the type of the object. + * e.g. "int", or "object", etc... + */ +extern const char *json_type_to_name(enum json_type o_type); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/components/json/libjson.c b/src/components/json/libjson.c new file mode 100644 index 0000000..5284fd0 --- /dev/null +++ b/src/components/json/libjson.c @@ -0,0 +1,26 @@ + +/* dummy source file for compatibility purposes */ + +#if defined(HAVE_CDEFS_H) +#include +#endif + +#ifndef __warn_references + +#if defined(__GNUC__) && defined (HAS_GNU_WARNING_LONG) + +#define __warn_references(sym,msg) \ + __asm__(".section .gnu" #sym ",\n\t.ascii \"" msg "\"\n\t.text"); + +#else +#define __warn_references(sym,msg) /* nothing */ +#endif + +#endif + +#include "json_object.h" + +__warn_references(json_object_get, "Warning: please link against libjson-c instead of libjson"); + +/* __asm__(".section .gnu.warning." __STRING(sym) \ + " ; .ascii \"" msg "\" ; .text") */ diff --git a/src/components/json/linkhash.c b/src/components/json/linkhash.c new file mode 100644 index 0000000..712c387 --- /dev/null +++ b/src/components/json/linkhash.c @@ -0,0 +1,602 @@ +/* + * $Id: linkhash.c,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_ENDIAN_H +# include /* attempt to define endianness */ +#endif + +#include "random_seed.h" +#include "linkhash.h" + +void lh_abort(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + vprintf(msg, ap); + va_end(ap); + exit(1); +} + +unsigned long lh_ptr_hash(const void *k) +{ + /* CAW: refactored to be 64bit nice */ + return (unsigned long)((((ptrdiff_t)k * LH_PRIME) >> 4) & ULONG_MAX); +} + +int lh_ptr_equal(const void *k1, const void *k2) +{ + return (k1 == k2); +} + +/* + * hashlittle from lookup3.c, by Bob Jenkins, May 2006, Public Domain. + * http://burtleburtle.net/bob/c/lookup3.c + * minor modifications to make functions static so no symbols are exported + * minor mofifications to compile with -Werror + */ + +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + const uint8_t *k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + +unsigned long lh_char_hash(const void *k) +{ + static volatile int random_seed = -1; + + if (random_seed == -1) { + int seed; + /* we can't use -1 as it is the unitialized sentinel */ + while ((seed = json_c_get_random_seed()) == -1); +#if defined __GNUC__ + __sync_val_compare_and_swap(&random_seed, -1, seed); +#elif defined _MSC_VER + InterlockedCompareExchange(&random_seed, seed, -1); +#else +#warning "racy random seed initializtion if used by multiple threads" + random_seed = seed; /* potentially racy */ +#endif + } + + return hashlittle((const char*)k, strlen((const char*)k), random_seed); +} + +int lh_char_equal(const void *k1, const void *k2) +{ + return (strcmp((const char*)k1, (const char*)k2) == 0); +} + +struct lh_table* lh_table_new(int size, const char *name, + lh_entry_free_fn *free_fn, + lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn) +{ + int i; + struct lh_table *t; + + t = (struct lh_table*)calloc(1, sizeof(struct lh_table)); + if(!t) lh_abort("lh_table_new: calloc failed\n"); + t->count = 0; + t->size = size; + t->name = name; + t->table = (struct lh_entry*)calloc(size, sizeof(struct lh_entry)); + if(!t->table) lh_abort("lh_table_new: calloc failed\n"); + t->free_fn = free_fn; + t->hash_fn = hash_fn; + t->equal_fn = equal_fn; + for(i = 0; i < size; i++) t->table[i].k = LH_EMPTY; + return t; +} + +struct lh_table* lh_kchar_table_new(int size, const char *name, + lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, name, free_fn, lh_char_hash, lh_char_equal); +} + +struct lh_table* lh_kptr_table_new(int size, const char *name, + lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, name, free_fn, lh_ptr_hash, lh_ptr_equal); +} + +void lh_table_resize(struct lh_table *t, int new_size) +{ + struct lh_table *new_t; + struct lh_entry *ent; + + new_t = lh_table_new(new_size, t->name, NULL, t->hash_fn, t->equal_fn); + ent = t->head; + while(ent) { + lh_table_insert(new_t, ent->k, ent->v); + ent = ent->next; + } + free(t->table); + t->table = new_t->table; + t->size = new_size; + t->head = new_t->head; + t->tail = new_t->tail; + t->resizes++; + free(new_t); +} + +void lh_table_free(struct lh_table *t) +{ + struct lh_entry *c; + for(c = t->head; c != NULL; c = c->next) { + if(t->free_fn) { + t->free_fn(c); + } + } + free(t->table); + free(t); +} + + +int lh_table_insert(struct lh_table *t, void *k, const void *v) +{ + unsigned long h, n; + + t->inserts++; + if(t->count >= t->size * LH_LOAD_FACTOR) lh_table_resize(t, t->size * 2); + + h = t->hash_fn(k); + n = h % t->size; + + while( 1 ) { + if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) break; + t->collisions++; + if ((int)++n == t->size) n = 0; + } + + t->table[n].k = k; + t->table[n].v = v; + t->count++; + + if(t->head == NULL) { + t->head = t->tail = &t->table[n]; + t->table[n].next = t->table[n].prev = NULL; + } else { + t->tail->next = &t->table[n]; + t->table[n].prev = t->tail; + t->table[n].next = NULL; + t->tail = &t->table[n]; + } + + return 0; +} + + +struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k) +{ + unsigned long h = t->hash_fn(k); + unsigned long n = h % t->size; + int count = 0; + + t->lookups++; + while( count < t->size ) { + if(t->table[n].k == LH_EMPTY) return NULL; + if(t->table[n].k != LH_FREED && + t->equal_fn(t->table[n].k, k)) return &t->table[n]; + if ((int)++n == t->size) n = 0; + count++; + } + return NULL; +} + + +const void* lh_table_lookup(struct lh_table *t, const void *k) +{ + void *result; + lh_table_lookup_ex(t, k, &result); + return result; +} + +json_bool lh_table_lookup_ex(struct lh_table* t, const void* k, void **v) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if (e != NULL) { + if (v != NULL) *v = (void *)e->v; + return TRUE; /* key found */ + } + if (v != NULL) *v = NULL; + return FALSE; /* key not found */ +} + +int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e) +{ + ptrdiff_t n = (ptrdiff_t)(e - t->table); /* CAW: fixed to be 64bit nice, still need the crazy negative case... */ + + /* CAW: this is bad, really bad, maybe stack goes other direction on this machine... */ + if(n < 0) { return -2; } + + if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) return -1; + t->count--; + if(t->free_fn) t->free_fn(e); + t->table[n].v = NULL; + t->table[n].k = LH_FREED; + if(t->tail == &t->table[n] && t->head == &t->table[n]) { + t->head = t->tail = NULL; + } else if (t->head == &t->table[n]) { + t->head->next->prev = NULL; + t->head = t->head->next; + } else if (t->tail == &t->table[n]) { + t->tail->prev->next = NULL; + t->tail = t->tail->prev; + } else { + t->table[n].prev->next = t->table[n].next; + t->table[n].next->prev = t->table[n].prev; + } + t->table[n].next = t->table[n].prev = NULL; + return 0; +} + + +int lh_table_delete(struct lh_table *t, const void *k) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if(!e) return -1; + return lh_table_delete_entry(t, e); +} + +int lh_table_length(struct lh_table *t) +{ + return t->count; +} diff --git a/src/components/json/linkhash.h b/src/components/json/linkhash.h new file mode 100644 index 0000000..950d09f --- /dev/null +++ b/src/components/json/linkhash.h @@ -0,0 +1,292 @@ +/* + * $Id: linkhash.h,v 1.6 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _linkhash_h_ +#define _linkhash_h_ + +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * golden prime used in hash functions + */ +#define LH_PRIME 0x9e370001UL + +/** + * The fraction of filled hash buckets until an insert will cause the table + * to be resized. + * This can range from just above 0 up to 1.0. + */ +#define LH_LOAD_FACTOR 0.66 + +/** + * sentinel pointer value for empty slots + */ +#define LH_EMPTY (void*)-1 + +/** + * sentinel pointer value for freed slots + */ +#define LH_FREED (void*)-2 + +struct lh_entry; + +/** + * callback function prototypes + */ +typedef void (lh_entry_free_fn) (struct lh_entry *e); +/** + * callback function prototypes + */ +typedef unsigned long (lh_hash_fn) (const void *k); +/** + * callback function prototypes + */ +typedef int (lh_equal_fn) (const void *k1, const void *k2); + +/** + * An entry in the hash table + */ +struct lh_entry { + /** + * The key. + */ + void *k; + /** + * The value. + */ + const void *v; + /** + * The next entry + */ + struct lh_entry *next; + /** + * The previous entry. + */ + struct lh_entry *prev; +}; + + +/** + * The hash table structure. + */ +struct lh_table { + /** + * Size of our hash. + */ + int size; + /** + * Numbers of entries. + */ + int count; + + /** + * Number of collisions. + */ + int collisions; + + /** + * Number of resizes. + */ + int resizes; + + /** + * Number of lookups. + */ + int lookups; + + /** + * Number of inserts. + */ + int inserts; + + /** + * Number of deletes. + */ + int deletes; + + /** + * Name of the hash table. + */ + const char *name; + + /** + * The first entry. + */ + struct lh_entry *head; + + /** + * The last entry. + */ + struct lh_entry *tail; + + struct lh_entry *table; + + /** + * A pointer onto the function responsible for freeing an entry. + */ + lh_entry_free_fn *free_fn; + lh_hash_fn *hash_fn; + lh_equal_fn *equal_fn; +}; + + +/** + * Pre-defined hash and equality functions + */ +extern unsigned long lh_ptr_hash(const void *k); +extern int lh_ptr_equal(const void *k1, const void *k2); + +extern unsigned long lh_char_hash(const void *k); +extern int lh_char_equal(const void *k1, const void *k2); + + +/** + * Convenience list iterator. + */ +#define lh_foreach(table, entry) \ +for(entry = table->head; entry; entry = entry->next) + +/** + * lh_foreach_safe allows calling of deletion routine while iterating. + */ +#define lh_foreach_safe(table, entry, tmp) \ +for(entry = table->head; entry && ((tmp = entry->next) || 1); entry = tmp) + + + +/** + * Create a new linkhash table. + * @param size initial table size. The table is automatically resized + * although this incurs a performance penalty. + * @param name the table name. + * @param free_fn callback function used to free memory for entries + * when lh_table_free or lh_table_delete is called. + * If NULL is provided, then memory for keys and values + * must be freed by the caller. + * @param hash_fn function used to hash keys. 2 standard ones are defined: + * lh_ptr_hash and lh_char_hash for hashing pointer values + * and C strings respectively. + * @param equal_fn comparison function to compare keys. 2 standard ones defined: + * lh_ptr_hash and lh_char_hash for comparing pointer values + * and C strings respectively. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_table_new(int size, const char *name, + lh_entry_free_fn *free_fn, + lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn); + +/** + * Convenience function to create a new linkhash + * table with char keys. + * @param size initial table size. + * @param name table name. + * @param free_fn callback function used to free memory for entries. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_kchar_table_new(int size, const char *name, + lh_entry_free_fn *free_fn); + + +/** + * Convenience function to create a new linkhash + * table with ptr keys. + * @param size initial table size. + * @param name table name. + * @param free_fn callback function used to free memory for entries. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_kptr_table_new(int size, const char *name, + lh_entry_free_fn *free_fn); + + +/** + * Free a linkhash table. + * If a callback free function is provided then it is called for all + * entries in the table. + * @param t table to free. + */ +extern void lh_table_free(struct lh_table *t); + + +/** + * Insert a record into the table. + * @param t the table to insert into. + * @param k a pointer to the key to insert. + * @param v a pointer to the value to insert. + */ +extern int lh_table_insert(struct lh_table *t, void *k, const void *v); + + +/** + * Lookup a record into the table. + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the record structure of the value or NULL if it does not exist. + */ +extern struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k); + +/** + * Lookup a record into the table + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the found value or NULL if it does not exist. + * @deprecated Use lh_table_lookup_ex instead. + */ +THIS_FUNCTION_IS_DEPRECATED(extern const void* lh_table_lookup(struct lh_table *t, const void *k)); + +/** + * Lookup a record in the table + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @param v a pointer to a where to store the found value (set to NULL if it doesn't exist). + * @return whether or not the key was found + */ +extern json_bool lh_table_lookup_ex(struct lh_table *t, const void *k, void **v); + +/** + * Delete a record from the table. + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param e a pointer to the entry to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e); + + +/** + * Delete a record from the table. + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param k a pointer to the key to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete(struct lh_table *t, const void *k); + +extern int lh_table_length(struct lh_table *t); + +void lh_abort(const char *msg, ...); +void lh_table_resize(struct lh_table *t, int new_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/components/json/math_compat.h b/src/components/json/math_compat.h new file mode 100644 index 0000000..f40b8fa --- /dev/null +++ b/src/components/json/math_compat.h @@ -0,0 +1,28 @@ +#ifndef __math_compat_h +#define __math_compat_h + +/* Define isnan and isinf on Windows/MSVC */ + +#ifndef HAVE_DECL_ISNAN +# ifdef HAVE_DECL__ISNAN +#include +#define isnan(x) _isnan(x) +# endif +#endif + +#ifndef HAVE_DECL_ISINF +# ifdef HAVE_DECL__FINITE +#include +#define isinf(x) (!_finite(x)) +# endif +#endif + +#ifndef HAVE_DECL_NAN +#error This platform does not have nan() +#endif + +#ifndef HAVE_DECL_INFINITY +#error This platform does not have INFINITY +#endif + +#endif diff --git a/src/components/json/parse_flags.c b/src/components/json/parse_flags.c new file mode 100644 index 0000000..1af61ea --- /dev/null +++ b/src/components/json/parse_flags.c @@ -0,0 +1,50 @@ +#include "config.h" + +#include +#include + +#include "json.h" +#include "parse_flags.h" + +#if !defined(HAVE_STRCASECMP) && defined(_MSC_VER) +# define strcasecmp _stricmp +#elif !defined(HAVE_STRCASECMP) +# error You do not have strcasecmp on your system. +#endif /* HAVE_STRNCASECMP */ + +static struct { + const char *arg; + int flag; +} format_args[] = { + { "plain", JSON_C_TO_STRING_PLAIN }, + { "spaced", JSON_C_TO_STRING_SPACED }, + { "pretty", JSON_C_TO_STRING_PRETTY }, +}; + +#ifndef NELEM +#define NELEM(x) (sizeof(x) / sizeof(&x[0])) +#endif + +int parse_flags(int argc, char **argv) +{ + int arg_idx; + int sflags = 0; + for (arg_idx = 1; arg_idx < argc ; arg_idx++) + { + int jj; + for (jj = 0; jj < (int)NELEM(format_args); jj++) + { + if (strcasecmp(argv[arg_idx], format_args[jj].arg) == 0) + { + sflags |= format_args[jj].flag; + break; + } + } + if (jj == NELEM(format_args)) + { + printf("Unknown arg: %s\n", argv[arg_idx]); + exit(1); + } + } + return sflags; +} diff --git a/src/components/json/parse_flags.h b/src/components/json/parse_flags.h new file mode 100644 index 0000000..c5e2f41 --- /dev/null +++ b/src/components/json/parse_flags.h @@ -0,0 +1,4 @@ +#ifndef __parse_flags_h +#define __parse_flags_h +int parse_flags(int argc, char **argv); +#endif diff --git a/src/components/json/printbuf.c b/src/components/json/printbuf.c new file mode 100644 index 0000000..fcef381 --- /dev/null +++ b/src/components/json/printbuf.c @@ -0,0 +1,192 @@ +/* + * $Id: printbuf.c,v 1.5 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +#include "config.h" +#define _GNU_SOURCE +#include +#include +#include + +#ifdef HAVE_STDARG_H +# include +#else /* !HAVE_STDARG_H */ +# error Not enough var arg support! +#endif /* HAVE_STDARG_H */ + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" + +static int printbuf_extend(struct printbuf *p, int min_size); + +struct printbuf* printbuf_new(void) +{ + struct printbuf *p; + + p = (struct printbuf*)calloc(1, sizeof(struct printbuf)); + if(!p) return NULL; + p->size = 32; + p->bpos = 0; + if(!(p->buf = (char*)malloc(p->size))) { + free(p); + return NULL; + } + return p; +} + + +/** + * Extend the buffer p so it has a size of at least min_size. + * + * If the current size is large enough, nothing is changed. + * + * Note: this does not check the available space! The caller + * is responsible for performing those calculations. + */ +static int printbuf_extend(struct printbuf *p, int min_size) +{ + char *t; + int new_size; + + if (p->size >= min_size) + return 0; + + new_size = json_max(p->size * 2, min_size + 8); +#ifdef PRINTBUF_DEBUG + MC_DEBUG("printbuf_memappend: realloc " + "bpos=%d min_size=%d old_size=%d new_size=%d\n", + p->bpos, min_size, p->size, new_size); +#endif /* PRINTBUF_DEBUG */ + if(!(t = (char*)realloc(p->buf, new_size))) + return -1; + p->size = new_size; + p->buf = t; + return 0; +} + +int printbuf_memappend(struct printbuf *p, const char *buf, int size) +{ + if (p->size <= p->bpos + size + 1) { + if (printbuf_extend(p, p->bpos + size + 1) < 0) + return -1; + } + memcpy(p->buf + p->bpos, buf, size); + p->bpos += size; + p->buf[p->bpos]= '\0'; + return size; +} + +int printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len) +{ + int size_needed; + + if (offset == -1) + offset = pb->bpos; + size_needed = offset + len; + if (pb->size < size_needed) + { + if (printbuf_extend(pb, size_needed) < 0) + return -1; + } + + memset(pb->buf + offset, charvalue, len); + if (pb->bpos < size_needed) + pb->bpos = size_needed; + + return 0; +} + +#if !defined(HAVE_VSNPRINTF) && defined(_MSC_VER) +# define vsnprintf _vsnprintf +#elif !defined(HAVE_VSNPRINTF) /* !HAVE_VSNPRINTF */ +# error Need vsnprintf! +#endif /* !HAVE_VSNPRINTF && defined(WIN32) */ + +#if !defined(HAVE_VASPRINTF) +/* CAW: compliant version of vasprintf */ +static int vasprintf(char **buf, const char *fmt, va_list ap) +{ +#ifndef WIN32 + static char _T_emptybuffer = '\0'; +#endif /* !defined(WIN32) */ + int chars; + char *b; + + if(!buf) { return -1; } + +#ifdef WIN32 + chars = _vscprintf(fmt, ap)+1; +#else /* !defined(WIN32) */ + /* CAW: RAWR! We have to hope to god here that vsnprintf doesn't overwrite + our buffer like on some 64bit sun systems.... but hey, its time to move on */ + chars = vsnprintf(&_T_emptybuffer, 0, fmt, ap)+1; + if(chars < 0) { chars *= -1; } /* CAW: old glibc versions have this problem */ +#endif /* defined(WIN32) */ + + b = (char*)malloc(sizeof(char)*chars); + if(!b) { return -1; } + + if((chars = vsprintf(b, fmt, ap)) < 0) + { + free(b); + } else { + *buf = b; + } + + return chars; +} +#endif /* !HAVE_VASPRINTF */ + +int sprintbuf(struct printbuf *p, const char *msg, ...) +{ + va_list ap; + char *t; + int size; + char buf[128]; + + /* user stack buffer first */ + va_start(ap, msg); + size = vsnprintf(buf, 128, msg, ap); + va_end(ap); + /* if string is greater than stack buffer, then use dynamic string + with vasprintf. Note: some implementation of vsnprintf return -1 + if output is truncated whereas some return the number of bytes that + would have been written - this code handles both cases. */ + if(size == -1 || size > 127) { + va_start(ap, msg); + if((size = vasprintf(&t, msg, ap)) < 0) { va_end(ap); return -1; } + va_end(ap); + printbuf_memappend(p, t, size); + free(t); + return size; + } else { + printbuf_memappend(p, buf, size); + return size; + } +} + +void printbuf_reset(struct printbuf *p) +{ + p->buf[0] = '\0'; + p->bpos = 0; +} + +void printbuf_free(struct printbuf *p) +{ + if(p) { + free(p->buf); + free(p); + } +} diff --git a/src/components/json/printbuf.h b/src/components/json/printbuf.h new file mode 100644 index 0000000..b1bde7f --- /dev/null +++ b/src/components/json/printbuf.h @@ -0,0 +1,77 @@ +/* + * $Id: printbuf.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +#ifndef _printbuf_h_ +#define _printbuf_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct printbuf { + char *buf; + int bpos; + int size; +}; + +extern struct printbuf* +printbuf_new(void); + +/* As an optimization, printbuf_memappend_fast is defined as a macro + * that handles copying data if the buffer is large enough; otherwise + * it invokes printbuf_memappend_real() which performs the heavy + * lifting of realloc()ing the buffer and copying data. + * Your code should not use printbuf_memappend directly--use + * printbuf_memappend_fast instead. + */ +extern int +printbuf_memappend(struct printbuf *p, const char *buf, int size); + +#define printbuf_memappend_fast(p, bufptr, bufsize) \ +do { \ + if ((p->size - p->bpos) > bufsize) { \ + memcpy(p->buf + p->bpos, (bufptr), bufsize); \ + p->bpos += bufsize; \ + p->buf[p->bpos]= '\0'; \ + } else { printbuf_memappend(p, (bufptr), bufsize); } \ +} while (0) + +#define printbuf_length(p) ((p)->bpos) + +/** + * Set len bytes of the buffer to charvalue, starting at offset offset. + * Similar to calling memset(x, charvalue, len); + * + * The memory allocated for the buffer is extended as necessary. + * + * If offset is -1, this starts at the end of the current data in the buffer. + */ +extern int +printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len); + +extern int +sprintbuf(struct printbuf *p, const char *msg, ...); + +extern void +printbuf_reset(struct printbuf *p); + +extern void +printbuf_free(struct printbuf *p); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/components/json/random_seed.c b/src/components/json/random_seed.c new file mode 100644 index 0000000..3b520d4 --- /dev/null +++ b/src/components/json/random_seed.c @@ -0,0 +1,237 @@ +/* + * random_seed.c + * + * Copyright (c) 2013 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include +#include "config.h" + +#define DEBUG_SEED(s) + + +#if defined ENABLE_RDRAND + +/* cpuid */ + +#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) +#define HAS_X86_CPUID 1 + +static void do_cpuid(int regs[], int h) +{ + __asm__ __volatile__( +#if defined __x86_64__ + "pushq %%rbx;\n" +#else + "pushl %%ebx;\n" +#endif + "cpuid;\n" +#if defined __x86_64__ + "popq %%rbx;\n" +#else + "popl %%ebx;\n" +#endif + : "=a"(regs[0]), [ebx] "=r"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) + : "a"(h)); +} + +#elif defined _MSC_VER + +#define HAS_X86_CPUID 1 +#define do_cpuid __cpuid + +#endif + +/* has_rdrand */ + +#if HAS_X86_CPUID + +static int has_rdrand() +{ + // CPUID.01H:ECX.RDRAND[bit 30] == 1 + int regs[4]; + do_cpuid(regs, 1); + return (regs[2] & (1 << 30)) != 0; +} + +#endif + +/* get_rdrand_seed - GCC x86 and X64 */ + +#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) + +#define HAVE_RDRAND 1 + +static int get_rdrand_seed() +{ + DEBUG_SEED("get_rdrand_seed"); + int _eax; + // rdrand eax + __asm__ __volatile__("1: .byte 0x0F\n" + " .byte 0xC7\n" + " .byte 0xF0\n" + " jnc 1b;\n" + : "=a" (_eax)); + return _eax; +} + +#endif + +#if defined _MSC_VER + +#if _MSC_VER >= 1700 +#define HAVE_RDRAND 1 + +/* get_rdrand_seed - Visual Studio 2012 and above */ + +static int get_rdrand_seed() +{ + DEBUG_SEED("get_rdrand_seed"); + int r; + while (_rdrand32_step(&r) == 0); + return r; +} + +#elif defined _M_IX86 +#define HAVE_RDRAND 1 + +/* get_rdrand_seed - Visual Studio 2010 and below - x86 only */ + +static int get_rdrand_seed() +{ + DEBUG_SEED("get_rdrand_seed"); + int _eax; +retry: + // rdrand eax + __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0 + __asm jnc retry + __asm mov _eax, eax + return _eax; +} + +#endif +#endif + +#endif /* defined ENABLE_RDRAND */ + + +/* has_dev_urandom */ + +#if defined (__APPLE__) || defined(__unix__) || defined(__linux__) + +#include +#include +#include +#include +#include +#include + +#define HAVE_DEV_RANDOM 1 + +static const char *dev_random_file = "/dev/urandom"; + +static int has_dev_urandom() +{ + struct stat buf; + if (stat(dev_random_file, &buf)) { + return 0; + } + return ((buf.st_mode & S_IFCHR) != 0); +} + + +/* get_dev_random_seed */ + +static int get_dev_random_seed() +{ + DEBUG_SEED("get_dev_random_seed"); + + int fd = open(dev_random_file, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno)); + exit(1); + } + + int r; + ssize_t nread = read(fd, &r, sizeof(r)); + if (nread != sizeof(r)) { + fprintf(stderr, "error read %s: %s", dev_random_file, strerror(errno)); + exit(1); + } + else if (nread != sizeof(r)) { + fprintf(stderr, "error short read %s", dev_random_file); + exit(1); + } + close(fd); + return r; +} + +#endif + + +/* get_cryptgenrandom_seed */ + +#ifdef WIN32 + +#define HAVE_CRYPTGENRANDOM 1 + +#include +#pragma comment(lib, "advapi32.lib") + +static int get_cryptgenrandom_seed() +{ + DEBUG_SEED("get_cryptgenrandom_seed"); + + HCRYPTPROV hProvider = 0; + int r; + + if (!CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + fprintf(stderr, "error CryptAcquireContextW"); + exit(1); + } + + if (!CryptGenRandom(hProvider, sizeof(r), (BYTE*)&r)) { + fprintf(stderr, "error CryptGenRandom"); + exit(1); + } + + CryptReleaseContext(hProvider, 0); + + return r; +} + +#endif + + +/* get_time_seed */ + +#include + +static int get_time_seed() +{ + DEBUG_SEED("get_time_seed"); + + return (int)time(NULL) * 433494437; +} + + +/* json_c_get_random_seed */ + +int json_c_get_random_seed() +{ +#if HAVE_RDRAND + if (has_rdrand()) return get_rdrand_seed(); +#endif +#if HAVE_DEV_RANDOM + if (has_dev_urandom()) return get_dev_random_seed(); +#endif +#if HAVE_CRYPTGENRANDOM + return get_cryptgenrandom_seed(); +#endif + return get_time_seed(); +} diff --git a/src/components/json/random_seed.h b/src/components/json/random_seed.h new file mode 100644 index 0000000..7362d67 --- /dev/null +++ b/src/components/json/random_seed.h @@ -0,0 +1,25 @@ +/* + * random_seed.h + * + * Copyright (c) 2013 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef seed_h +#define seed_h + +#ifdef __cplusplus +extern "C" { +#endif + +extern int json_c_get_random_seed(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/inc/Maat_command.h b/src/inc/Maat_command.h new file mode 100644 index 0000000..fbdd9a1 --- /dev/null +++ b/src/inc/Maat_command.h @@ -0,0 +1,165 @@ +#ifndef H_MAAT_COMMAND_H_INCLUDE +#define H_MAAT_COMMAND_H_INCLUDE +#ifndef __cplusplus +#error("This file should be compiled with C++ compiler") +#endif +#include "Maat_rule.h" +enum MAAT_OPERATION +{ + MAAT_OP_DEL=0, + MAAT_OP_ADD, + MAAT_OP_RENEW_TIMEOUT //Rule expire time is changed to now+cmd->expire_after +}; + +enum MAAT_REGION_TYPE +{ + REGION_EXPR, + REGION_IP, + REGION_INTERVAL, + REGION_DIGEST, + REGION_SIMILARITY +}; +enum MAAT_EXPR_TYPE +{ + EXPR_TYPE_STRING=0, + EXPR_TYPE_AND, + EXPR_TYPE_REGEX, + EXPR_TYPE_OFFSET +}; +enum MAAT_MATCH_METHOD +{ + MATCH_METHOD_SUB=0, + MATCH_METHOD_RIGHT, + MATCH_METHOD_LEFT, + MATCH_METHOD_COMPLETE +}; + +enum MAAT_CASE_TYPE +{ + UNCASE_PLAIN=0, + CASE_HEXBIN, + CASE_PLAIN +}; +enum MAAT_ADDR_TYPE +{ + ADDR_TYPE_IPv4=4, + ADDR_TYPE_IPv6=6 +}; +enum MAAT_ADDR_DIRECTION +{ + ADDR_DIR_DOUBLE=0, + ADDR_DIR_SINGLE=1 +}; +struct Maat_rgn_str_t +{ + const char *keywords; + const char *district;// optional for expr_plus, otherwise set to NULL. + enum MAAT_EXPR_TYPE expr_type; + enum MAAT_MATCH_METHOD match_method; + enum MAAT_CASE_TYPE hex_bin; +}; +struct Maat_rgn_addr_t +{ + enum MAAT_ADDR_TYPE addr_type; + const char* src_ip; + const char* mask_src_ip; + const char* dst_ip; + const char* mask_dst_ip; + unsigned short src_port; + unsigned short mask_src_port; + unsigned short dst_port; + unsigned short mask_dst_port; + unsigned short protocol; + enum MAAT_ADDR_DIRECTION direction; +}; +struct Maat_rgn_intv_t +{ + unsigned int low_boundary; + unsigned int up_boundary; +}; +struct Maat_rgn_digest_t +{ + unsigned long long orgin_len; + const char* digest_string; + short confidence_degree; +}; +struct Maat_rgn_sim_t +{ + char* target; + short threshold;// 1~100 +}; +struct Maat_region_t +{ + const char* table_name; + int region_id; //If MAAT_OPT_CMD_AUTO_NUMBERING==1, maat will assigned one. Or users must appoint a unique number. + enum MAAT_REGION_TYPE region_type; + union + { + struct Maat_rgn_str_t expr_rule; + struct Maat_rgn_addr_t ip_rule; + struct Maat_rgn_intv_t interval_rule; + struct Maat_rgn_digest_t digest_rule; + struct Maat_rgn_sim_t similarity_rule; + }; +}; +struct Maat_group_t +{ + int region_num; + int group_id; //If MAAT_OPT_CMD_AUTO_NUMBERING==1, maat will assigned one. Or users must assign a unique number. + struct Maat_region_t *regions; +}; +struct Maat_cmd_t +{ + //This Struct MUST alloced by Maat_create_cmd(), then released by Maat_free_cmd(). + struct Maat_rule_t compile; // for MAAT_OP_DEL, only compile.config_id is necessary. + int group_num; // for MAAT_OP_DEL, set to 0. + int expire_after; //expired after $expire_after$ seconds, set to 0 for never timeout. + int label_id; //>0, to be indexed and quried by Maat_cmd_select; =0 not index + struct Maat_group_t* groups;// Add regions with Maat_add_region2cmd +}; +struct Maat_line_t +{ + const char* table_name; + const char* table_line; + int rule_id; // for MAAT_OP_DEL, only rule_id and table_name are necessary. + int label_id; + int expire_after; //expired after $timeout$ seconds, set to 0 for never timeout. +}; +struct Maat_cmd_t* Maat_create_cmd(const struct Maat_rule_t* rule, int group_num); +int Maat_cmd_set_opt(struct Maat_cmd_t* cmd, enum MAAT_RULE_OPT type, const char* val, int size); +//input: which_group 0~group_num +//input: region can be freed after added. +void Maat_add_region2cmd(struct Maat_cmd_t* cmd,int which_group,const struct Maat_region_t* region); + +void Maat_free_cmd(struct Maat_cmd_t* cmd); +int Maat_format_cmd(struct Maat_cmd_t* cmd, char* buffer, int size); +//Input string of REGION_EXPR and REGION_SIMILARITY need to be escapeed. +char* Maat_str_escape(char* dst,int size,const char*src); + +//Deletion failed due to not complete synchronize with Redis. +//To make sure the delete command is excecuted, user should try again after MAAT_OPT_SCANDIR_INTERVAL_MS ms. +//Returns nubmer of successfully updated rule. +//The following functions are NOT thread safe. +int Maat_cmd(Maat_feather_t feather,struct Maat_cmd_t* cmd,enum MAAT_OPERATION op); + +//pipeline model +int Maat_cmd_append(Maat_feather_t feather,struct Maat_cmd_t* cmd,enum MAAT_OPERATION op); + +//Return nubmer of successfully updated rule. +//Return -1 for failed. +int Maat_cmd_commit(Maat_feather_t feather); + + +int Maat_cmd_set_group(Maat_feather_t feather, int group_id, const struct Maat_region_t* region, enum MAAT_OPERATION op); + +//Returns nubmer of successfully updated rule. +//Return -1 for failed. +int Maat_cmd_set_line(Maat_feather_t feather,const struct Maat_line_t* line_rule, enum MAAT_OPERATION op); +int Maat_cmd_set_lines(Maat_feather_t feather,const struct Maat_line_t** line_rule, int line_num ,enum MAAT_OPERATION op); +//Return the value of key after the increment. +//If the key does not exist, it is set to 0 before performing the operation. +long long Maat_cmd_incrby(Maat_feather_t feather,const char* key, int increment); +int Maat_cmd_select(Maat_feather_t feather, int label_id, int * output_ids, unsigned int size); +int Maat_cmd_flushDB(Maat_feather_t feather); +#endif + diff --git a/src/inc/Maat_rule.h b/src/inc/Maat_rule.h new file mode 100644 index 0000000..bcb1546 --- /dev/null +++ b/src/inc/Maat_rule.h @@ -0,0 +1,244 @@ + +/* +*****************Maat Network Flow Rule Manage Framework******** +* Maat is the Goddess of truth and justice in ancient Egyptian concept. +* Her feather was the measure that determined whether the souls (considered +* to reside in the heart) of the departed would reach the paradise of afterlife +* successfully. +* Author: zhengchao@iie.ac.cn,MESA +* Version 2018-07-27 huge service_define +* NOTE: MUST compile with G++ +* All right reserved by Institute of Infomation Engineering,Chinese Academic of Science 2014~2018 +********************************************************* +*/ +#ifndef H_MAAT_RULE_H_INCLUDE +#define H_MAAT_RULE_H_INCLUDE +#ifndef __cplusplus +#error("This file should be compiled with C++ compiler") +#endif +#include "stream.h" +enum MAAT_CHARSET +{ + CHARSET_NONE=0, + CHARSET_GBK, + CHARSET_BIG5, + CHARSET_UNICODE, + CHARSET_UTF8, // 4 + CHARSET_BIN, //5 + CHARSET_UNICODE_ASCII_ESC, // Unicode Escape format, prefix backslash-u hex, e.g. "\u627;" + CHARSET_UNICODE_ASCII_ALIGNED,//Unicode Escape format, prefix backslash-u with 4 bytes aligned, e.g. "\u0627" + CHARSET_UNICODE_NCR_DEC, //SGML Numeric character reference,decimal base, e.g. "ا" + CHARSET_UNICODE_NCR_HEX, //SGML Numeric character reference,hexdecimal base, e.g. "ا" + CHARSET_URL_ENCODE_GB2312, //URL encode with GB2312, e.g. the chinese word "china" was encoded to %D6%D0%B9%FA + CHARSET_URL_ENCODE_UTF8 //11, URL encode with UTF8,e.g. the chinese word "china" was encoded to %E4%B8%AD%E5%9B%BD +}; +enum MAAT_ACTION +{ + MAAT_ACTION_BLOCK=0, + MAAT_ACTION_MONIT, + MAAT_ACTION_WHITE +}; +enum MAAT_POS_TYPE +{ + MAAT_POSTYPE_EXPR=0, + MAAT_POSTYPE_REGEX +}; +typedef void* scan_status_t; +typedef void* stream_para_t; +typedef void* Maat_feather_t; + + +#define MAX_SERVICE_DEFINE_LEN 128 +#define MAX_HUGE_SERVICE_DEFINE_LEN (1024*4) +struct Maat_rule_t +{ + int config_id; + int service_id; + char do_log; + char do_blacklist; + char action; + char reserved; + int serv_def_len; + char service_defined[MAX_SERVICE_DEFINE_LEN]; +}; +#define MAAT_RULE_UPDATE_TYPE_FULL 1 +#define MAAT_RULE_UPDATE_TYPE_INC 2 +typedef void Maat_start_callback_t(int update_type,void* u_para); +typedef void Maat_update_callback_t(int table_id,const char* table_line,void* u_para); +typedef void Maat_finish_callback_t(void* u_para); + + + + + +//--------------------HITTING DETAIL DESCRIPTION BEGIN + +#define MAAT_MAX_HIT_RULE_NUM 8 +#define MAAT_MAX_EXPR_ITEM_NUM 8 +#define MAAT_MAX_HIT_POS_NUM 8 +#define MAAT_MAX_REGEX_GROUP_NUM 8 + +//NOTE position buffer as hitting_regex_pos and hit_pos,are ONLY valid before next scan or Maat_stream_scan_string_end +struct regex_pos_t +{ + int group_num; + int hitting_regex_len; + const char* hitting_regex_pos; + int grouping_len[MAAT_MAX_REGEX_GROUP_NUM]; + const char* grouping_pos[MAAT_MAX_REGEX_GROUP_NUM]; +}; +struct str_pos_t +{ + int hit_len; + const char* hit_pos; +}; +struct sub_item_pos_t +{ + enum MAAT_POS_TYPE ruletype; + int hit_cnt; + union + { + struct regex_pos_t regex_pos[MAAT_MAX_HIT_POS_NUM]; + struct str_pos_t substr_pos[MAAT_MAX_HIT_POS_NUM]; + }; +}; + +struct Maat_region_pos_t +{ + + int region_id; + int sub_item_num; + struct sub_item_pos_t sub_item_pos[MAAT_MAX_EXPR_ITEM_NUM]; +}; + +struct Maat_hit_detail_t +{ + int config_id;//set <0 if half hit; + int hit_region_cnt; + struct Maat_region_pos_t region_pos[MAAT_MAX_HIT_RULE_NUM]; +}; +//--------------------HITTING DETAIL DESCRIPTION END + +//Abondon interface ,left for compatible. +Maat_feather_t Maat_summon_feather(int max_thread_num, + const char* table_info_path, + const char* ful_cfg_dir, + const char* inc_cfg_dir, + void*logger);//MESA_handle_logger +//Abondon interface ,left for compatible. +Maat_feather_t Maat_summon_feather_json(int max_thread_num, + const char* table_info_path, + const char* json_rule, + void* logger); + +Maat_feather_t Maat_feather(int max_thread_num,const char* table_info_path,void* logger); +int Maat_initiate_feather(Maat_feather_t feather); + +enum MAAT_INIT_OPT +{ + MAAT_OPT_SCANDIR_INTERVAL_MS=1, //VALUE is interger, SIZE=sizeof(int). DEFAULT:1,000 milliseconds. + MAAT_OPT_EFFECT_INVERVAL_MS, //VALUE is interger, SIZE=sizeof(int). DEFAULT:60,000 milliseconds. + MAAT_OPT_FULL_CFG_DIR, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1.DEFAULT: no default. + MAAT_OPT_INC_CFG_DIR, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1.DEFAULT: no default. + MAAT_OPT_JSON_FILE_PATH, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1.DEFAULT: no default. + MAAT_OPT_STAT_ON, //VALUE is NULL,SIZE is 0. MAAT_OPT_STAT_FILE_PATH must be set. Default: stat OFF. + MAAT_OPT_PERF_ON, //VALUE is NULL,SIZE is 0. MAAT_OPT_STAT_FILE_PATH must be set. Default: stat OFF. + MAAT_OPT_STAT_FILE_PATH, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. DEFAULT: no default. + MAAT_OPT_SCAN_DETAIL, //VALUE is interger *, SIZE=sizeof(int). 0: not return any detail;1: return hit pos, not include regex grouping; + // 2 return hit pos and regex grouping pos;DEFAULT:0 + MAAT_OPT_INSTANCE_NAME, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1, no more than 11 bytes.DEFAULT: MAAT_$tableinfo_path$. + MAAT_OPT_DECRYPT_KEY, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. No DEFAULT. + MAAT_OPT_REDIS_IP, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. No DEFAULT. + MAAT_OPT_REDIS_PORT, //VALUE is a unsigned short or a signed int, host order, SIZE= sizeof(unsigned short) or sizeof(int). No DEFAULT. + MAAT_OPT_REDIS_INDEX, //VALUE is interger *, 0~15, SIZE=sizeof(int). DEFAULT: 0. + MAAT_OPT_CMD_AUTO_NUMBERING, //VALUE is a interger *, 1 or 0, SIZE=sizeof(int). DEFAULT: 1. + MAAT_OPT_DEFERRED_LOAD, //VALUE is NULL,SIZE is 0. Default: Deffered initialization OFF. + MAAT_OPT_CUMULATIVE_UPDATE_OFF, //VALUE is NULL,SIZE is 0. Default: CUMMULATIVE UPDATE ON. + MAAT_OPT_LOAD_VERSION_FROM, //VALUE is a long long, SIZE=sizeof(long long). Default: Load the Latest. Only valid in redis mode, and maybe failed for too old. + //This option also disables background update. + MAAT_OPT_ENABLE_UPDATE //VALUE is interger, SIZE=sizeof(int). 1: Enabled, 0:Disabled. DEFAULT: Backgroud update is enabled. Runtime setting is allowed. +}; +//return -1 if failed, return 0 on success; +int Maat_set_feather_opt(Maat_feather_t feather,enum MAAT_INIT_OPT type,const void* value,int size); +enum MAAT_STATE_OPT +{ + MAAT_STATE_VERSION=1, //Get current maat version. VALUE is long long, SIZE=sizeof(long long). + MAAT_STATE_LAST_UPDATING_TABLE //Query at Maat_finish_callback_t to determine whether this table is the last one to update. VALUE is interger, SIZE=sizeof(int), 1:yes, 0: no +}; +int Maat_read_state(Maat_feather_t feather, enum MAAT_STATE_OPT type, void* value, int size); + +void Maat_burn_feather(Maat_feather_t feather); + +//return table_id(>=0) if success,otherwise return -1; +int Maat_table_register(Maat_feather_t feather,const char* table_name); +//return 1 if success,otherwise return -1 incase invalid table_id or registed function number exceed 32; +int Maat_table_callback_register(Maat_feather_t feather,short table_id, + Maat_start_callback_t *start,//MAAT_RULE_UPDATE_TYPE_*,u_para + Maat_update_callback_t *update,//table line ,u_para + Maat_finish_callback_t *finish,//u_para + void* u_para); + +enum MAAT_SCAN_OPT +{ + MAAT_SET_SCAN_DISTRICT=1, //VALUE is a const char*,SIZE= strlen(string).DEFAULT: no default. + MAAT_SET_SCAN_LAST_REGION //VALUE is NULL, SIZE=0. This option indicates that the follow scan is the last region of current scan cobination. +}; +//return 0 if success, return -1 when failed; +int Maat_set_scan_status(Maat_feather_t feather,scan_status_t* mid,enum MAAT_SCAN_OPT type,const void* value,int size); + +//Return hit rule number, return -1 when error occurs,return -2 when hit current region +//mid MUST set to NULL before fist call +int Maat_scan_intval(Maat_feather_t feather,int table_id + ,unsigned int intval + ,struct Maat_rule_t*result,int rule_num + ,scan_status_t *mid,int thread_num); +int Maat_scan_addr(Maat_feather_t feather,int table_id + ,struct ipaddr* addr + ,struct Maat_rule_t*result,int rule_num + ,scan_status_t *mid,int thread_num); +int Maat_scan_proto_addr(Maat_feather_t feather,int table_id + ,struct ipaddr* addr,unsigned short int proto + ,struct Maat_rule_t*result,int rule_num + ,scan_status_t *mid,int thread_num); +int Maat_full_scan_string(Maat_feather_t feather,int table_id + ,enum MAAT_CHARSET charset,const char* data,int data_len + ,struct Maat_rule_t*result,int* found_pos,int rule_num + ,scan_status_t* mid,int thread_num); +//hite_detail could be NULL if unconcern +int Maat_full_scan_string_detail(Maat_feather_t feather,int table_id + ,enum MAAT_CHARSET charset,const char* data,int data_len + ,struct Maat_rule_t*result,int rule_num,struct Maat_hit_detail_t *hit_detail,int detail_num + ,int* detail_ret,scan_status_t* mid,int thread_num); + +stream_para_t Maat_stream_scan_string_start(Maat_feather_t feather,int table_id,int thread_num); +int Maat_stream_scan_string(stream_para_t* stream_para + ,enum MAAT_CHARSET charset,const char* data,int data_len + ,struct Maat_rule_t*result,int* found_pos,int rule_num + ,scan_status_t* mid); +//hited_detail could be NULL if unconcern +int Maat_stream_scan_string_detail(stream_para_t* stream_para + ,enum MAAT_CHARSET charset,const char* data,int data_len + ,struct Maat_rule_t*result,int rule_num,struct Maat_hit_detail_t *hit_detail,int detail_num + ,int* detail_ret,scan_status_t* mid); +void Maat_stream_scan_string_end(stream_para_t* stream_para); + +stream_para_t Maat_stream_scan_digest_start(Maat_feather_t feather,int table_id,unsigned long long total_len,int thread_num); +int Maat_stream_scan_digest(stream_para_t* stream_para + ,const char* data,int data_len,unsigned long long offset + ,struct Maat_rule_t*result,int rule_num + ,scan_status_t* mid); +void Maat_stream_scan_digest_end(stream_para_t* stream_para); + +int Maat_similar_scan_string(Maat_feather_t feather,int table_id + ,const char* data,int data_len + ,struct Maat_rule_t*result,int rule_num + ,scan_status_t* mid,int thread_num); + +void Maat_clean_status(scan_status_t* mid); +enum MAAT_RULE_OPT +{ + MAAT_RULE_SERV_DEFINE //VALUE is a char* buffer,SIZE= buffer size. +}; +int Maat_read_rule(Maat_feather_t feather, const struct Maat_rule_t* rule, enum MAAT_RULE_OPT type, void* value, int size); +#endif // H_MAAT_RULE_H_INCLUDE + diff --git a/src/inc/gram_index_engine.h b/src/inc/gram_index_engine.h new file mode 100644 index 0000000..a69e924 --- /dev/null +++ b/src/inc/gram_index_engine.h @@ -0,0 +1,68 @@ +#ifndef _GRAM_INDEX_ENGINE_ +#define _GRAM_INDEX_ENGINE_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define GIE_INSERT_OPT 0 +#define GIE_DELETE_OPT 1 +#define GIE_INPUT_FORMAT_SFH 1 +#define GIE_INPUT_FORMAT_PLAIN 0 + + +typedef struct +{ + /* data */ +}GIE_handle_t; + + +typedef struct +{ + unsigned int id; + unsigned int sfh_length;//size of fuzzy_hash + short operation;//GIE_INSERT_OPT or GIE_DELETE_OPT.if operation is GIE_DELETE_OPT, only id is needed; + short cfds_lvl; + char * sfh; + void * tag; +}GIE_digest_t; + + +typedef struct +{ + unsigned int id; + short cfds_lvl; + void * tag; +}GIE_result_t; + + +typedef struct +{ + unsigned int gram_value; + //unsigned int htable_num; + unsigned int position_accuracy; + short format; //if format==GIE_INPUT_FORMAT_SFH, means the input string is a GIE_INPUT_FORMAT_SFH string + //else id format==PALIN, means the input string is common string + short ED_reexamine;//if ED_reexamine==1, calculate edit distance to verify the final result +}GIE_create_para_t; + + +GIE_handle_t * GIE_create(const GIE_create_para_t * para); + + +int GIE_update(GIE_handle_t * handle, GIE_digest_t ** digests, int size); + + +//return actual matched result count +//return 0 when matched nothing; +//return -1 when error occurs; +int GIE_query(GIE_handle_t * handle, const char * data, int data_len, GIE_result_t * results, int result_size); + +void GIE_destory(GIE_handle_t * handle); +int GIE_string_similiarity(const char *str1, int len1, const char *str2, int len2); +int GIE_sfh_similiarity(const char *sfh1, int len1, const char *sfh2, int len2); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/inc/inc.mk b/src/inc/inc.mk index 43fbc58..b4119d5 100644 --- a/src/inc/inc.mk +++ b/src/inc/inc.mk @@ -9,6 +9,7 @@ d := $(dir) OBJS_$(d) :=\ $(OBJ_DIR_CPP)/moodycamel_field_stat2.o\ + $(OBJ_DIR_CPP)/moodycamel_maat_rule.o CFLAGS_LOCAL += -I$(d) diff --git a/src/inc/moodycamel_maat_rule.cpp b/src/inc/moodycamel_maat_rule.cpp new file mode 100644 index 0000000..7d88947 --- /dev/null +++ b/src/inc/moodycamel_maat_rule.cpp @@ -0,0 +1,66 @@ +/************************************************************************* + > File Name: moodycamel_maat_rule.cpp + > Author: + > Mail: + > Created Time: 2018å¹´09月04æ—¥ 星期二 16æ—¶56分42ç§’ + ************************************************************************/ + +#include + +using namespace std; + +#include "Maat_rule.h" + +extern "C" Maat_feather_t Maat_inter_feather(int max_thread_num,const char* table_info_path,void* logger); +extern "C" int Maat_inter_set_feather_opt(Maat_feather_t feather,enum MAAT_INIT_OPT type,const void* value,int size); +extern "C" int Maat_inter_initiate_feather(Maat_feather_t feather); +extern "C" int Maat_inter_table_register(Maat_feather_t feather,const char* table_name); +extern "C" void Maat_inter_burn_feather(Maat_feather_t feather); +extern "C" int Maat_inter_table_callback_register(Maat_feather_t feather,short table_id, + Maat_start_callback_t *start, + Maat_update_callback_t *update, + Maat_finish_callback_t *finish, + void* u_para); +extern "C" int Maat_inter_read_state(Maat_feather_t feather, enum MAAT_STATE_OPT type, void* value, int size); + +Maat_feather_t Maat_inter_feather(int max_thread_num,const char* table_info_path,void* logger) +{ + return Maat_feather(max_thread_num, table_info_path, logger); +} + +int Maat_inter_set_feather_opt(Maat_feather_t feather,enum MAAT_INIT_OPT type,const void* value,int size) +{ + return Maat_set_feather_opt(feather, type, value, size); +} + +int Maat_inter_initiate_feather(Maat_feather_t feather) +{ + return Maat_initiate_feather(feather); +} + +int Maat_inter_table_register(Maat_feather_t feather,const char* table_name) +{ + return Maat_table_register(feather, table_name); +} + +int Maat_inter_table_callback_register(Maat_feather_t feather,short table_id, + Maat_start_callback_t *start, + Maat_update_callback_t *update, + Maat_finish_callback_t *finish, + void* u_para) +{ + return Maat_table_callback_register(feather, table_id, start, update, finish, u_para); +} + +int Maat_inter_read_state(Maat_feather_t feather, enum MAAT_STATE_OPT type, void* value, int size) +{ + return Maat_read_state(feather, type, value, size); +} + +void Maat_inter_burn_feather(Maat_feather_t feather) +{ + return Maat_burn_feather(feather); +} + + + diff --git a/src/inc/moodycamel_maat_rule.h b/src/inc/moodycamel_maat_rule.h new file mode 100644 index 0000000..bc8ea8c --- /dev/null +++ b/src/inc/moodycamel_maat_rule.h @@ -0,0 +1,67 @@ +/************************************************************************* + > File Name: moodycamel_maat_rule.h + > Author: + > Mail: + > Created Time: 2018å¹´09月04æ—¥ 星期二 16æ—¶55分54ç§’ + ************************************************************************/ + +#ifndef _MOODYCAMEL_MAAT_RULE_H +#define _MOODYCAMEL_MAAT_RULE_H + +typedef void* Maat_feather_t; + +enum MAAT_INIT_OPT +{ + MAAT_OPT_SCANDIR_INTERVAL_MS=1, //VALUE is interger, SIZE=sizeof(int). DEFAULT:1,000 milliseconds. + MAAT_OPT_EFFECT_INVERVAL_MS, //VALUE is interger, SIZE=sizeof(int). DEFAULT:60,000 milliseconds. + MAAT_OPT_FULL_CFG_DIR, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1.DEFAULT: no default. + MAAT_OPT_INC_CFG_DIR, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1.DEFAULT: no default. + MAAT_OPT_JSON_FILE_PATH, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1.DEFAULT: no default. + MAAT_OPT_STAT_ON, //VALUE is NULL,SIZE is 0. MAAT_OPT_STAT_FILE_PATH must be set. Default: stat OFF. + MAAT_OPT_PERF_ON, //VALUE is NULL,SIZE is 0. MAAT_OPT_STAT_FILE_PATH must be set. Default: stat OFF. + MAAT_OPT_STAT_FILE_PATH, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. DEFAULT: no default. + MAAT_OPT_SCAN_DETAIL, //VALUE is interger *, SIZE=sizeof(int). 0: not return any detail;1: return hit pos, not include regex grouping; + // 2 return hit pos and regex grouping pos;DEFAULT:0 + MAAT_OPT_INSTANCE_NAME, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1, no more than 11 bytes.DEFAULT: MAAT_$tableinfo_path$. + MAAT_OPT_DECRYPT_KEY, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. No DEFAULT. + MAAT_OPT_REDIS_IP, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. No DEFAULT. + MAAT_OPT_REDIS_PORT, //VALUE is a unsigned short or a signed int, host order, SIZE= sizeof(unsigned short) or sizeof(int). No DEFAULT. + MAAT_OPT_REDIS_INDEX, //VALUE is interger *, 0~15, SIZE=sizeof(int). DEFAULT: 0. + MAAT_OPT_CMD_AUTO_NUMBERING, //VALUE is a interger *, 1 or 0, SIZE=sizeof(int). DEFAULT: 1. + MAAT_OPT_DEFERRED_LOAD, //VALUE is NULL,SIZE is 0. Default: Deffered initialization OFF. + MAAT_OPT_CUMULATIVE_UPDATE_OFF, //VALUE is NULL,SIZE is 0. Default: CUMMULATIVE UPDATE ON. + MAAT_OPT_LOAD_VERSION_FROM, //VALUE is a long long, SIZE=sizeof(long long). Default: Load the Latest. Only valid in redis mode, and maybe failed for too old. + //This option also disables background update. + MAAT_OPT_ENABLE_UPDATE //VALUE is interger, SIZE=sizeof(int). 1: Enabled, 0:Disabled. DEFAULT: Backgroud update is enabled. Runtime setting is allowed. +}; + +enum MAAT_STATE_OPT +{ + MAAT_STATE_VERSION=1, //Get current maat version. VALUE is long long, SIZE=sizeof(long long). + MAAT_STATE_LAST_UPDATING_TABLE //Query at Maat_finish_callback_t to determine whether this table is the last one to update. VALUE is interger, SIZE=sizeof(int), 1:yes, 0: no +}; + + +typedef void Maat_start_callback_t(int update_type,void* u_para); +typedef void Maat_update_callback_t(int table_id,const char* table_line,void* u_para); +typedef void Maat_finish_callback_t(void* u_para); + +Maat_feather_t Maat_inter_feather(int max_thread_num,const char* table_info_path,void* logger); + +int Maat_inter_set_feather_opt(Maat_feather_t feather,enum MAAT_INIT_OPT type,const void* value,int size); + +int Maat_inter_initiate_feather(Maat_feather_t feather); + +int Maat_inter_table_register(Maat_feather_t feather,const char* table_name); + +int Maat_inter_table_callback_register(Maat_feather_t feather,short table_id, + Maat_start_callback_t *start, + Maat_update_callback_t *update, + Maat_finish_callback_t *finish, + void* u_para); +int Maat_inter_read_state(Maat_feather_t feather, enum MAAT_STATE_OPT type, void* value, int size); + +void Maat_inter_burn_feather(Maat_feather_t feather); + +#endif + diff --git a/src/inc/stream_fuzzy_hash.h b/src/inc/stream_fuzzy_hash.h new file mode 100644 index 0000000..9e85e81 --- /dev/null +++ b/src/inc/stream_fuzzy_hash.h @@ -0,0 +1,78 @@ +#ifndef _STREAM_FUZZY_HASH_ +#define _STREAM_FUZZY_HASH_ + +/* + * Copyright (C) MESA 2015 + + * + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TOTAL_LENGTH 0 +#define EFFECTIVE_LENGTH 1 +#define HASH_LENGTH 2 + +// typedef sfh_instance_t void*; +typedef struct +{ +}sfh_instance_t; + +/** + * create a fuzzy hash handle and return it. + * @return [handle] + */ +sfh_instance_t * SFH_instance(unsigned long long origin_len); + +/** + * destroy context by a fuzzy hash handle. + * @param handle [handle] + */ +void SFH_release(sfh_instance_t * handle); + +/** + * Feed the function your data. + * Call this function several times, if you have several parts of data to feed. + * @param handle [handle] + * @param data [data that you want to fuzzy_hash] + * @param size [data size] + * @param offset [offset] + * @return [return effective data length in current feed] + */ +unsigned int SFH_feed(sfh_instance_t * handle, const char* data, unsigned int size, unsigned long long offset); + +/** + * Obtain the fuzzy hash values. + * @param handle [handle] + * @param result [fuzzy hash result] + * Fuzzy hash result with offsets(in the square brackets, with colon splitted). + * eg. abc[1:100]def[200:300] + * @param size [@result size] + * @return [return zero on success, non-zero on error] + */ +int SFH_digest(sfh_instance_t * handle, char* result, unsigned int size); + +/** + * Obtain certain length of fuzzy hash status. + * @param handle [handle] + * @param type [length type] + * TOTAL_LENGTH:Total length of data you have fed. + * Overlapped data will NOT count for 2 times. + * EFFECTIVE_LENGTH:Length of data that involved in the calculation of hash. + * HASH_LENGTH:Hash result length. + * @return [length value] + */ +unsigned long long SFH_status(sfh_instance_t * handle, int type); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/lib/libMESA_htable.a b/src/lib/libMESA_htable.a deleted file mode 100644 index 6fc977870946776852feaffe1ccb9c04e7ca7b40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138390 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

vyS%(mM$-ji#z?05? zr`dk8{|N1e&qZEBCP3+m9IUc#d=z_n6VFJoT!M4fh8S`^GIkCe+=)TY-Z~AGFxecgUJ0FoGdMBGiRAon>j2-&i;gAX zBvY&(V3x~^PA9wV$LimiwIA(z?GL@WpYEi?OY$$72#XKyCfT{*I47YkVP65QkeU%c zauGs5+pJ#bA8=M6O&Jdgb$J>!K!Kqw^s>Jz!3OtEf7=Gp*hM(*1pGxe!d&2lk>7w1 zpVuG~)8fe*G|S*lz7A1#=kV2S=?}R!^pnwR4*VJnzOLJVhOFThGPN^{J#Gtbl`zQ) zS&RwIGNCdPnr%YM>B|_7i{k=vqPN?!&*O7-fDebtIAw*T%w><;@^4O9lpR%jZ2_MBwq_5cA^Da;~C3`HUz9S6L)APJn?VvZ`|<4Af0Pi^UG6RML-dih7c1X%R8 z(*({z>7!Hd!7Y956e5M=vi@N|@Ze((&(rP!S!nphlm!XXFu#=82rl(c7|m{U-^ zJ{t};fmRJ=1@+jt@C3KHwf&0b=5VjvQ*Etnt>IqNPetws_lDnn;U3f5+FGw@i|lP} z1OF+wwuiek8}0 z4Sbitdy7C`Q$Y~lU4avD8~No4dd0+k_uD`E!FxO3J2=C7DFRt}?`y!R7<9mdXMot+ z`t@=1218wZgLk@yd2?Ky7lEAg>jQ|<>kIbVz(r!j;C9g#3xL8Rz2l{N&e^F znbtFX4vlN@U1u2{%wmmk3I_3m9F}1f)&<1>$ao=N@e($pSqm}Vuo-To!Fgd!#e~gh zOQFc$UTyHB)4hv~v6|Ex2CQ%=zEt>i+BB4#1T1ZZ*~)oR5-s zoZe*nBaU-Kf`gYFM)UF=g{1}Kklk)Q@;r+;&K3TisCi@ze!&kjK9H}1cja;&%s9$r zh!C05L;BuKgO_~v3!i`C{9LN=V#e)vNKnrQ7%x)v+ZbP{aC{&|C(oE7_X-0C-XTH$ z4>MlPSD|0Z;dKgsiur#|;WccG-3tFM)8D7?1c$$>@cA63X$DJ0V!UVuw2vAJOMwg|nK@ql*6{9PzxO7ykCUA1GJ&+wXoL z{%KDCE5-jH^FOWli+!a1i9N+W1KECp<2*LH3m8Xs=#~5NL`8oBiNVcMxU8*6e=dCF zesi6o7dzA{T=>fxy6E`==ks$+FZ|`cb+6(h{J*B~Z!w>5Df~v0EvHTKm;2n$6ut02 ztZ?D~UkaCY_$S3*?w8px34u=XEBuEk{9Z%mj8{0GrGf5pg}-iKr-*T}o7|6ADSDCn zS%nM#jS3fiZc+GEtPh?sfllN~{di2#i`;K3T!XM#qhr*?Q zIIM7K*Hj=7CwfXdjk28tm-bbtaA_B13YYr4P2p1C_9 zwCUvUXZhx!Ie9pHXsF5a)!AH!OABrfBJL}|zP%$rVr zcfixhFV03Ta88R7sDe&j!8Eg_fq&1q2Vxq3fIWKldmv8oek9bV5P#rz0L&KA2he5l zT0VYl*6d}of**^E=J01evWQFcyO+3TN!;65@Ug_^i?JDYA*AfT0M7W)N!a`V9{`7C ze%S@^KeZpj*5@~{WkB>v+02o$KjSlIGlZ9$uUe`9zrSC@&i5co^I*PVcgcZcOU|?# z0}7M(&d^SZ1ILD)DTUsP0rf9i?cX?UwqkhZz0!pLzWp5ER7~9e{rw%=p;N4ybl87OVi#;s4VSII4j)D(6 zIEX|qe21n-L#)Da&%>u?+=9_9QVYIr^yMzY1;>NrCh*m5!7kizxgfx0sNqu912_)5 z4#<(C;WB~~d(+Hr%S|4a5dkivEH0yk3*CI&al&Par_0wpF5|w&5_@5!n~zDwVMmY7 z?A|cC=l>fdHF-V^zTUp!rW<0IqhOEQG68NWCfRfrV?r}bsLX_BnviTOSS);Iczmin zK8piZb+$stK86Pn(N0es!ZOL<=) zC5L3&$>SI)ZZ3P=78jbc$sG|uRS1N*OgT=roqW^gC3D1X3!a={QWJs4i$lzdL(9#= zhV{P%u-DEPUduKrhq@N+}m1{rQ^ocf(WMCWEL-Mg7+_Af9sS|iT1b-o?7=F)g2Z=d5 zU+p7dFm*S{K)1QM6?T)fzH5G9N69I@xn%Dt+FkO{sWjV5+D@hJFWGyFHkdqg>Wn)~ zK5mPMzoulq*y68Jsm&uM+TSN)ubr7V_$HCR`esbwZ)xEjC#kzjv;zLt5^vv0s;J8N zKXxxks>Hxf5!wP`e=j4jK}6>$Wn&DP3#v|9LTkee+mKcS&uSwISuCJSYDUr>2b!XZ zUK}+AjHuQb?eh2TT}f36IP>CbU?&SL!jf#-Ym*|}J6`saeW76hHhgd&x0huY63AYP z75-if86?oKNaC%%D623XxJ>bzO?1ZZJG5{-4PAu4wO6K{<7tc{@z!3Mw>X~09umLN z*Z)I~rxAw4Z}Q`Zz?cZ#82piVYp;x~w-ws))?S%aoSsGql0M{rUt%-I(>Orlt-UfW zXNYg-c)FiUdTTGvTO3dKXo(LMQU-^B&L46-&ewVQ`ybvbgX<5tmqi@aJqTRHxP)aW zUdZ?YxS|vM)8I!w9SDM(WiAd54iVqXBB*QuyP5Gv7%zmdxWDIn2bkvX-*ZFbbrI)X zGk-6juSa8h((TcC5D0LO$VMiV$AbA z)oH7pF>?2YgQHV>X1uAVqImyJMfDaN|E`fr>-YFdxwLy{3@3kVqSK_=M`Mzl)ovP} zRPU)VG3jsMX2Wah?b^R7m2>&P!vURGskf{vy*${S-c{ z8DEIZzzHsU6>tp%o!}-EJdYLme}^Ng`6~3Xj=N6bvbSly!ewpoW`)b133+H0{@a;O z&Jz|KujpFM#nb;UoZoLUjzQc0uPFNGIP#RjOF8@}z6u|?FJ!YE(MR}9Q+Nlbmvc3R zkK9M395kP;iq8YgX9wfLNBY|qMKAsD;|j<7F*@1%Abg~Mw)eVVxeK48iqAw2pWv(T z=|JY-!faQ`*DeD){S^KLhc8t4iyW5o@r2Jp#_>BP=tQpEM;0hPqW=noi#~E65dPO2 zwoaYmFZUxk$3y6azw|SL3;#!yT)9s@rufS^;rohS?DLw!#Xjrub)UkepX^Y$gbyoR`XwqT zh!Z`fKZ#<7EL{45LWN8FEmOF($88FicC=67QvW*?F7@`X!lhn`aYcVA_fN1F2+vii zjIT~s!SHiUy~F`77cKWmOmbF=VcWTAlydX?OzUjAFtZB~S(F`+XY2^b2VTAaxIjrdGa>m zi*|PTp-Ni+8o)G9fSk=NU!F4%L#Jsg0ZS+UDV8tw1nUp>{aQXQxgk%{6TIY>BQZD` zFPM-oU-Z}p5$V!j*4w1GL=y5<(#drvr&nZ7J*U^Wq!S$H7ZAh#$94^zevpcg{0r@U z;7Mn{m)L&WSRmRD@9&ac?26BSy6phu6c5E;$9c?LykD(L_m z?L5cC3^E&XPM=Mx96J@A_mP_FK;bAX0AzlkFq%D9ywv&(nu7)s6=h2ZsW}P^G|A|N zw#p&z5_2gWwe!ctyJ@m)KQcImKl@=X$VYrShB;Q_>f+`TIk08q5Tre{t1!R$bY%Sy z^cU{}lM}SF12>*v^5UIyvJVs;L2u`(??>UnFrL5>Ob)E*t7ahSZPlt9biS8;oGVO&8Uo>=>^boL!1cL-Wj|oxB5bmBSZhkRm zCD(%H6H&GtieCQ-5LV`b`xyEIp z09zP}Zp<4hpLfCuiA|B2QQDb#K^AtF9tMtBhNzOM*0JU-hn>U{%FFo99^c2>WJ(3| z1txh65;UI0v55G=lldsezQ2jz1 z#%NtmLK?|u5$FNA`lxdjcqsiOth33KL~T|gFtSO6R>DNkhjd~uN7bYVC;{;5Z$35~ zID_eM@y^miJ4=2cX-a>ADW0cJ3yizm%G|B{fhqCZMXeM%g~IIOX)h#EAgX&D@9l;F z&g(H9HUtmta=>s~x&zgpF;i=x^r0WxFcPlC6$iZpyNj{0gF;j`n1jyYiey&`3Gpxe zkPe=rjIM$ThB?O$oqz=Zbh>*!LFQNb*6n-iY^idYGLOfVczMy8M-yS@kN=KM#C@^0 zL23p&7yiS{1v>2LNDeXl*AK9ir!Avc0zQUi1NP*ShpO?;Y12;?bMxnW2wVMMA-92I zco6`*81T*noif~+b|w$*EPZun$x8={PN2+Pr7wZ3Ltdx<3u@penzx*A`rbOjSQ&0C zQ1|H2Ec8aW8lHd>`{;O2rePb;KyJI1Byvb*K!zUyjpy(+)VI}d1f9^ivwazMF%lIF5daNbmpYNu2LZU8CZFc5$EU8 z+*`p|2ae_cHyIvo=h$syxg3HU9CXoM2@u)U&{OUzIzbJAfY|_037mjC=v4m>n*DqS zn8Lbq$S!7Img+a4w>I6z;o{~k$DHJa#k*012it0du(T zH=lMBQTS=>1^2GR=;qVmL|!M^n93B!U!yuNp>o6gK``1m#t!Cpo@WPhIwK$h%Kjs0 z=O1{TT$DOFs*yO>3#fW5y{ae;Em`-+k3QPbv8e|QsgCb3+QZNhO+Jf=U`*jvQO+HX zC(BYnY9Iml`6#5YEsi&6kV~1R{o!6&k~RgP4feiHhO9wie>{!5%Ax*b8c%=FN&1_t zjF+rfwFg+iP@9I<_P~jT;T)%p3`!tKMx*l($o$bAntVvu7s)R}ao40_}(*eh(ekClE2_EJR2yzVOL%fomOSn~QO2MeHnv zMvg_%+Z07t>^--7?Gz^k(+JoFsdPasfzC43N@w6d^UVCE6i(XuFM$U864a#*s7w2y zK7b!?I?#Z<0ZCVAl_tGRcuo2-e{%zDWy6L@_-%uU0!su?~V=74M7N0gdUG{ z>AW1mpb-X6V-(++fD^*d%dOE%!nMJo)V&fL$WgA)KOfc6ABiZPWyc-Gd(;7C+>- zGhqj+?7%g4pu`Ro+JQ-SV2m9YYzKN-fjyAb)7SnGm9re}U}&Vz-KRS-a!UTC`BRbn z#eI{o1v?k4oLsQhB&w;aic!L7eM2G&U*ld=TT_u39!*p?#>*>XP33Fi(VC{{=*scY z^18}sbZxRWQ4`}>{GvQeKgW^eveA{pFIbtswujju(&#aU4I+)}qQhrI7nLqt2wwV9 zcy!E|Xk~e#T&}U&c%73jG6$(JHaat!Kh9}NR#e2Bnw+Y5UA(cpHVU7auWxk98>^By zN8ZF?lcBog+ST#K=$d40tz!`QaD08^CL1vyln3@ziE2&^8$+U)F&#|Qtc}+v6VCj) z_2soSl_ok0LeQdrpHrrfHNiLM!TcT*9ly3bk!*~6(b~c>EGj>ix9IpK5W~;D+ccU# zWZ6!h!XMMyG&()*X|WmTc=_zSO@sb{eaxIC%mz}ul)Wh)-MVbgYbHiR-LWEvBVD>| zforHQ{!&vl>lRyS_2MS_-9o>c>GwwZy@7r=(eFm5TbC?6qz!s>U^Ey{HIJaT3yw!Z zqw&`;reBwSL-ZSVx_6<;e3NwbD?r*Kau9xdqB_UnuNP|B%kw$iBb1T`lY_!thC^;4 z>yVKw^s>Jz`XdJKh77WS?Mb+joGx8)a-h3;<+i&^x^@qd9^GBaQg@@CM?zqaNDU+Y5!92jAf0tp_T*kvv$AgX?we+?H)_py-32^-hqDB@J{p(GFsh*MG`gs zyD@E!@qZEh^RssS6%rXu@2T*-_xk943DD9g-5aL7N$EUR`hNT#YmW$?v@wvZ z9d(D{(i_QgFGMfm5qI3@;7aPa7x~_c$D$7BqV5Aa z+|l@TPa|TCC1R{4A_x81=dFfcV-R-&ax(^TbJ4J)42L}Q8{@e7$auWL3s6|T@lV9k znri%$&SEhn5)5&j&dlwK=cH zW&+M_83cW;NqscHnaDlP9AeJL4CiWN%i~-Nqr`ADbA~oV;gMe08BV}OuC2&l5d#Nr zs(~US$a|5?nn-Xez@fcMBv=P5Lwk!QXq1^dk_uxyafg@7MiHwC9ITB}P)@oMlE52b8;ob$45q!i~wuTO3qwu^Uisu^Uj%unX;F<*o`Uw=}5SHkKyJ-QrF_ir_}yM^^xWsLQVC*OqSm^CogX!vv7=iCW+ zWhZ{OWk7f6G!b>%Sf6T6@xwq4S~;cL@?}Udrg5F){*YKg!!#QH(7hdKpcYEd7@ML{TR_fjo*ewrA<6k$P>vHat%7T|8#492lGzQU zFmrJ#Ja#_=cE)-GLs&%6G$d$@StdbKbC**#ehsN~HV~t13~>4)SCfsQ8RUKnc83|x zLxXaMh5~YjhHSalKq@U4F_JsT>5E*A+))|izTn9n6_h(FD0fsq?$6T5MU3S3clsh% zBX@iTxtGBx-{g0EQ11Ak-0=aqn99npRShQx0?>df@tH$uG(Z%fvVMJX(vvPn0zX#ftJRq#wD4?6!GYtKHq>CHwV^eqM zI2d^LkT;im4s^#3p5Gst^~!QkQt#j^&wYZgJohzkA7`HjdWaVNd=ePsyRKo^e>kwq z1KkGnw`d2Bf!v>a2}wGyH(c;N>OubN({j{3zd$`W_yYC$!5646@Y4m3+aCg{toN*k z`eeeMPtqUe#{}NLzR*8XA76)#_An)p)ej17IH$=%y9H8>U}dxB_s3KtSy>K_VxAlv z?Y)bg%4iJh&!l59<^2AW2P4-UJ6%BhI6FpEV>@2tXXp&y2(Tw0u%?O&<8Z-w0D>R) zhId}FsWA^;da6xU#`88#of4Zec>?V3Om3V|RhP_LU6W`^nX7@>GH_}F#e7nLKzxhU z3YI78*Va@Rpux<_B{nt0n*gD1q`TLdgr~aOTSKDilsL2GN|gAFStzSzl8yMC%`xW^ms9*qnbzTw^V ztyPh{sypHDt_?E^+Sa#X2z-&@5L1=+K;+s7BCp*E-pJ9&(W*P|+BjokWcY^2jH=|y zqYt&d8>xCIx$=8$bh!&yRaGSm?lhN8E8Bs#YD1*zk;sDN%6D6n`OTq`10o;nZC@F# zej|K-R7GFY2GQG*ycG5xw7reoq=CNFkcG@5&G0#gw0J=VJQMxI2+SM=_^(0!$;|kS z9gzWT?Rl+lCL>^*WaN9{#LeNpZ@ih;+V(+P_=cOq-Ct^pAU_)r)fYySP ze{Z^s2?NrSlg7zObmIXwkCRqS6wGr&qb_8)zC5{IT*{n4^uYPOhPuW7S@a zHuM5}>RarJ78;zNiwej_A|Au3Q}dFz&jaTQ?YH#(L^S&ui!FgXE?W`1vUJg$W%Cy= zip6Zc$;KE=B#`z>K23+~22Za8LV7T4MR{GUCRSM=gEuOjl zyUqi1FI+rlL2Oyc;?iX)-2HbSVb(c@9ftFd`HV&d^eYjT(XmFQWW6#Do9gQ~S;CAln^GuO*EjM9NYqR^ z&V+lPM}e9f-mYD3wP&3%ASvdNGDV_3QC(_hB2~ zsL47iMw8rIz)IU{p%$67C;G+uZB-!1OuP}=r_UKzcq-#-%3<}4R(hluan;AK_mr*< zN)H=AO7<2Oi5aZav5M+AG$pD8vQ!2fHZ9R!vSbqsn4k)Y$M7-s^b1_pPsqAvN{L=oRI+5*3SdqJlWK5$ zU!IK*L~F3uPstsx4eBg_)^+uPDk!B0nFD5~?eU!O;}E#IN(?H=F2;@hu!rQ|Xev(NEvb@icWV@u7u85QVt6IG(1pm^LgeMe&m#sHU&!$^eeK2n$%`-Jc$$Qk`0zRx2{8RO zj{j?b|7kD%K8~m9Sug#~ls*b+I?}~M9>ZIRgk&6HnhCZH>uj1X{DjeJ$A=#CbQs0) zG(RXhgx>JtWxC_GK>8hCd?lx+=OQotbzc1TGsJ)84Dm0XA^r%*mj(1&>B;W`PlD*m z@W<27j~~VHG?^pu;XAxCoXhbYf%I*}AE9&(i{yXqJ4@YCRDyXDc--WQ0)A@qaK(8e z#3f8^9k44Hmo+I_Mk{6fDY&8&{Ce;spQD8OXGZK`d?<^SWw84g#|cw()P~{u4fsPo zC0wSmfQ5S)5tq6^?G>=s8JF)N$THL0jQ8mR9&oZq^k3jdKHIYhcBox~7tjAwIxY55A`wlbd2 z_yDHIcdt?I62?U?F1;h(z_{>#h4DR#KeB^e63jJt37cadgCNnv%xfB#`RpERZ-|vH z^LMeZmZrLN-`B$LGxVdc${Ttv9w!3%><4zBiCT!_;&(;?o=%&~GT^f^;JB|L9iLBU zz-uz#TQlHyWx&6b0dLKKG@m+9M5*ZGl_2JDna)WuphpYnU2qKrf+9@y0-wm z3w>oe`o4f8{~B0qK__cbV=~ZB%YZM)fa6&W$bT-(&Z47x6=_&% zrN=ry$j^B`KvM-N(=C2H&u)qO!N;Ra^FO(hr%s*> zgWFSsu{etd6Ce7CiRODMECsD;A9)~z>6<2a#7o51l-Hzk;775b@^nO|K%|{IG}Aw$ zRBPl<)9Dzhj~etIm30JGSB)WYI-DwVI^whuGMgs(Gv%dG%C&%+w>Oajeauq0@1le5 zAMUcatIjyw-iHplCA#<0yOyvU{Yw^(V@<(jLk7~@_|G%YlR)N=3zEWT8!7@0`CJTt zf2h>PkM}siz!O$K~Ck1LT+zD2({1D?-A zcul`D1AbEm{7DNhu;jjD;S(+VNghaPxvyEcU0%Ppa64ZEd0e3Rlv}th_Yn)9Z0YkB z<5J(`J5t9KF8BNYQg{pSLid5iAD>yIyl&y~i13%Q$L>_P@V{T-!v7)0F~2tdUt0K7 zOYVCXKFz|jc$|WKY)?@;vPng6Q_7ydt2xbQ!ef&WMz$B7*l zGXFe<3;!t!7yg$suI;m3(bqEnJqj28_bB{DV@{_f1OFq6{tL|iq{4;&e=A)0f0%*) z*LggP`Lg?&p*%j-__z%CwHff*4ESaXp98X`{(r&3=UVuK8St-LxUK&$7?*OFv$1}a z0so!CWj^H33V)Bws~Zpch5v_)4^TMHL!cY3@L`PSDml;+R~)OL6FxZqgl?X>c=%8bmni&dY?$D#HW!aRpTjE@{ueezOyM^% zpSZ&Fx*6blg`?T#G5pR>!4r1T+U@ZPbeJc`Ov*@E}s3r&ic#zq}T!HMbLHUbb_C2U}vDh z<*dydg%4pqmneJ|18*%(Ek_fbCsgUc?)#XuLylR<1s~lJ(ri9 zEhY57GHkrF%>^%FJ*B<~Ud{BkY-fj&V6VQRsVfJ$X;j<4Zm0{;crhY#;mk&BKB2L8h0pj)nh~%zrHFD|ijlPgFSH zwwxIXFJSr=3YRlUW&9%i#XcJq{q?NRc7@-;{AJ!+_}s(g+oI^lvppYE_XC85JoG&VR;qy&}moT5_6dzgdJE-V| z&pQf#i}U-g;`0>i^P!>_K0P^|*!e;Z_v5SN>nQWdQS`#+QiUfuJWKJB^~dFkUiefg z{KqVJt>Po=m9l;)`U{^26`$c;Pg)h;!1eDtioe`?!M~=QA#Rg#V=qmovp@DL%5^x?Ir51wzA#jJIi8E`F?U=Zf8Q@#Pow17kZOhc-KtP zOFg?>(Mx_mrEuxzS14Ti`D%rW9X_w{ovi2G3Ku)HFfRErFY!ChR~5b3;R!{*mhJd$ zMKAUK2Z~oc5jv70>qjJN1fuB@kDrRYU&nZher{|1HMN?JJ1jf%gl%ip2s$1NpJ@+bH^xUU#(eoP$7d>SiLG;XLJr61RUork0#*rQ& z*+(Jk4%(jGc|uKa(Q}Z(MbEJc7d@riM9(FxPobih`OgK6i=MKtW3{3ed#+Qs=($bd zqUY@j7d`J|T=Z;UJs(r_cXR#xrbUmWvd`odMK5~(QsJWKF@=ksQa)mT(bMJmf6;Rf z>zU2C(98NOyfR5n>?!+RauvPkS)%ZZxF5b&;nM%qDqNm7Z&bKEZ{DVG`Nqh73V+L} z?fkRCf6MLeS;i$_vJdAciayNasNX95eJ;m8T71T^OuU#gdi>|mdswbHLB{mwqb+)7 zN6)P?*Nf3Ioh^DwlCQHz<59-3Sh;8kDOC784worho*%aOJ7vDjMI)OT*foFPZ6Ey zFXNLOh0A>(_Z`~wa$l}ixZF=~Rk-wnk11U47cVJX+W#?yOMe*Qc8SsWC;d;3!lk_z zD_q*kC;WB`yqbhBTk%&}z>B_I*n$MRhU`zZ{Qv0dE>yfu6fRh0q&*wdaSGX^-*)Ee zC1iA>5q1?qFA=xSFurUaU*kL>rsbeHvkAi32O-YI1wP}RmX1UKzH0g70Y{#nH2oAv zi#)MT*{*}-|FeqFYT?>#y8Pc;WVjt-rkH>1nsxr?0G2NQ4ZJm3=$Va|UcxiMlTLoa zm4-t0YomM|;tD^(mjkA4iYYg;{K34=ju<*ki|bhFu=E|G+M zm2`4_oYN~ZXDX-HxTF)j13ZYSV)oLPDf>zOB|WZ(rn6r)+fT-|Xg{pdl3whJ>*Y!W zAdYj07o18L?|uK6 z3*A+9>eM->PMtb+s_yNZ^;wQ=qro6)mr1%-qEg#v4oMXizAKF)CrPPNk`#sScxfcV zjR@A9pV`{XVOlCl3Ih<2NPH4Du_981Wwf+i5be2$*K6%a3Th|c#o_W@3d?9|yXcCR z${bBHBc;y)Q6HMd3p_0)T)Og)IZG(Nv6dCjOEXOzuBBSL>(P$n{GVP{fjC$2>rXF- z1V1em-C=zj0vc4EGiQNxWx_QNzOeU3>m|d-#s08z?G1&GG+aq6GVqy<59uWupC&ci z+ICTTle|E3|28sCk>kyY^S9igZXDAjHI0&_`%?#5qqeu*Aa6{T;!`(Xoo?RPB(;x< zzV||D_@;kZ4Mb}SKKg4K3d#6fgU?KSQt(N|Ck>x;d`JemvUnn8i&RBA3m=Cl=OLXV z^4BAsFY*hJE)x00B3&XelVA{`SZpJ8vGg&^&zWL#@i;WosUKYwUH~ z0lVst*9;tg7ngYN zdurCzrkOc6_{JRj>hC9GN)LYW%jfQ0Z+Ua>lrw8qkC}QP;$3&~I6300d9jZkxch^` z=UcyyIq@ssBPGipedmJwxnEk=ZGUx7tl^Q5FO+V5_R5#dS3G~sz{H;#?H%t-xapIe zhYk*yQ#I<>@oAr$zkjZ7?R&qPy5QlhTceI|dhJX9+@>ep35TD2W$bkw9|y`avfe+w za_q>$t6JXvNB(!~)Ds()mpu8`84ZyOjy(LrKkr$y;+?h+8sD7#W#N#26uJ+^J=(hP z!?}uo)}Kl@H2m$zhkKuzy>zwZqgxjbL$)8TUy1$D`L-YY=zj3O>PK(q`hlAFkMDO||Q7x6?|EAX|2E>6II4Li9& z8Y&&wtT2cc-(1f88|2C7h}wF%RM77da$YXzJS^Z%g73uwK9kw`K&eZ>hYI*b%nuBd zc8Z1fQUNa$d~LKqCl{@yB(Y=x?H+(<$g&#;c`2YxKp!ohslfg*-|w2auno z>nWk~UZP*xIA#cZWhq0Grt(A@qUlHIP1EyYfnO-_ zY5k|`B;=uS=?obc;u9+kl6sagwBXl$f=;UFZ@H*>O~6kHxF&z8(8ErZBbEyM6+%u) z^lOxWM~HFRDeM8_u`6G|6)|2Kyi>rt=5jnuKgR_8R0#YJXc!x2z5Yy$dy^PnO`h9? zoVE}->lmGZQoa~Rjqk4o9fbl(Tr$dZJ*)Lg$Uj=xL!zLggwXjy@a+_Qwf_DM?Z_S! zA?FpGyp$mD+r+qQ_|1ZEo8UV@;2#roSWpcZ(Ac$3jANIulLbP~e+am(kmIHDM8bxO z$&(+Vw~s}?+C}iYOW^-W=*LDuHm;Y2ey)O{Qon3P96ng^jnU>&I){JB6KSXD?>3w0|F0>;#Pl^6&d`Ag< zYX(O=A`}`e`n9u=!-ZRs-V}N`B__)ILY}>lpXTEsArG3f>j`A6QkY!LKV9_eI?=DxWt^clz7GmI+d}NE9Qf3) zu8@9RF6f*pn(BgjXHlKApg<}ptEjG!3UcS?6_k|Ll`gNS zcb3-8&&#N+sV<#gw5+m}*M+YsC~hnw0!5V-w*hh1%7VFqLPlj#eSK-YR8>@2SyL=k zl~&c4I;C1?d0lByNkNs%S=w0Sbk-Gsc{uR$Qm1oOZE2VWAai~VCzF>oFRh>w9VjTS zaaB7Ru%I0Lh<$NYt>mh&EG?~-YHMmstEDpVsF!MsD{JaYrFv&wC19F-#YNS{rIk{B zad~M;!K#YV$`YxpuC!O2V)VkQQSB^~mK7DRaMl$Sm-glg>Q_}QtEmJbfm7B{R{>tb!~)2+okp zoiuy}j7&jsowF|(y)P)OEUhZ2c2)Jk59u2lDwqaDuUUl-z)0%);1`vY1og&|UDX)! z6(K+d3he~i7C4J4D%k+lioOWBL;HbpK}{tIQczV??JDZqo%+(EIt)Q5gQ}X9rJRFa zB^OKHXm->cPMNU^; zU)C#2>*^~oQ5R;-otJyvoPxi|i)*S&3L1(+=TCvNZdD)sGranW<(Q>X zF`tf%b>*$DQdemK83N`nG;c9fLdLvit{~Qm>x~DO`DvoYsHs>aDCu4 zA);~MdBW(xLL@lwpiXHQ^KfpH@Q=yN1VG z@){yXbm;1(V37Xb`)86`S&YYJy?68dhM?7Ec7Mrl9FbZPfn zL{Ul|!{Ih@KSqN~0&%R}I)0=_W>UZ}|*0&f!V10irp=&dsZ zzH>a+!=Vs(lYsj|;M)X!NxMJBo8!QV_doRfD5;Vogq~l-i5noH`>0d_58a2@=ztT~ zfx8u!M&g@3+SRFpYiB=HcSr})>&13M)n7TbtJe zP;_u?J%d+*4lcH8tj?x`Yi9#gk)(qY5A90T!LQPQNL3wNvqh@T*TFGSgV$mmd`b{1 zNrgK2avi)}2Opq=*XrN{b?`fjgY;D>bZi*@iW9ek(`?$g1w zvs)^w5bgVk~O*(j-4z8VTP_?3iYi9-oPtd`& zGh>3=bnp=b5Lc28u4q7{sXF*b9bDDHN9o}CI`|45e6bELj>1@Np$?v-!!Ostuh7A3 zb?|r{yio@qt%En|;9{v@wHtMCv9vIFn+`rsM`w!;E?hjT-KK*l>hO2y;1hN5ojSPQ z@9omTuhikU>)X48POgjRU!04m+wv0$c zj>_ePD0~N$;t<*OfJ^n&dP64nOv)XCoAuvGPzX$HdfwEWpbtdHdcO` z%Cuzon^^e?DwFy9YgzfXR3?|{FJ$EhsZ6fYpU=woP?=nwKb4h#L1l7vej6*_LFGYI zR#^EqDwC`7TUoh|%H*Q_5-Zi-ccD^%`c<&UXMQ`_Ik$_J@TQ`+Co%5PAarl5Z(EAOE)O+EiMR^ClznsWX&R(_hw zG}Zh~to#I($5OeLm48cRnsWX^R(_DmG}Zk1tb7lZDOB*MvhpveOjFKpW92)joJeJb zm2ac+L@HZZxsJ*-wfqt*-$G@YQvOqC+4xhLrjoylm2af7oywi8JeSIJ%Ij}ua28$YjI&(>!wi-Vy4n|Adts z_EnJCc|KM>SxNRYs;9$O{v!Zb3heDSE#Ec?+c z`w6wRGT!QFb;b|t9%>mOdFM5zx%U}z-LEBNCVyqQYby{{&q?1Pbl%}Xw@>;;BI^Y{ z^6qyG9^`zJzNgwRRnzKx)$=dk2w=3dr+GS@J3&t^-lMk8NcGH*9|V@*NYV_0G+sa; z&SL1Y<>310?#sYm^{!s*Fnq~Hi1W38aPo@A^**R3A=A@y)6%phX-m^?PAd@O1mfL~ zpp#YTPJmI~1GNy$JNvBx#;+HzBpOtDe2i$+^Yv zk;$C&O#?&NP^RY#DlKBA!@iR}1U>0{3@P<+Z&@3&hwg8J^yjJGhr4JjT0U`J1|?a2 zPk_GfTh{XMP5!Olk?Tm6ZW4OY<4^UP5V}lKTQ9m87NxeXwyK_w>}PyE#GXi9hsM4q zKni`!U@~QpQNyN#^C#$EE*A!7^%`5+U2$s1og@}r7$WOUa7$c&wDPJLbJ=HSY>-ns zI9^Ap%DLjz8u^tHI?n2hy-u=874{rT5Kfu~$^TxIuTsH+1;Rm{Pta^U(ENWbL zDJFv{2{&~Kh8b}u`q7cq$S^Q3F*&dnw9ed#4W;i7X99t4e^8%ki|p;s6MKx;XK8rm zQky-uAE%&5^{k1v!K~s79j!0MlW30CXX6WDJ&x8t#&;oAkrrZ35@xE}x;4HKvDj(# z4=V5r5!Mkr?kNQjhbQoqm8IW_^dw6$;;f#vV4YeMpVSLMs7EwZ4)Gj*s)+8wZEy2-6NZFdtMvLF_z-H#0s?^*RW9 z+TEb1Vk*KZs~++N_>vu-squN9@2S;2P^!Bp%Jml&t!!#C$TvK$UITs&zwNaT%zodl zZ^@p}38+>f)q0o2C+(J?EH3%g#L4rSFQ4!{AA1L=?mgz#+XLs$o%`-@BM*#j!sYaP zUVYVXeY~kjk}R38s;#q(swMMpQoHK@*rZOFAD^VUzcMtvIHV978{m@nH?tggfiL-< zDd=QNyTe-&Z*xXCCX_%K5CbE0(Hqwidw-s%>tzs!y-@F(NtV`WGfNcI9o|jPRQYV= z=v!w#L-?)JMxp9?inCAu-txd+%RTMm_F7*41k-Ss{S)=}*T`DP&_eu@1M(14Cy0O} z_zI@52A>5?9dMy4Qw|{TtiIjwH4e`={c42M4LBp&u7TKoZ}5{|*HF+3*4{(AwgkfiFJa(iVAM*UbT$EZm!uqhCmFm4F@IKta=V|VS2V! z=;vjHq{5QnIp%9WP4@7mXFpa9-xX9K%iiy7hxV7wB;jY?fW+5GV?|Z_oWDaA&bXya z&k?L1nEDQMEUh&MD_p?QTJoB_&c^$tde_1+?UQ{2*`QuDf0+V$(?)|H6a=4pAsRZ8 zkGVbomn%Wh;qmXzfov~B1Cnnvjg;p*-^_0)J?VQNsed%IreAAi?{(&u*PTw!Df_|i zzJ}Tsdq8c-N=IvaC(VmOuxM%b>~p2$dd=}P1E)K@g;sAyDze$$dUMMeOUpX&^Ukv- z@2Q)rdNUFjdqN$U3rms%ozUt{SUbtB`M9-V8B3KM-sOwCA1rJ0W)O0hqu8f<-CYnp z&s2#y-qLPqeibFJ-154m`8niNZ>pS|yl*Y0Gq}KY(7x97U@m!sG?>^Bl`uaAJeQn; z>bX9_^J*@PH9>7TLsqPh41JtN`e<|3{GC-xxtAL9UIpgWXXrzMCV#Fbx{xX1hhrIKW zJg=&rsc-l%0}KdQhxVLOEl*`wUEgAO{MFdmaXa!fI4Hymu7}R`!zM+sG`9kc1d;^j9X34GjftEd% zmT4H6S9+k!EUeR~z77PQAAtg530S+sa|-@6(_1CyzIx0=R{1UvF#A|kiqI#?_YQ~= zLZ+vv_ZmgZY|7AGvtUSz!) z7z0alCYsNrHQ#b)Bq6k#n=sD%z*F-5g}TQ`=3ANxWIvJXIY!~`pzir>{y;A_rk69M zqAX-#n}d+&vF#w=+rM)Jvsd^3Ou1F&U!i_HjD(~^%{Hqc7vohP>( zbUp6zVKDUggBZTElkgFRJEydqa9-J(6|kRxyK0Kaef6lRdl1FH?tKYxN6rfo9Z)#! z+Y9!rqpqWvQu)4TPSAj@PVzlL8H8N4`{0WY*&WR3Yci)7QHvFhLbBf4Or|rnBcl+S zkdT&B3LUn*2t$8`LN=fKq^7aO&bw%rV}t-58SHsd)pIC>qSTRrbqCwkC=eihW@4>F zu+Jj#q+U{%<|vwsC(w15+`SX~FU&^ISI``^G?!`gK}eju!rXAIms$7bV7x}Y<{g|f zw7l+$Y&lcb=Dd;owG9FvJPy%Ww0;Qu{Eq{P>)~-6HtZ(5(89xVaOlV|6X+Fb8-)G` za6zUCrx-pj$k^NcZ=fFlO~XNkuMwc`?bxrMyg?zw?gYEtbJ1JwoV~Iyk~or+0MZ*A z>7RsjogV3PDA1qFk$%UKE(X%mjv$#=2xBZTdi_i2eh#iYU}?UQ?7QU*7Gg{D9OPRL zV%Nd5xt@0d^A!6*?`-pTQ(9j4yl-jQ2q|E;h;rs21ozHM_3XzsIM%z`3|cMmG=pYa zliyf5538uhPZkc;(+Kx4V<;L4I9GuhIAC$R3>M86H`ssiS8D5g>?&yb^{H6MTJA+2 z&67M|p+=#aG*8VC_z3toIoER?yqJs?&uMmeRw)io689-BYk)}0TDxvpL!*4lbGFRZ zGDBIWN6E50M>7(OT!YQG^eZTMHlhGa^S>~TLRqx&)%lD#^v3@#s>we2Dve$_7kRB1 zmh0KV?8%Aq=Rplv(=x1BLz$BXVd4$qeYsnj*8r9C3R&ZpqXJ9j%5G@%2^J)M$Twr# z0ikFg5k7{y14{JS0rEcuvC_y31pE_O78Vomk3tsO!}9iOa9Ny}>OX*irPD3ldYHBl z1{w^CEX`k$PjA7V7nb@N@?@#+Tbf@-HpEiNf?Ixqz7f4=dOpwed_`th$zwg$v(M68 zLbSZ83NwKWR#^m>hKCkUmdQJ+rj9gaR0ZQ`jqb=2?3KgLD>`sI4xW zZx)7(g(XYSLl&0I^SXe#Rtj|HA=JSRdaaxm_yCUBe!@4yCkEJg8Mtb;>?l4myLHHR zX3JND-c24~ueWW22#|ZNW}Hije%eoPe*l?U7Gvl|v^+3K;B1y9`z&kipra$~ zuD-@l(VBJx*_t=X;cdjIB3!{#LomHwei>mH#yHoz2mx=-kX&yAS&*gqS7ce9x&%~k zlR^zls!lVhWMbc+Nii?*9mbhV%5ql=8+vY5Ytp<82zFDrS*<1!U{)mQm&Y`t8m@P* zz5ekF5X7j^i5&hXzujPMIbmr&1wALfZMnmTG&8vpdb{&|WU(~E4U5eIJ0gRh0bgGe z*pT_P2=lWvug7qa9Dz>16ItNd4|;sGcOm!Q%YP+TW%CA*haCO}AoKA+iOhsz>F}aY z;G+98IsGs}I_woe`YRyKD4YRvy}3Stir)2*Y!#9E9{fSUWoIGuo98+HGYjvV7*vcn{J2)3-3ps{DS)t8b8{iBKw;4Wlcz zceC~y$addR))pL8-H>#Dsvi$`7uh%_)eyFR#qjkD2TR>gkUa%_-ya336Ylj6$>~LW z^hCZwLDUNgh>z&D5sQ8x3>IMD%i!vt1mWDP9g?N_2ISD9k+-;kT8!6P5c=JOK9$fx z+|s;}Ou`$H%h7*9=wIm33kkiL%&AxYv_-j|K=8l==j9k<2TTPUlXHBaRnKWl^EYgu zY31@U=SV|q!RG`4M8FnHVxai z89@*9hBejF{5H_i-0Nx7Q^ERQDCS{ja5;$rueC}>E9}f=FiH!&Pe(wo^GIUl@a!e) z5UxJgo0G3!jQ;dbW*t~1^+!_}7j7SfuAk?3VW?=GnFXENKk43w^MpmTy>Gq*0PlJS z#hx*IQ2sfGhH=^qXj%2*q|bNo2(j<+c?$Zh`0Le=u7a#((3<|2C|L!`wTjwH_&~-^)1(XXBU(i zVxOufEw1}s+Na=V0f@&j5q;O83rx49>$vWZFd}4E-*CJ7I*%FdrMMxmdm?Bu!^PPj z#s3h^e=pG5^zr&>?la)r_5cQdIx_aWD22Svxu7+HUY0@08*_Max zB;jpLLK=Sdw-g@7Ar_n;O}`Y0oPH{);SE`o+1$<(PudS{5gU-EEu`& z{c=`vV*L8eY%3`1B6RyfG!E0W8LA*lD8=w}ztB5hcA=Aj?%hHCYW+p8*a-*CAKVJU zkkLUujhm%;CpKDaWqk}8@1jN=7OWeb>CJIu(ei8qko83EFvmkrQb%U|FiK(8!_lK* zVY_w|U871jPS4*{~~CZ)yG=;snYXEiI?8>^nT?=swGJ zU36Z;vfDo+-@#7h(74CpnGv7nJ_htDnD4os!-T#K=p^w*5OZ8JBi_=y4!PXqla^)| zGI?0u8M|C>0m4vpJ=c;of=)QmgXKPA??gQ+0rr{N=MFYX`y8XIcRk$V7cCG#4d9XyDWs_Fi(NSF*d6!&obNnfAVyAU<_=cU#N}F%WVX~by@f5|} zNjgBAa>ip7iJ*0c4n-6GL8wqx^WR||JT71($_|(H!^my{4r$_sO54W4;DXgFA;dkU zG7%lrqaQ&MPTe_+O}7NBTg_^ zLNRAF#VmwkoKOr7UJsJC{|Ab>6qdpjvu?bm6eq}t(Z$Skj>z?{Lu~gU?9cxRL_>;^ z_eq~p{1tG0PR%I(w=`40Ovnhpo~Ayu33L1lR4TG>AnPysN%&Qwv+EN;+?=lP0hX3O zKzuhQgu&9h7f0OoPyE|~(RM1=je9u;*2`S)z1m3ggVC`tAW^XTrwKgM7}0yO*8)iBJ>>03+N|Td*A)72m_s$(q{7_II%TwVy;Wv=LWBI!lNOd zwF@m-NJi&y-bD@|9*4kBS$j`9PJbizeFm&_c7WTF-bnSTCmoKZlep>c4+r=EY(c>K z4_xn1tb91DqCmofrer^Cd@^zO-8-xk_l{hbcbBknLj;PM^FD3;|8@uhBM6$Udb;_9 zQ@tw^EO&hm64ab~qi&RN--+%xVY`09^)gkl&GidJ3pW-(Y6Bc(6j#m*zNFq4 zpCaH%zKdDzMK;@Vv7WJ`s5G&@`FcJiBNgN0HIbi=!0ir*>fWoU$=xn&9lrD(1Xl0n z2Da3Mp29A!@$Etld-#qo#-H{%f1zhTa9f-`v%>Z(S7HP*y*17(gkvoOQGnDN(iGgl z9-RWIgLE$>iyn-1E^rW@Y2z=68RT&!|I>BEn}u6cu6f=(+AKKSYct_x+JN9)DyfD~ zNW=4?B=%5D3#}#mxtM%tp(D!)jr z(7bG&X~!)T-*#Yh{~o2AfG_B^0p`Df?1G^t;%Fq|&sAWr&G^$u0> z_yfMZ>v7DMF8dC?PZ~Y!djlzZ2!pl9nS{L^1EnxWMi;Gb7h{L7Mv~qw_l2ALG6w z#%2hS*UlF-yTsXICg0l9$vNHSTy^LrIAh$iNI;vn@U)4|Hv&?2zbfW=MjR*i6t?tq zFcYUH?^&}Qbok9k^t?%;ZMoG;53PDLE{-GOEWnG7%n1eOY;fjGd+8_ zGxz+M=6T0AoU3Hxn}i0E|6l{0NNI=saQ6f>{bMQ2^K#>J`EERfb^dG;}xU88iT zc)@sg7xhVCI6q^($_357sL(szSi~a~Cw-q__{b$;i-DWmS(v~$Mn&v}yIa(2C3gbb z;AAMiuhB`h^#x{73(yMAj&@Kh!I1dih~O}w?6#$6pT7WLZl(*63(A+a5Rb`_e%?=; zk0T0p;AUz5J-QEVyaEm|ke0s+ZM+M57@W&`2g;S8>j}-zyq2R5?-XYQxz;P77se88 z4zaG!0f9aZ(C>wDxlkj+TmJ`y575GwzW3i_{Xtk7*Mc2TmgIau^l2bSbpIkK|86s6 zVdkt_b^$|#3yTOfL$J~pAP#GYGtg?yUt`M=XdJ&DH=d~<-SijyCl zsb^VzpHd|av!@CcKzCMD!#=V;)w4ozj`SrSWTU&-R}4fp;;xZ8Zf(9di9qY>#lCb6 z!FgDyt`iU)E>n5;9?!n+mwV?o+>4@LpBKDL3J%WkKf#<^+8gcYH5OL|4mgHpw@TBS z`Ddnrw zpvl8+(ADr3E$z>I-*^Ca{mhR(FJy(S45e6tJl#{dcv{Tnn+kCE@5x=Q^D#(}Q@_igV^)(?Qj!KH}LbOZ66Hf^(pYY=wCYSva--D>|(_#G!WajK#R(~G0 zixm1i8MeO-T}|6X3gzV{R%Z1yrTn$M^{iajThH1TQsk{`pU=v@?NfW}S$lfkicRE~ zcY*u@&rfWA&GRkB(6`{pg+eG{uPUAv^pqzcs{m2L15~YzHasBlXVMks3bNbDT(~nu z>3%A~{a^borEILl^NDnK;sddsu#goCKGymR)YkPj-+Is^fB9!Hq49Z`1Zj6A)1c-i zf9e`s*4CZK?F=z0T%_uq;KTtaA8S0sB_i3OJs7m)YA`!ej5j=i}GKu}5Q@NwtJi7E(SCQhq$7yeXty8&aMVQcesh z4+<$)^eKB%RL^=DzDdXz;%`)MlyTW<6%gz{54RdnbFJ#hMq*P0L1xbyL!!B*>bP;JiE&Er*`D9?Hzk)sW1Hy{i*I8 ztAQp*r_n|JfVo=|7A&f$LqNK(!s88E8u?QRP!ouI4uFv>1sZt-ahLss z+hdLoVQyS?)9j@}oX|NY8)nsv{!uQ6Ve7x#2ANI{v0>trs~vFG}BueOog z4UyD=mA7ir|N4NCzTSpI+jJuBosCcxHwUnRl3lelf9RQ098$f&4D>Rz;C+3F`nm)$ zg^n51&&pj)L_81RtJ;t5K92QL)%uU--K`=0XHQCVIqCj&oS}QOHlLV2a3XC(D{=HfH&A{gvEkVeR;EA$q-A8MqNBnDQ5u7i zZin~)7^oinc&x*>1~qv0(?;WJxpM<(F((E8ct!U-vP17RYDYFA9-QXyGx+jeBj2nf z@2j6qtzJ<8_xV7M+T)i?E8oqeV}(royyhS=!Knkl+1BpoG{24WMNAa35a#1ulR7dO zb!eVF5-Q?gM6RIZYh{vVS=lLrh#&66Nevxradc>In+49g-8T}lG4*#p1MN<^fYdTA z1$97~g=9C~D)(IjEM5GmX;)_2bj7jt%VqZ##4*o_&>Ta{*4-TV_1@+0@4;$rhiYq0 z5;onwPa&}PCZL}5y$Vi2ENwj5F&S(MJN+|wm=-QDhHYjD(Z3!W7Kpx>8N{^a7$*87 zNOlj!hQ!yjmk6@QKAaa(dzi#Q=Ro`GtZ_PT{3Gk|v>e{Jgc^^a#u;c#<0;yQ<^N|+ zVhbl><|IZEi7`Y%`dR*Uyzw=>amQdL{81#kCsE_Qd;0JnO7hchr*z#0+KVp2R zwQveo5c}yw;dW4H)8xm!v@=fJBwMF--^3fQ;f+<^_zPlxEj7+$>~S9t&sTh?w&o=9 zXDsMQN}Nu|c^Abq%Y{y1pB=snX#DUaI|m-Wb0nX3#dl|vwc-59Ie>P~*hHrV-lLpv zS3B;bCI8EbjdeF|wjACGs{5SWfV<*HonK%=3S{88KE(s&m{d4iJmebMZEqtd*ogK7 zb|iN>M>stHbg#96=@l#g$*A9m+U`S|{ZQS?XqDEAG}a7{@g=bgevRRq^eSm^ZJ}=; zx~sOn)JD_udCI$ABb(HAVQB?^6B@xDLiS&SNpB(huXFe%QP96(|Mf*kSsmE7eq2a- zP)PYyNc(p}%6|+gZwe{O6!y=SG`kNR>oTLgiW4s^= zyC(Lza;rh4qFyO;RTtCi`RWzC=d*Z4qEb#TSra0a3f@reSUy+*-caJ zRYIv5cRi=i{Fo82#!6?!MBa7Sdr4`c0>dvZsw*vXRq6y3CNK}3 z8CzUah2d>1SW&vFpu|->Rv|g6Yx-8ui&n=5CsHY<(kcZuv0~%^g$DkAM2{E{Gthy} zSJgr)rLMFdB8v%HQC(b9SBDo#3abBex|1uaOG+Dwu$necg(gr6N3_ql=q&`cp}?QG zmTa?O`=-$Y)ZfILnC^Eljdlq$6~m^jL0p>w^7^7ou3 zj8hnaL?xFyGNq_)c|CJV(C7aIAL?XIj@oEbL0N!VI`;h5W63y#S+E&FwlQ)*bZ(il zs>Y?PsIF7geg&Hr^hVD*_*;0}8qLB6MBf19`tllAWeJR`va&C$-gnxLRch+` z)`=G{jwNdB^}AqKs=$m(ORE*uaFQ}0I^E^e8*HMoyr!l^sdN>u7&(&J#Q8nL`(l4a zmHpv2a6g_H>Lo^(u+EPyUIvZTD{~e&9BfGRrb259>;nqbQ}6pu+JtdLI%wYHNUE*wx4W z^_)4~@P70EW51*yVa+A|ujKlF!Jq&CsJEZ<=lY=x`(96aMe&ujbv4CT)|Xb6U0GFB zTQ7~SojAHSdUU=1zaFWyu~foKWa}p5E!m~Y#0+I(sWKWXY2)b1MkyubYTJN5)mVx2 z2&3y0M%PbZ$m4)GL7H@BWzF&{nY(1~mgSLH=_Ej<(e)xW(Ht2!jN)Ylh?%fR;62RM zntw*rvb?AgQ$U+?Y=suiJ6MSqK8vNByI0`XyNZiT>+7ZErPX++bS3w{65Ot<3eiZt zD6^NNYH=V|%}R+w@HS>z^mGvZrf=dA?^& zWUiHmCaLJ3(p$Xwn|B3HXgzx??|*1dum9Dq(DwQ|!U?SpzEfBb2(4!? zBG!n9X)oSv9K;B1FBTxlaGB|vOg!wn9GF|4f}W7?`AZa(0(j7?Y_OK6WC(<~a{+vRZ z^gPBZ0(_7zM!E%QBhntEJCG*q4g?M$ZA9ur>O*S9jWzWJv_rc1Wza{u@s&WJ3uz;E z)k+k~NRyE6K$?$~&b``^ZowI?6*uH|;2uXl(sJB2q1((VZkhHV-9op4qtPDs;I|-c z{1AMRCLBUL81ELO2}t8U4g|`PmVbiwNDm-AgtP}~4^s70=m(qW9;Azr#(jqVAe9fJ z9%(`s^fUOMqW&LaHL&g0vjzPNW-=9zePSX&04!poe3_1AfSbwDBw8Bi)D}qWX|- zIS%?bv^|70AL-%~ppSIdzd#RZ0)9|)3TZh~E8ZBm1E~$E4{1Kqgj3KD(sHD2RF8Bg z(p_IeuSn?{!V}4G+g!=eXf<3mC`xX_xn(@y^klH@NzlNYkz%abF>$jj0~_QfDdo~@ z#!rbKMNO%FCO+j)2Lgg5ff;-W(%LX^@-B^G;F|z%0vufmUPOO8J{!ZpUj)1@4E#O7 zw}gQo2Yg!?I6d~eBMh8=Yp@gW(0)w?d>7#3K~K9#U-WozC*XDhro~t{7&Bwy+@{PJ zr8p)oBgUE*BQw3wI|O{d!>cbOX+?~64&bEgO@LdU2?Qn(JTu1XHkP4=Uk)UHM7@Ig zAxNe480+1}^q9E2OzAO7t2sR;VN*m}jBP_?T1=8VDkrAU`1b)ZNtrRWw3q~7023|J zW8@r6TwJ8rv!HVd^e$x({zY;O!2xCu;8zi-Pd_9n5umB4k9#%{SVZ-px{3O^!8AKY zahq2Sh>1fJFv?(zh+YZsRp2vyq0()xiLn{02gD>~#3<+~AK$Fr@%L(OkP|-2V-@3ixmhh_n)) zK|myY_P2&W?JnZiO7^wMoEej_f$Ym2nHiI6x(S-FWyB;vl%Qc{4x9@hKUXAgs;#dvNDUv={R$D>%O}n}YKz-&AM_D^TV@<40lbgqa@6=>(r0IP7PC zdkS)f9oMap5}qSCuGxHC$N310VDwCMdyX@i9{@f|%k< z0{o|ONHyLIda#5Lzal0U(VGu?KF~Wth8K=ruO1E z4C+rj#bEDZO${>-(cT?_{uu8I8Lup6`F)0vR5T3qoJ0?gt<5Dl;m2-{k@I4#`K%{Y ze=q8NsPFZc<(fNW?Vm+`59-sXJz6e-SC2tFK^OJ+J=7;42K*BN(BD@0OZW{~(*|?4 z@Eh>JecT3{1VE?$4iPcrP|;tZFU*5Y* z^ODi))&H#_jk5+8hd|&q*gSm}^yI$>0`%)a9la1gWxOGzxjN9eGz64BuqXtgtq){R z!w@sKgAb-#@S2L;RKRxuzJ#D`ok3{iHepeM0^(qxSlF@#<^}UKb%|aj+I5AY*9`cf z{{_7l(N1~~>leu(F5>eZ;60$HhtoJ52mBP^qlMnW#}AnlKfuW^4C}K=>HoAP|}}aAgQY zMd;JV({TIP1p0;eIlwNWFD}yOcEIHiu&!x9qy&Eva0zg|eX{Wfya#kb;Db{(J2931zi7k5D2~o@O;4a@h8FW z1$;5!p?390fgjr6*985cA##2y@Y(YoqK@eQThPBE1b-0rnzi5?+TVD@F0DM~* z`R4<^18{nYx^@xYO2Bu9!EXk97vSXkX?0c<|A62;E?$5|Z`MFq7{X_Q zGJpRX@Mw=eUCirNTHl1fhoP}J(PW_2O&=(dHx=tY_RNhq6L^dO%&uoq+ysB7_xCj+ zo-$)#MF@oaoC750gWe&~W51*9mtK8H>zr`znbF%0dRsn%Jp}zd)-w^8Vm%Y<(Yo{L z%?|+~R%GK3dW&)PF_roYeb+$W1RsPweiPu01V`{d_LqrIJm5aS+23SEe>NErm2IFn z+-;^1{KxOgs&THe?ps?;Ql0gU~fKW;7!`Lg{}L4 zM0*?Br&4>!jnxM03>A1>@fPayQ9qLas3&95*B?iH?a#Cygbug;O#4LC??ioypuZ@D zz6150sHeUXf2cpmpY&LX`ctT9e`6MUyqo;3K5k`kp;&+Nja}ikuQX22g3hjg^!1a( z^1s)AHSk{z{8t12)xdu>@Lvu5-_=0s7{OmlL&Rz72Tma#UE1G+)c*b>hxYz0KJD*6 zYJcxhtH;qMySDX1-z@6e`>EIV7TVu`6zIVuQslb&X^*29evQ>KeN$}@r}R@#TY9=u z`>Ds4m0gAX)QfOXYU-yRTOWQ+;EDEkBAdqZGPV-z+SX5d=`MxUYvV+JOO!5+ZqGC(ofRt&JF zr-OokrWb85sM(3srm*&OJ+TPM91k>P>5L}bfCkScdsPQ`&;PST% zMc`*6tUsC_achp1-w+MH7iCRvLX7|GiF}t5R)*2#51i*%t#%S5_Tq#Hzfzes;C(!Yvyzeqn2=@%kB zBhtut-u;V3I!2_^MVc+rg(58z=}M7q5b6CQ{k=&4D$@NT{XnE&i1ds|BS#DVA{`^r z=_1V*=|Yi~iFBn%H;7dG`>opFYt{ZftM>O;b22inRuXdNEEuOuos=|bnu5P0Kg~9M z@)RXuZfOagcXx95#Hr)hy*DEhi_Zehx7P|@p$3x(fKeJXOPk%NW^;^<+N23-!0}fs z*YKMba0Gg@S-6YicU5cn?F%{9te?d%#kQZzZ(YpseVP*qVRAVnH#3-Q@WJsJzQBRP zD?kp{aU;EK#Q0tkzD;t}SfHB>ay^=xj8Sxx-(-$TWWY!l14itFM>CrvDyp4m5V02` z8E++oO%+5a7pzRJlm#`qfndBFuGVxftGomXG~Jg6XoPHhhH(Bu@JN|tFc~80hMRF3 zzRb1*fJE7mJrr>!7lS7kLH>yEmH{lu&qN@9b|+=!53t#e_=)mX`5}0{h;x*WlZ|K; z5um&hO%EqTNCqk-L{EYc5e8PUMR!oa$O=i(A4B#C6Dy=fS5f;2R#2nAL?0p|Ss_1~ zj(H-YSYdJWEI9UvXv1b;7e?=;HUkY0ASsWog7P8;8;hJBMyEq(5f>V2 zQPUXRMQw%}vQcP?F0`T$XPA$|9ns$r(@PA$27){KL*g^s@Fx^DM$@m|BgPn31FbDu zCdFK7coc;#(H1J$SYccA#Z<7f!j9PJEP;NFvTztZFWV|BQ_D!m~8FQ2T0JX z49|eiYtjH3mm82BxB(fHG-wei{-G%P0~v7`WW;=Kwn2;Ftg3Spq07oclb7LhDsu5w^Ftqf=10C zvyExPASOkn#6l{|cLYvj**O45rB?uhlOKh8M*b?vNKK-$PJ&0&418ImX3s^fd;tOS zC@abI)5EAa3=DB50%kk{c+_>2jk+49nGm()HEQ(%#wP0KWyDB+4l0N$$V1jFuO@1R z1efIq;72VZ_#pY0_>L+gxK;iI!OJN>M4pFf6jiYpB;zD&Le!1_Le6rS#HcT(EbE(- zL0R8iy?r}}de*nGlChimwvr^Xnxh&Mz<{^BjpmVswOmaMB_sVdE^19QFfdt1(Il25 zUWRR5V2+;91Zb=q1u6Cff)hh*6p)Rv+wpCRU4m~Z_86v>A@(Ko-WawYbh`9k4RFDi&~?OhU;uamXjh zf5f+3dpW*Sp|i(<8!7*TeMdt6NSV|TDNn_>(M70r)U6B5@=7O4v2VfDq}W@b-}SMD zgC%KQ?6c_8+SocEt%-dJvaF6h35&QbHU;@ru}hF|jC~H64N}}=NX&AI;ZvgYM-x$6 z&Fo_^^2XQ)LBkZg6tb9OuK_$F_B3oIGWK0?i;9f_T#o%7IMK1IAjN>#oAEs`_D!@M z6x)KH4~{K`?qZ~;1`m<28aA}A}#CqZIBBm>LuQa*|lL%xS3GTsHA19xc)-qZ4hJ}Qe!t580J!TXq;gI-3phVB}xlv z^dp7CQL&e zS))ykqAt}FlO$&01}9NYWfpF55#>x~;f9rzSLI!#p$5u3Fq@04F9TU$r@M)_`7mSH{R*T~0>$T!UZ z%_eyy;ot5+{uD-IJkm&cEBX;BllzR6X`BYopc4x7rh@^^2EaI$M80D%R6FbuP%#@? zRstv1fQZ2m`+ILno*M62!NsWZ34^r#QyFh$Xn$nG2snu-y z@i0w=rCh!U)B&LU2)Yk%*u*N)^lE?x6P4i?kS^b10fFIH)aW)cthWp#-sP-!Zy89+ z%ikeIzr|t-c5hZR8o`*nV<5>!G!6#NyNnK)d<5ML{}qkl5KjL@kp2)I{UJK~Lv-|c zBToNfMqe79OkI56Nb|!q6e{LjeBa2r$hd!Kq+VcZl3vD{Omu6KV~sPBj3aBEb&_Gc zi7evsVQ_88lg6asgUPZlFq0{nO3`X$H5GZ?SgLawS>0nniY~_Ya(d<*TFlT3hTBMJBM zlSJ(A)a55ghA}om;Mrva{R0U)W-0aPj}nG_%p4;2B$?uvM=ASfitfh#mB?I7SZ_{b zRU{M3N(mhPelMJ5F(oMqB&pbxT+hoS4=V?FnRt%d07U%#-AMT*WX4Y?y_=btX&WEy z$pXXHTY*1;*|nM3ongTQX4z(Dd4~BDm~ESx?HR700D2lN!`wt>-;vz@sAl|c$Pc72 zR@=r91e22|l40woK~^b!Sf%t~mC}b5r=_tTfg!+ZZe5JXRZntlIjp zYU{(Qtq&_sOJj8pWA$go>I<4W4>MM&=dn5+Wc6?#RuA`K^>80noR-Gw5yt8uWAzrf z=qDMg!t+?&j0wmU_+%eePxfK;WFJ>JoR-Gw6~-zKs*}c#B=>fdvDzkB_0FrQ@YK9lNBgik+K1KAKCCz`jnx;76*)s` zd^Gv}6U^_o3s&Q)ySNNn|3s|jMKTOp1q}-(*3+7C!bH0Pid84E@QSy;7v(D}B*RI* zir9vcBRa(j?nDo1?`u}*VvHN8b_!b(w3`KX&KHbF0(%m3Q&Wt@_*y80yPs)BTDqug zk4oV9N3$rFmZZss1;%tEEh@+Kq&(gFOEn0HJFrr&CmrxXfVhQhT#zG#!RSTK~v z|Bh6j$fVJ3n=pqiYEv=PW`;i13~6J^dj zf{hkoPum*>`jJN3mzy^4TtCf2grit{)8-e~+xP26^HX%5w6#*29KTW;H6>oMo5yjz zO@bo-&8v+oVFVTdp9Od_RkY{8Q+YA)19t?E+1^Z(`W`6RB>b%rI!ylt-fc0d+o_MO2Sa zc}#Ep2`YcuTb~TQUG=db`y50;-%O_s*TblzPaEbB!W|2GQg8g@q~CutTj&zl?1v&m zX{#I0{!PRWRyB+|ASbdQ@eB)5cc5(lK{WdSOctX7x@j6<9V^)*ghx1ziW**Fut`%c z(1C#qkd(9)(i`qu2BwOrWxqyYLae`7l9E}tb|1~9&%pC)7Sr9g6ZJ!(f~#3v$C|H1 z@tU$)$?yP$IParmpDrjyGuo#YLJ}%kSH{xFmt;o+>L@XzJ;O_h0Po7CLwyt zF2mOQa6gRCQ-#gbPlyxaJc@BKqnrxU$ zZgl$R0>7Ii{X!H-(kUlIfeb}h#I-2{Ae-=Ow*&>HSBa(Mqv|SrQkLNBe#+39Ed9hW z#fdNaZDVQ^GP{vUqu2kW+XNxaYqORnXe5Igm@}!padUYlQ8vsw4?T^jr4pqXXfq{6 zP@~yS;;JOxwGX3OyUpEeyqDmPU&>BYdC)TDMP$xV<^VE zZr~^CE;ag*aH-K$#F4Dgjg%q6>ySwgZOF(?d0dbSt|Hrn-zHx$K!R71rtA|q)C+Vb z+Eg3#?bp&St5ejoUsa)jVe2H^GfjCPRnLNR_P5BGHn*?HrhCT7)MO9jnab>od8R6R zB+pc5vs)vZ+i%UjibLwN(|N{~oyRi`*^60b=ZfrNmf5u;wHle@;FY=xnX3>7rZyvU zFEZJ8BQwDCL?w-YBt4w-6{L}@nTL@KFe&#a1eG4n+BT78$R)7raws_MVYE$q0$)@9 zB&sjvIxuhEvLX^HV$(D0l&Dlh+iR)qbJTVZz9joE6M01v8sCCCro^R{92nwc4hUTG z9d~nFObsy_KaH}Y8r?g(<4&m_Da_F|Cip9O?gvCn{`t&5k3TPUg*M`q4dq0caVy2psq;j?7^4>pBZk$EQp_v zXv**zEnA~+kqn=(%L(=*Dkt@#M-#A!tv zf8!uv^cd-UrAr8Gqu3XhVe4BsI^v5$4|C1mfbv9k82i1E;)DsJF1>zzFTo?hf6dom+Qk z=VqI#%fGOof!jn!(~?pZgTiY@lNxOO8Z)xXAAmzO>|+MQkNoyA;~G&#@D3v#LQ9GZ zreY{GnQjZVF4XZZ;=CEZBF=C28;Iu&51DA*SVb?{uVYa*Y#lvHv-F1;nK208xg|fu zL~lVkkqPi?6AADol^>MRl$VDif=*j}$22f9xP7L*vCHGQLi1-&)#4PrsS)B&`tAsS3?5-hAJ)5Y+nhpS1WHdF2t_`1jn ztsLY;x&+r+01W#~R%korC%|t2wUn=s*-V*Z$QaR~l#h@}LdG!^bhWna)V9S`JW`V8 z*Z^l=DKn5`jpLD_HqazY!mh~ugz*vf?f^QpUo9Vb^Db``y?mJj-FN5?`u}t#PDJrqee;#X_044Cv{v0!H_KllU z`Ck7zR&CtunD-P-o3G5RQ+bExJz_-pnAsu9xpbqVTnLRUy9`Ur|AJ0aZUlf94(#6q z1_iu>V0JwdX`F z9dHaB&1V9&8X|N_2OL*0_rfcIuvupbOqJ|3gXGMWj z-o&{@o3<_2bb3NmQ-2=d)xey7PJrng#hx^+x6ki#oQu{-{1n3;ujS~-XL^}HW$k_= z3dH(HjrD<2jr9&uO=lNF#xOnbvx@_wg0@a+ydv6EC`5)9Y5RmxWV1L5-Dm%BmoSoUG^00y3@=rBAVAk#da`=1} zS*9~$((p|bTU=nuMpsOa1u0*k3kqU+P{88TSVnn1^9e&(qY;r2AaIe1?v+c!N5PTd zNh@+RhHrz^p3wy;&*}n{XIX&4sr{8T;=#&uJXjfiEs@^BDoj@;XuaN|yC1nlN8v$E zf!&XMh~=g0qal^yEb~saLfwzRBhh8p`kO?!Ms@^#mO0b!;CpzOe%4H3`a&$HJWM~! zoZ}}{W=cJ4CZ&FYH7k*+o zh#*Hy+!x{NQA!<;%n@X!4-+Eq8Rso*mp9+|1gOr%T#GWnDlhRi<7;Dl51A(KZ9QynOgBV_?yzUW#Lk(_7R zJ09p1&@BK$9syZE=S3;~8G#y2{lmrZUJX~n1G?P;zi&WizfE+4T<9lYN0E)JN{j9h z@NWf-?R40d{4GII zD9{=h{UvFgM*R%@oALs_wi0+8Uk~A5G0~>}E1);=Z|Xbv>W{GVsGx~faO$PO~cHTFwdMIDmd~)9O`oD%h(0#G&3{G zHQSqzQm2`jQKp%h7uz&5Gs@k{4_THi*sLhGe3Ri9I?c?Hb{-Au(}B5BrkMd44xMNU zM1s3f)`_K15)1k1#BxRS++|uiNh}MEs8cEZqUceWUlI!$NMeb=9>r{x#4;WwNi2H- zjN4`?nZ&}~%5FoimGo9bAHlhZ>{gx!zU)>ykpp)t6$H3jc>#dzR&3w1?7L5OMc=sH zige~~g?;pH<&uk| z5|TB$l{^fh>{iwi;BMsvfN@F23bLM>7AinOdiJN*BqX&ms@R!{@GSyNgsI?K+)BZi z2uGkKo?HT8+)zW$Btp`&VC6B(sOwk8k> ze4FPvJgsm>H(S};_`YqY?_ULamA^*Had(L4&NN8oTT;cq5t@Y3-R^Sc(}ligyC+(ATtUEH$^W3T?xZdkd!cd2tYAgB@EM% zk|YeP0F3)i$(JFxpwY+I|=n`hLQ*p|$&OLTmTmgjU{f=#{se^PSUc_tL*|-SJZNUa0edkw+fB zhO&F;Nw}g;3ok`^gmM%j>M8LhnG*kjvR-+=B*FN5SWHiWr=rJkynM1 zMBY~cjQgJHY$A`@d4s9iO?vM~|3rH9kaNHn4@sE{dTf8_>3CDuCSp?j+C*;LiZ2$9I`}2U>bh1Ea43$Oj;+Un<7W2}=5Q zKuSFCb4xOj}I z{RjdCbCAABzXK6kWZJnp>j9?;a5kZJxoPF@_fU5P zw~hoinUU}Z7*~DEh))M;QR!1irFfG(|axeZb?@G828($?CNhx88|mil^Q2E1@uNHbDogE zxfl2nI4=SyW~&6wyQIhcVj4zs+#{xQC~ztvC_e}FAIBY-+?|=9fio6;BycJTFmO%* zm>%Cn0_XQ=k-+IvXaeVip}c~Uz8&F4SOQ`;5QHaJ(Ib%AMR@LbiV z)lx7^ysCW$6RX0z1u|j6xU$CMaqMT^AHw*icM*bvh(RCt{!(gWa0P6n+M{J>2A zmV*fm=BIHp`AlxNMXB$jpw|s8p%cVS`mC|?*>qs-6Qg!P9^*$ISc<>yq2ErmgS1Zu zDP;l5H#5QL9!FS~ZSl3q4!o&bi5;|SWzd$PtW(*3;c%K|4C3{Y_{vH#ET=H`?iXa( zzQ(c)(-&o!$ZdpdK5t0o#-n=~%Cex*<)5*9tBd}++;<_$FLmG7E22G62J+~5rW<8Cm-j2rMb8ncn{`5CVN z;O7v@6G{JZw9y25dwr9^dXCQbWbzhbaIv0W5eB$ezk@YgtbYVRFV_Fvbmjx_-KUa& z!p_`gFI)tG+w3n8;9~t10L4U;#d^QRJV7y??P7grF3BE&pV*eEB>Qyo08Hjn9!Gr{ zx=lF-U_XGo-o}_*qjv=g6WvajVn3!Deps*f+ScqFNbd*94W!3lc!u;C3~vA^W~&51 za-|7?;Q+=hG;DML$i>&Ars^o^eK&dQ6p-P?*V}LYBDW4CzcevyZm($A%$)I)>v6E~LcH_me#Be3k(1+;a&uA)45E9!jH=jXm1f zxs014+X5{4*dvWSsL&T+32;+%62Q2rVZ&<6wwkIH6zu!Sr=|d(mVJ*MY1tu{Lmsw@ zWiLlbQp>FX#?^3u@X?md`>AnZ%K0`3aXj@sB)c%$|7u!!Mi`wM-ztv=1S#%h`unX; z)ePffG8K)N224wsUYxvx#-^M~s&$uC)pmbKYcHJ7ebf zNkauJPAB zbQZ3w&A&&4IS*EPzS@(*W8Bp-Js(|@QVjevEUjw6)t-GRT!=TU;C$kOkS_ll%Q8P* zD}s}n9C55Lj9byk{YuIvaFrvDi>d+ec8U!I7|q`Wkk??)nI=nsOI?~!|3s*MT~I%6 zKOCGNNvq>NGFWo$*b6m{%M2HWhYNERCN0x>O*8_F@Y7YV+e^fmlETC~~biP?y$&%U>$>@L#u7Qbm2tU%hJ z09fJV^)rne?=7aDcFHyNyf@&W)CWI0$!j)^ym0+(-3f7jrDo&g?KI82aQ!3QtS?-* zng)93?+i1^fw*3u_dtkfs`kEAwc-j(^xGk#gLJbd`omDaX|~_Tw%_Qpp=KzZs&Z|U z{1*f+WWsW5WCU`;hKo37~iFE zF92TU4}L`4{aQ9IuOP`Q+?9ZNb47PjT;VL|qZLy7D^|~BHNUV_p0w4l zO}-AG1qhr3(gf?rSlXqI7cAaqAr!ikp0oTpD3^Lpj=XsLLU+!?LdB^bkc@NT&X2J+ z4GGIx`8ZTco5p@}_X4jPcb6~GZFwe0rR$$1bJy4X4l-CdAH0*Fu2WY6T+QFgclxb+Hyb%P&dPG2q`t$(@3T_A zen9AqkCW_?PO|q?)THszC3uhlz@8+)9~@{(%kO}=sp-O;fu)((EP|CgbU9G@EyaeQZaXDF5WC#mHU z&(t1CMeX0D_C>!o_7ZR@0hf0=mL(XjMU_)Mi|jLa%n=f*dLag|Lr<=|LbYp7Q-N-w z8fR92n8mpfZeX4e)wjETLR9>tuDW!Cj2-a)W14}j87eM!&^?{e!T6>QfS)xPXFw>Qj;{1^v{gq=*#s zQ=gKQ6!cS{lA==3Pkl;CmV$ojQ&Nf)^i!XbQr#MesGs_j)X}{X1^v{gr0`RplEP1Y zN(w*qDJlHar=;*xpOX5x8$nG!^(m>pdo>FBsZUAar#>a+xHm#M{nV$VTq)?MJ|*Qz zK|l2gw{;OsKlLdo{M4tU@Kc|XE_JU1A3XJmgE11I%w6WGPf6yfPf2%EX?f~X(me$A zQ=gLVrTY4*Pf6cciE2K1Wen=@hz*p4Ce;v{tR(}fp&uEIymumEQ2z8O)qCUESpngVEbm?pbOg^J2x_l)X^)s5Hp=UHj zSFm0`qba(w7_fduQ*;gM^)s5H@}wU9jHYPq>mc9TNgozn!gr6!Gn%4JZ=zD3v=nWY zT0Uth+ENP=TG8D=6-CW6nxdO1S$a}*%L63Br#nTr(d*?IP0{USuAk8qz3Pj==#m@V zdm&P-)@7eGdjoo-*R_;6$$Jk$9KB9ZyxN|C_Y>~oZNid1dOhLZUJBta&>u3r>z4s}}>RsF&kJ_$q~H>k!mH zy<4s)5mE29Kt1_!qZ4_m7cg(X0vO5Om+{>-Qi+=60hBPZ31pHp`K!`o?=gILqt2%R zWUx7drWMHPo|HVeGqPEaJJH$caSBTA>M;lE?d)+2Xzb{bg_^5+ybE}HkAtY+)}t%F zxAx%AJGVHIpOEKBQr6EGN=YhWZbTk+QlbR)^Mz89rCOdZl#)Uj=;i!=%_~C&b_Y>|-XKv5+{kkv;SMI4as%Xf+#SNl--rkccvF0wJOm+gq38sA9L-&J5 zrqEbI`TrAGnKx1d!T<6q6h#UtzJ=mI5o9T;UA`Ji&kJy1!Ox9-@hbul8|+pj3o2?$bM3CYrF$h%0Z#9#~;VyHdz2_+Lv@1mk_fZ33GgY|7N%i0tw zfGGE}&d^~{Vz+6S=F{4b<=92z(UemIUI#tii4Q ztQV}cC}n-e(0viytrof9k#(18_}8`#118V`8mZoCgK^v!>0yx8hHs?dzW@f>@Phv? zi(+^w&RuH_-%M)XKygG5I@6a%Ucf&G^NKa$>=mG83ox#H+}js`*+m=BG<+Rw5Z^v* zJ6ej-k~PhcIjB3=w(CJj8ymyR{raGxrcY-K(t$RuJAf%a&C1#k!tr~% zqf9fd^-+mjaqis)RbGDu?(U(@9ifthPOgN`Mo5|`;iDG?ooZ2*5XyCl`wf@ zw!r12yV~Wie8t#1(D4#hih)m|l+|m#9+EZ&79v-gmZ>DN!u_));;sh~Rtk|Ul(MdF zkI0}$j7aqLJ}^UI=RDXMXJ?hppyv&ahkZq!kK+s)YbXvwJ%5(LzB@3sF;mYhQElom z=2=!){$vois%bwK?E3QCfQvYm@6t_=n%?hfoiUVM)8N)Jlk|bj`dIQYON-_5?l(M=l(DEma*+kRA5qCSf_gf;5kq9e=$m=9h(H@aO|6)X9 zWbvK7{+MC7%A7$zHO(BZvwpKP$nxcIeQfGEEDeSUT>#Y5diP`xV90%Qvh0!RWCXTU_vw0`8j~Q09?xJ`NMvXueVSd z|1sMgjKXgW4>7SVI2cR!y!%obC@}}+w!k{96zkNYl=T=2fX~l#%>z1fEEA ziU4Kou?YaBzJUPscn!cM>~$#x9Y!IuHlsvAMac0KQiS6rLVi|IxcA-F=^#Pjb_4$s zR*G;xL@BG)ptcciP%l$^ImX~>^v2N9-uT&brZZ@eY21XyJ9Q(E76*+n^&Ggzq@FpH zX^^euru7E2{=)QX;y_9)T8SBRf6%R}=`~D>EvDN&Xvo@W3Vvh)1J%zP18dLVTuJ|6 z0+z(!T=qr}*x_r)oX!bD0V@ajxQ7}r1`{P}zs9I_EAl1KzQI7lPvkB;#%b)--f`$wZ-wkyM>tt|> zwnDN0G?;A%=M_`SxVjL%{kTFj3`xWFUVa{jy`0N-QTV4ROs1(16zA)twc<~Q6@S($ z{#(qj?#t|(Yx)L?59w+Zf0=#vLfCQNWuEvU@jGs_ivNhj*eZ(8F?3E-@iTt~e^GoV z&@W+~DE=}^S+xeUt>QCGEfwzt3EC*W!!(NGlP~TK7K3F5Y&8uKPd@IUqv)&W+`%&E zMz*6YnR>8nwj~`LZFi1A>AvJgWj;hCk)n&q@coWlTqt57F6%3G1T? ztovfMs#y;c*2jI;!-Vx)KI>t^dcaj+{lMp8Jszy{$+}ippD=yog^bYKJ(L5-)U0cT zbtOnoU4=WWYTd763?D;L8{rC(PdGDtBP%_P0^i3ynHqID01+7UOz? z(|Rf2!?H|a^|J8jxyvp*8f4)y4`n@x-Df7Tue*KB(tR4k#1`O&dfdL@a>*$!b2~cc z#3P9Nd>1WQBMcquK5`G9WX9vBr3)q&_g;6WCHQM}yM&cO#MuoZFSbWyP}J1Y2k$~} z`XG~BImUia57Wpg?GeAxnLf?bza({~y!UocwW;IE@?9U7gv`V?sKv4ZH5Y?NR$RAS zW9qqX>0|1fAh6ZVX2+|zZkaraVSG%67i z;sm#&JmmvJhKu~qj07lADd7+S3s;{$+l~SM95}4@$%lpiPkjCl3xDrw%l~2FKMmz6 z&O%2%|22QcL?QV!U|0jk2a+byZPYVApp*$t?bMvSE$>gsuaoCOl|A&139}EA} zKL3wp^Palb^8Z*i@Y7MAl56;L-SuCOe-rKhspxMz2K;m2oLc`+h5vT)6Z?NE{J-t< z|5W(DPySOsH-GB?3-Z^`gtHw3{tx;5BN0wyL$9&?BM}~6#8?*lM-tk zkAFYfUq2(xb`1FE^tSq^2>+k@{8NN~hkcfRitwL-@{|>yn?Ln`lygESp>8`~q2mX0 zLZ?2UKaf1L@NAnuG92RAEn z+O++~S8PCEYuGGNeHjFo$u29x$!_3vb~4L~aC7!7%6c*zDt1`5-%ef^MYsXGmE|`w zz)Y9_$g(W$hKXHBevIs*7e>BTuBFhs>7vMFQ8d4H%WDA1T*}g38tEp(bo++7|dR5>Uo&;tgh#h$r-fM zG-P5NUky48+HdN|q5cEY;i}ib;b5pC71Wq=IJHf&h9(P>hv4s>>FG`-= z3e|`Bn5ewJX$&4Z{}hHDwY~|@6s(dS9lik6IlQXx-N&mUb9hzXyPx3P2N=k`NjFuU zK9DIi3)aIYQh%;iqI4fb2*?7-I>uc;HfwU z`2!i96uBA3BDYh318t|nD>CzNbC)p^sPg3$@ScSZKb0o#;B;*eKhGf6B98+2-wUa*Nc@G=3Jj=!A@y<;{`)SZR)d;n-S`WqS*X(&PCFh1CdKX~F^5`SLX%ovt(96{IK2@Cxp10N z3J>fZO?n;`a_uM@C8)1NMU$mkE?`Cd3s|?HJ-rvYxebc%OXw`GvuGe{XSvHxKqtKn zk&XM-lMq*($0NJCEkZec9qXs4QLH~IuyhqFQv)i~1iQ;Gh%v&(2hQQ-gZtLdy{nv| zj36S?e77w`9bF5ARufJzs6oHMM;}cP=*iwK0Me*Umn+z_?5OG0&F}R$zgL-N(p3c3 zh4KulcCYD#BOT4Ey!%ixDCyppe=53n;UCNjU8a!9%I6F|lA*>wRE?CPHCY*Eq6?bI z3-L@=YbF(%NlCzj&uVk`o;COv`b?hnnd~)8WJq+OJmVCs@V4oM9Hk}O*ZnLlTiX2> z{8IuIE+X{<&LWL4$RZwUfT4THU>qRzv@fE#NN`!4jbcxFitWgJ%8z?%89Mg**x>kp zh?8Qt_~LBtMR9h4ST7264*;NNi%{7JwrcwF0C$!g;YOg8RUW0M%_E%DRlzexa zK0R%pL%i8e^A!Z}6;`@Xu7%KrQsH`CJlDtDa4qwFwcBKh{PIe0I%@hLOUTDPRN!-c zS;y-Il3IR98F3GBU#^#u?_)CT%k@(7{RI1ci+Xfmrb|_?ne~!$HKyAnXLUy;HT{0O zbKuKK_c_DWzDTblV)%b2U0gbrch>#yqzhM+chn`_``RUykNsq%j%}h(s&a_e; zH`CIQ(;jD()T!S9PM0~dj;QGp4g%tSPtLB5>OWa8X}wV>r}0s}ZV$H~Qcd~?aBjB; z!Ok0Es~oo%ZxZE7K)yL?ru~?l^3@`u%RHAaoP_D5fazpS{03t8#j`do@xB4^{v_TR zTrMPGC+!~M4syTg;|>aNvn=iq!R^A_>|ShR6B`QoxQBZ9GuXuxz^&*;eP1#$j2PE= zBQv7lqFTBf=^o$KgHthEfZ4N=BIT0hX+t7dEX?#-~zMn`Aoq(pJ}? zla{tZOIy-L+HR(UFYU@e+W5A%(!Oe><;}D1?o2~xurF4Z)hvUVxWkSDE{INj*3lj6nPmvNIZUHAC59(S6SK3@g!i4il6fpt@fp)<%A zGXmxp$U5jIipks3E|7JQ6f05e%{aCl{dGm_n-I0{`)DGL4|Jnmi{tfb?n|8x5RqDa z8vv%hMuWzi?fUC=P=9|N#2bCxJ9j6@`z>GIJ1Ot0Af@G%VwanYytbn+?_CGcQN$#U z4~V?iwJ&c05h-sS0LXifL1Wa!%iHsEy1|1K>4ZTGRt8Pj18Bz;1iKE>h4KqzaUzAA zDL$j_Q9~o0jPY@gj|2_v$EDhj%e5cFK^S!}m=1nM_-5e8@ojDW`1_)q)AKJ>;1Mct z+R(yrgM8dWqmZk#0)k){RJvdmRJu?q;=dX%{&#*KFMe5@Fnlv!{6_=v|A z=REF9D!OCnvurhP5##SWBiKO|9uoIxjMOc?g zZI^cd^RpcpKhhP}ilmN5QF%#8k+iLVqA%0^qF=>X&8}z#dhd+(0nr`NXHj!i^bFwb z(W`;8E&3q3Z;ejGH*VH+(Gcl%8Tux-pfxEn_C-CjPf-n9Pc~W1ukkdiG)wrX6t8pjUNUNl7FkM<9kW`eQllDhAxJoAMrTv7k zyi2kRsQ(7dk*i^nq?EyE?x^3VEGy9H&!tAcTDhZswQ@)OYUPfuyjrZ4iKs?2}mMrv3lRM=>j@MHDk`7-_<2~~!A5lRkjc-a$ z`P*(_dTD%9a>}QKyQJNPa8LO=;ofNxjC0CA2xq4AP01-p*eDE3&jzy;R|>i5d{c5t zL<-~6`KII)C55T!>)Aglg}Lc(L;I9uDU_t+rzB2FiWDl-`KIKQRF~nnBAu)Ll#cFF z6xOBlP01-~E+yWO&Nn5ebd^GLI^UF>l7Wk082EH<7E^k;`6%p8UqNMhyIkq-N&kpU z`?#D)_olx}KK-$J!ckB9Y!Vpit^!(Xx<|w0xb(t1()p(3lw2tsP3N1EQ}U#6Upn8E zoH9xZC(`+* zIn{L|{}l0TxG&$~+ezMQ>=k_(ExgjE zdv2rGcbA^q$xz=j?_I^WiTB9$_VSEg>#|Rp(|FIkcdgci_so0O5yea6J@ejv!d=pM z&%Ad%;ofPyXWsh)-6k_FI|J|+*>_kP@0s^*AUuKrItXfJ zMaY}E0@3R60b`<`xo6&M?TH$wcgr>s5yRXTsCQ7S$9v|z+b;)3iuXl)_v|!i0AzD7 zJcWX!Dt7=uovPeS!d{wO5gkY^x}?d~$U!u7Z_u~_?HQhX@gMNFyWCbjT)<}JTlW%q z6I(B8+2c8P2p%Wlr15zM?oiF*He^@#BF*9sBwTkG;V!*@g74mGd_RIalGHNOv zRme&^MbbHhhowD63b`aaBJKJM0OyfeZkpV%K8hYNK22^|A5C+uNRu1Z#}ICHI+Mih z=xSmlG7&R^JN82~_US|^XQ~&_dchaTZzjRM^)2fGk_A*}*M6Un>3G(tKl#YxiQHcv zi*=bG_xqH(93w-wH#}C;JZ8}Fz>{yX$>~=}G#M$k&__EcQphc~(gecj@g?2kEQNbY z2w&&-cuIQgp=i={fpiv7cYa1X_qm)3*SJUgjy3G)Tu4pqR1AsagXe|-ZGelfw3{&` zodCEc1N&F%=BI%-$lQAlKe>?N=bE-NK8gJZ(E&@)bU8>2r~&W>feip=wc;Y$b^xaV zWW5XtN=)~uhzyVcQypHNC58$pm)(z%5`6iTx)*4LA1@aNR7usu)K5wIipNpO| z{l@FE!yhURqg2Bv(J%@+G#=+-$|&;LmJO8erHiN<vx#CRGVZ zht24rnp)wqmwcJt^A??BO zacDfkV8+wSXc@?7kMP5&6p2|FzRuJ#t`>okZsF+S-G2Ia0K;Dl;cWX19Cr!H;fgTm z9BzZN!_pZzIfN7N@!Op-Pcm-|m$giZ!Tp9N3%|*SdB%$040q``e}GbBWda%Q()gRc zgtAN`!`)jkoBf?-@gz<0+BPUqKZxBAqvH@(imm?zrGb173qNgaEd%&n(*iTe$DQf^yCw7{x(CAMez|c3Mn}@N&Z3tz~@b4I$d}wx&!P&_n2;QzY@LULoGmEwOfuA0f1u`fjAgCUc1u`h#W?2ShfecFiU3O43 z#TQW4gHkFSK1Ep%N|}_ue#j2WZZjx5-Kj^B6Z zf!{X$+6>Bgy44=ja)LxQxfLWb7jOZ(>A3rv!SIJ|n|r%uwF_87;}L}IJ?!rc?{7MD z*q%VI_J*y=;BeS}W^gbp!!6w^)W4_eCC83>#dK&u!@zT)-u{5@VV+!|hG2;FfG*Gj z`q~Ak-zVnnvQ!TwH%0uohxp1_J)rw!KwpFkdO-K#xw`mC`e8eun!*{DCCYSp5Y9dI zfLTj03LG}6N~GPtARB%9zocCmHnOJ!%b%nAde1$0nGK| zZlA&703IC6tl3laT;)Q89o?7WQ9 z;Td4))rOHXA*CM-@m+T$S~CsS7{TQjZ^js3CYkg}8DqC(G}m~e4I;YA2k%o}2F!`K zq74^j2io>82|Fwf$kTfcKO*6awA|-qyjK_^TP%@7bGt#p#*IXvI46(7VdTxd&%4*4 z?B0#CJk;oU$rzWh%+7{j6UvkLhQ3PB!HW|&u+93|OTzOQP%agHPKrJsp*-+4BgnV3 zw5h74UiXC5scMy9z}rQ>kE;e{0co=jB0pKd_j0;Xhkkd0lDnSE5O*ueI;Pf3O!fI5 zV``iGHNSov>-*yoH&?O4iBKcV(WQD6`a}Pb%-B|0KSnGm&tZHwG zYAlO2UIHE-&uFYMei<15hat8*9rS~|1uSfko=rgc$Omp~|HOGC%0@%q=o0-dX z`w4db*!_r|_o06Hy$C~A4sJzh6H0^c1n>(0S$9LlCr!Vz?ALk+yFHHTPG1FZ9KcKf z&Zs7X^)9#%-){~h4rEOU+H?xS>_$O1K$X>qDRmIid{|HKR~fN3?uP5 z9s`a0D~I*;i(6HHjr7Z&}yxXDyQe4QR6gm7UG<0gL>H)(~Hbp$vOI-Zo@ zhg_~3c?F$%eGOK+%~0Wzbu9YRN}C}15mRrrTC8&>+(n1(A)QZ{_NhMi-aQ$!B;O;# zn7$U|^(4_F!ll?dEX%ClBf_Ov;n$7V4ns#gxX5_zl%E>P+!FjsOT>FAVSN7lM5oqL z#3uG313xghzJaeZ4xAXzy&@9NmW?qmd5eL&JKd9ggCXC*`in-3fkp}m3s5n5YXxSJQ z$y-$H?R2986Vfr%xQ&=DkZXuf1~>=TLE|R?)J-c_NQBC-mdYHbPXg#DYE8 z>nw{(nu2%CsKkz`s@1BLR;@|O3KvbEsS-wMgu(qBDoxfZSvF9Ku{8CM9Hp6&Ttg_$ zpRyOgBegm+MV%`kZvHC#{%mFh?&bUl#j#`PwKK&B`y98qdbZ?hqF*W}mUALpAk?s3 zPAzr$Us%4B_SEHbEX&m}U0(Q2OyNB47;7Z!f)RELBDIir8qYgiIl~~EA#9Q}jO)>9 z$W)Z%MC3S1g7i~ceKAi&2F5H!q!Ad`zMcrrC);IOu&mt;K1kL4?boRNrkb0Q>Bj<` z2LuPFBv=p&=J1mt%#-{Ef(Gq0dM zm0vgx=3(>fOpxjLLs<;)q%F0@McZuNq~GD^>J`AeZ#-4LOgElI&*she9sVRFc_gJt z&gRYd9p0B(ZXI;(9Ntzx9+bE_(N`gvUQ&7O$eH~Ib~!1WXn*14%ry^@<3dh)IJ0>- zJ)QtaI$SfQ@*0&h+d&HL^orCn{-jnn6&y!7ZRXEdE9Q_dp6yA|na$?|rd}b6bMYv( zg-!wQrBz+I-b>|0Px*3YKL>$3oD;<^!0?M=sf1w0O#JD_3pBHN`+bLJMVG0>5~~pc z)@)+&I!6I-309ryUxqkuzwhv_5T|cxs}=F~`wlM%F>Msb?dlFm&*-Sq`=)nwyQOpi zLZqjo(l6^cv10+usIr`n9Veq5m#qxw;UaNo)Z#iS{}2mT7*%@5j_l;3=%x7-#se+Y z6r%+hEOv0+f()W{9Ox8eeYr|=YSyA487cdRoA3Y0S^gQ>HX4h zEvAQiQ6IAC9u;yuhQeMwS*bbq$_Vx9on(1M*`*J7d0Kr+d%;+t&^HZ#SnlUzi>m#z z(HYoLlFpQ>3!_?lsEb53CRE4_kTvQ+ppuuM4l=DB15$%cyBImk(3Qdv&^1!xIKVKG zmWw*NRAX32c-qBcVK~U}fOA0x2YPDnFHIKoh!F~9ncU>X6; zA|0V{H%vw|h(ROZodH0FF0*tKhi10)Lfss}!tl%u^wEw{(BJT^>{umUS*Z2LAyFW+ z9m(7ENX}!QGPk0a?Ni(l1ssBr3*L+zZpL+`Y$+{UD-E|;9>!bB!gApmmmQ3E zcZIW+2V?0t6+wa8F9M=CIuRFZU1*BR_IMe6sx;cI(|Z$hi7sJGP=QNl##1~>k$t>e(xdWrMVLXxNd4Lh*h!nx!y~D&f zESLuLf(t*Xw#H?gBmL|8er<-Ue`vTa42FY)Vb|lxVS2d!>(SABouu$I7PZq@a@*ZN ziro--!8Dkvr$M|sS~gPHJ#`HBRGr#xoYI1Q4)+@AZ8uJxZ9sLg0o64a=5E?_8R5-Q z_kbMkJqyp(JvmqR(sOWcyS3>P?-wC!i>(8{=og^cd7Bfi?>84FOn(FHP-R+27^ugL z>3dLUzy{l~&Y~BX^{h+>L-g!8)VdUQI?NArkYS{b{SNzLKDCtDbV*p$Hu+>kTW1;> zC_vxJZtFNXc51{v$AX;h^6Vr&3QW!9KRV>UGX0D(GDvD}Bg0rD1C}Xd$|+XX$-0Os zR@~jRjt^Nx=uYrsDUtGJp}uf@Ouae<=7VF1#U!F}`OD~-Z2HJ@D?G!}kW<6V+9)xN zw7J_c)2x{~XS4~*nE`Dbd9%V5#Cba*45Ux zY{?muJwAJEPHp|Fx|Y>7j-Z=D&dS>6rZzQr8;+0Z6wg{XEw-+?dSzWr_9}35nwnRy zu33}4j(nT9Zm4Mj7?JAQ27owhgB%uYu3cZFD>pQ+YpkhW9qJLLybeDoa14sZm)5%4 zrsmkHhL-wfR02aPtD76v*RIkP5C+_IkEZ6v+WNH`ZmCDl)f&!U*-~5AJhHZaGR3di zQnRYIevJ{eY1O(KgNEat$ZlpAeAG1&x5uQ#W0vHooO2p@+mw4td0x(+S^npLRNwQa z)2HfO@5h`-Qe-~A{PiA8s-yLWoc#2cr!WnVUV?47EqS)~d+3a@<7wTs)>_EoqBceUbU@ReO;X& zxT)$u)#M9rQB`|}Yyu@WxiWdJS9GB2g!;;^b3G?590m)MnMvd~V4SuVvS=9h1C)Ud!G{R>rkYPnc5Y z?e^wH@-B!B>b>{LuS|L~GPnCRZ!H?L#QV^j6v^t71G!rP7kM8x?VZ@NcrtN7__wLMBzjm9|*LGHVqtvTz;nF#m ztN++psqSr@rJKe)Ew7o7FFzeD&b2O7-r}bLuAdyjpPV zpt^C_6MNO0xD#Hz;duG#2RpncdZ?2y|Jys=mU-$97yL2{-+MOa$!F9vyYkgb&atYA zRf{Lx^5No^%L|GoA76ZAN%3oQi+?psr528#hp}F}ZQ|rfSTF zbzc__zUh9?W;I1?3JqI$VW9rWBg=-=G9`#43^}`1zsVCIo6Y2{) zj;U{}$WV8T+Lpg&x|j93dU9K(`aWcOa$7e3X5%l$H@e2lm^exObo-i94K;VSTJ<8T_Nss1e#ZBl$Y59fZ6^@F z2UgE)tK8e7z8dxRd|Bmrnd-n*-kRg;&#G|KWlL^QH*7=I?#Q0wYHw2Ev&A28I-vG% zKL)!Ns(srF3l1!3BaoM)CEC-nR$ZM82VGLQSw?S9@=ld^Or6;774BPd3WA+ktNu<= z4{h@*VYx53UR&|5kDgQu3RPaVdL8WcCY`(NwY}{Zx?Uf!jA_)& zYdrQuQ-yG)<)omlkwX#igZlAwZtRU3(gs@ z7i(@+k8gpq{y2qtMZ5$2X8c7yP(R*Md7wo-kOJeRpUF`VY&qAWPNL+ESEJ60YQKI* zf>1TX8TM~EU$j?U=Xr(m7QL*xo_Xl_an;3Zy_-zbxrG` z)q5!?)u?0Yk6ZRUi`;oqa_1TEDi*wMdlB@9_o{cHuMu61IxM;rHLF8gVRH=58Owed z240=VkBR2rbFgZ=YCb6q&GM}}=|rfudfg(!dU}KBs3C{vol{&q&HymJzmS*yASOB!QPve@6S;WitSFSX*l9arzfgvmZ&vno;>oLy5dYrP1V9kN38z%l&gr7{y(rDly%w0 zmYSA-z9tMU1V3Z>7a|la@Zwi}SjpM-WSb>oc=1=$*jUds9af@T$k}%}sMj60hf2>^ zjg>{^3nmrhJ*7HUs+K~2Z``9MdglhsoxFBZK~dE;>b6~nd#JR0)s#JFLBaARUsoHC zEm32=!@j74?An`&B2^UX`yty$VF{R;_Ba zm-*IZldEnnT2e57)#SPJ@>ZyKu4+~HsAg67gz8YqQVTGTsYS=suXcEa-HV>iD!*3U zyQ@|G7E7YTy9(9Ym}kF?Irc55Rc$_3)wCA2Q>m^-yatF@L&kqV#%m$kCp(dlU5ogN zvnJy&8R}o0^Iq>$YP>{=Q&bfi+9fK?xl999FpLQv&@D*wEJ{&IQ6l#0Qz;d7?$o^nmjL&tx) z@0VV$mIK#4Q}ny>6DQ@ownKg8s!FvdsTB?(Zm~w)aMc<0KHkH(?}+MrGN?}5<8AKY z{Z0*OR*PFz=J}Q{hW=B1TU#P7qO|~V8gU+jd;Z_vy)mBt>E10Ar~bAJv4jm*DD-51 zRKf7V{%C}oG3iGcMXLW38LA<_Xc8bVGegA+2~PI9WvII5BJ4a`utZZeF7_8i(_2*E zJt`IJgeA5-bPqCLwmV7Px=U!h@8my|QJ`+nr9Z=)x*W^kvNK7NVGYOsfEcq*jQKo7 z`JXVR*^>UV?QnZ04ew;*3~L{eqArGERb8R#)7r+mOY;#D=d6KI`mJ*&u<<>+_NaF; zb$xzIgTeu~a!X)rRB*4;Wlj6`9tU5DI78c;YC?wv|GlRWdbiI#eJnK9h=YbIA{{rgbpf#vxiWl9igC6hlGE$puUE|n50 zj(X5_7u@o-TL({=QdGpneK(}LeA$wzk9M9cQr9u;@GDplRFmZF@3oZD2|VfCw> zht>6N{t@*dk+5}aRT*Bh`WEJg7T{E=BgnLmV{-Tbwi(|+LhZC>Lf+m}s6Jn%e&|$U zo;af(bI++$Xn)MD%qg0I&2!7CkvW(Wuy1{x+tkKF)j7NBRo2zFsy^AO+Zokx{#Di8 z%UoK1Y--iWoKg2;dH!e2Lx;es@nq3NOrh^1bsqxdrt@kOMCyCq;{FP_r-A!3;8uIx zfPBT%^R}vYb`+{RBKi26kH6V0=czj}A3l!x>SrKSkA2-{tY8~KZ7{Van0;BnA<1@w-S-p?bLm#*8{3dD1&_T=7)MQS4q z+u^+W?hcTBC$gtV9oumZ>pgWQ((LujSfy6kk40mtaymgvtK7+++H_VbxPP*RfEz5pv zU6xc8kmDln*M3couEEmLulgrQeJs*Ei}apfV<`>pqt0N4dq5ScpI_x=PF^%moltv< z=G|I#R8-&AisPTj1(WKk)aJvgb7jkQJ8mCO700Z-<(4A{o}Dx0uKoY@wAZkqIaXIw z@5EvfmFudT*2QFjx4NdTrn!doa~H&#YMNujys~ENsM`9~HCuEHvub^Hef8R!ShG`K zv!xk*oTl1sHH=edW%a5nn;WZF)xe9Z6o>;ySll#QA4rVmEhs< zI1-1WS30X2)^Dh**+S;A)h!!9qlrAgvIQZnCDA9KR!)&2PrgS1-KeZ`b&DFJa z4sADM`s|ANu^9`BOXkh0j1|qQip?&YHS05)W>%Cqt7~A>)iK&LfpTqAtY-a&=Bz#I=Pf9YEi70v%OB)XIGLui zVy@!s)V@vJo)wPTdMTeJs?v4v15N=s)Imy}n5lw1d?t&hRORzV@EFV0vztui)i-mKzT^UGuN zD~iQKm>dy(Fke_tL=kjqShI#^Yih1;L~v|qXp(J$@BT9C$SfeQQ1@j(K#YE1i_!%=ja>ZSV{JOjwxTplzvAqdvc4m7IR0@)q84V`T;Z)m~QTRkR> z<3-cv$$Z#`olSC};yZ_44*5krP$s$13(d?ge#;34f?=N3Q#b~SnNmUu$)shKUgEV) z6--Q9WQsRaz=rD8d5{!;WX)8)dbBQ$(SOD|u_mtIU{+)&YV3$hY3HGaGN%NKK0Q$y zA9rHwIR}MZ*o3+xE^3w}SkA12i`65A$4}9W+{T8jvL2wE;)qVpx`y>NIqOz!%^~SE zjn(UGHa9e085@JtR8zBBE4i{| z)s?8_6l3S}(Bx#IUMPh%#MHK`rm0C*&SEb~0g^qnM-V|;gXz_bmzb6TXGJq9;Vx9GTbdhU;Y`ss(#mGds3==luxJ*eO?S1P$k^eminVVj#{7vp zG1%-%p7{vL7mE&3lr(Q=X|u+PF;^g@xGsZ&A#?iHNRXReF4KG1y1tq;EmlD58scU{ z2_a}CHk&iQB6CKj+-8vp=NOwiZq(?^VP#l_%&l(L^&`hzoH=rRb4z{Aq_s6znAWaB zk?X=qxMO4FxG|X{*Jh4fFe-E8nv#MUq1{c>*7YkJ>U5MN0NZSO5^~_QvN@cnWWL}a zX@qIz3yKS7#L8hbnJchRF;2C*rn#Cc)6k6R_X?#}n^j}w#S1FR+n}#(XhcF{et>8# z8_X)d3Fc~MJhzit7nBvuDVUE;73R7L>qEq3Y|$+E%!2t6VY%b;E=3yQf)6SX)5!~V#T!OUb-2CF)5G_e;bm_Kt? zCD*$~?OgILWFI`7kYMIVNN>2qY^l+85(h= z&}h!X5{^R|b}^m!VyjkSF06&*4Q&Qm(wM&+Y~x#yNY8m~!yuR};<@AWAXZR5tJqoH z5G>4Pk!f6{O$LYaG_*9+y(QQCE5q>mF(DP}ag@^$;?0^@P>lcP+hv(pUaye2J83I8 zImudd4FX1QHucJ#yBbpWq z~`BfYk&$hK3X2h!;SFeV8@+O<}MWLs&Qq(b-5r>feaJ8xOXCN(#P&czrb3-%EsG4dvV)Rxv zHdL=(RgLYSE{h+v84tuBk}Fmfal=tO%PhCTieR3u!8)63$G~`jL&VL`vDoH@D{+oS z)>=&HY0e@^Lfbe@pN5i*7Em{4HK%NGLWZW5Fc}iJZo{Q?L!->5VU%Dv!nOWDi7812 zF6Xb&10`i=S&1ilZItFNrV`hz-$DP#N8Y>Xg#J!uOphuokSx8XkRE=ES@j+Z@+ zOh-%zTw?l#II`x{rAg5_dnkA$z zGql-`v4Spn{NhG%jqHicBxuGt-Vu4m=%0C8R94c4*n2nfbWd1_iriiYC10jcU9e=V zui31(6UYtWs37%rykKnZ*ijQggo5*`fT1|JIsX-Tqyh(SlB+-%OT@6aP$Fo=6LY1U0+|8 z^rwv)R0c!Uh$V?2xaq7 zrO*`2kd|>dKAuo8&f&bI^P{Ya*4H#u#~`qo2V#U1))*mvZI>qNaU_m&SA75}8A6Df z(E}+iT;k2=uQ20qlHOY;^{y(IgUF-$DdE6)G=a5(5#SmM=i;DpU^KpWe{!-b$3%O5^4U7}`lmW(n$}6as1+9Ua5tW|{Xz?NGgaDNw!{~z8AJ^P z-^GS(jrq&iu<*b7ESFjE%@CS}&--YLEcl73QqHDbM{I$=a^%N#7Fogi`&G*;?{y;T8R#9X%+jlN-by!An{o+Oe@i+EiH@fI%O6NcTDu@6^8khz7Q1_pMQ-3JZQ@s@k`@?RdiPSZ=}h{HjG3{6mOV`4L;P9N@Wu9vZUb ztuPot3%>7@Uur>1kfN5hLw;#I`KJtxmwj3#7W|!Gwa}9NvtLzW!9etd@w9oXIzL^_ zU!+|NW|=B&Qd=4xAnKmMw`ZCL_$Ui4IM}bMu!Ju5s}@?&(pFB(S(7RQ>bVAgzTdCH zf-R=1{lOn^TATfj30Bx`n(p;cDlB-PU$xMJ511;=&X#@`AO=EfD3N@*C2xC^R{0TI zdd8)eVF+2auJbu<7~fGjO=n%cF`S=w{1GX!pp`-JG*i9!x|Q40A|yAie~QAFDCO-GbK4T6v8Y zeA|v;tV9Lku_Dfxg!Qn+u_N97pa+~aRAL0*>YJv*f=lq|7=Dyk8Xj%_ zav`+S!28W#%>N~ZhNacsn#z*u6C!2dtbmjS$KY{?{J752cyb|G6zHym7N znFYg46XPe5X=x(Uu7bfCVl2cGC2AL%`~aWtHvqE1!14O3U`L^3x+M98g+nDdqa(ld!1z94J|)H(yoD zPkZk7j**owqvZ$6TQE>QrrUyV>nsC&`ARG}*srRvU_fYDKte;-0aE;4B^C^NEsyVI zo1XN0Rah{+gGR=PS+KjQ(qUvve9sm??F_9Yf5InUZb92yhmkGeS&fEPxdknGji{T0 z@5c@0S9}gj`xzRR0MbW*uc4O>_8GrdLawp|qrOB-EkP?$JA|#mzc+;GC>H*!Uzc~p z>RV>06VJjc{JK#`J~417V`Jj2GO!h+J!iCXxIV4CBNh%cDzRb&Ec4o7nb!`>yo4$5 zb)%kD6YdZ%X*(YBPC~DQlfH3A2P-6~#G9oZ!nRkj>5Xu-y^?*0O&E2HV(r}y;VTS5 zi%RobbgOWCLt^_K2?Vm>*GyH!xvG0#BV2wUqy-n4Dxz7~a%^X|wG6GGaMyS_+wtJU zUJDa@B`mdbKcCd`arkhYEkhz&JD|!Ew>q~Ybs`~O>VVL~L_!IQe8))ij6X^hg+{l4 zeZm1hU);@)HFel0EdTa=BEDby`fJ+3cVe&hlnwAT^kt*QUFI)5=9mGG2UYRCPMD^j z`zRF_EX1?c`N4c`!6A53JU@DZu%&UP7Kj#{(FW0$b_R$RJlF=&mcAMwTJWPbh_+OP zXY%s{))rjhS5;WB&Qxi!ZRsr^F=6pxt@4_0m2wO6)iRpnQVUu;=o!qGCi^-pwV<_w zMzkes3OI)atsOL?Em>0l(Sp_v8qt=lDS&9f)xNUj77X-Hm_M!lUMH6ne+R)<66hE3q8;>P&vxne;v| zemt~`MX9jhSN*Dm7W}5EVt7;>v84le(-A-IO#X@F%PsjsKKW%9v;=kZ+0tsC{4xt} z^Q+1&7zmND)K3(m)C#e~7hg)JUfM~(rg^W>p#FlRAYK^hffqJ#1wQTXovW zV78leiezV_)9Ca+V#~iY_DXg(NqxLdMW%h3Z&zO@OZxNaWCti9RA!A65GspjZ$0oP zpM8SkT9lC0KZ}Y*Y0uBDHXJOc&oYR$bugj{5+$M}#JfeYNf5zcK^rpv@AF~287$L4 z^n}SE5hcMnEy}0HU^J$MT|d2-STJCga9U+G{iEM&p-m3?e&8kWf7M{W<@YMFy(aj* zO5%05DF1};d_&NRg}iAcv%OIjPdKqxEU{OD>WRG;CiY5D{T3sk&93m8E=FaWT|bD&wmF8^MG9kNHf z$r4c#WVR?Leak0Kq2DxZFPXoemD-LPp--E?xa48M|I^!-$H`TdY2T0-5+u+-SX96k z1cE~B1PIY_Bw;f_LaMrM6&%a5 zWBiQ~1r_BpC@A6<28Jbr^1bJt=XuYqb53>Q{4xFeCFeQsd6(yX&vI{7-R`>iAj?h8 zdcs>FGUrse&fXUJjydxax!Vohr*ywdBUT81l5?Nd+1p#)R|v0x$nZ$(>^UC6Wjf?h zB#)ak%`QSFW{+d`7)CyzA#@Rowgm3~Fk*Wcu{##AT?@-Fa*IjmBDT9&7CnlB{#pxL zB>bf2*vV{IT{%1mA6Km+A;XH6AC8kYO|1%q8&p&we6NbiA%n_H%x9mQYQjrY6rArY zF|#!Ll)3CeJR_qkCNnjexhq&Su}#j3a+YL)lRSMz);z6tM~f7heB@4!*>E~|C&&Ja z$Z~q}uEgBo`E!(D+$GS>1n64U774IUB5LMn@`V&S3RVpnaj8hsgj_0eMIii}S4}hC zt^hiNt^lQwcgc5TzCg_UOkRLvuvp)&i#8|#9Wli{&NlQN+?UYzmqkq}u$_J*9IwQ&EF$J5FqC7iE3x(ONC-XyR$OWk)9GO|47L%70e5jtL}V`Ndt zt4LmRl~*Srnb^Av_DDvqw1gxh?`aW|jA#)$Uco)caR*y|Z$mpJseTB2?p9va6uHu& zX!!}A>N(mg*^tob>FPYVKvQLk3&wQ2oQPDIb|k)EV6&L1&e9keQ(#SEi=H#H3ya9; zOaq$}lvTwqL5u=jwW(PbA@h{$8zBkH{Uc$XJ27(*HNwf#c>-yWi&T%0QRIoQyEd?M zgs$u=;oBg}^w?RVdIdt#$@KjCJPaO>qK(WVE6!@mrH_z>&py8h3swn>6HE}j^BCIg ziNuPCV(30NPw9RJmb+OtT*Q}R{*8>78!d8IN3+#1<7+eaFVf{yBwX*Q`qd{Zx6?|j zBBM!~a*Q&u$d2NY*{R_QgwLs{la9{Ua0SBmtEiLk6DleYItIaP z{|R9ZR;w2uVBcFu<{jvGOp31qfB__ ztF1q|%OyMuB6IAN>+B6VMZ(KfR3;pJjk+UzzlzF)brl8oWFgg*RA+0r5+TVMs;N@F zz$p@vvQ=b{6y;J%xJjv{gk)=N{ql!|izIxK@~9Bz2^S-TD>1^wIth=d$08w#%VA9T zVa=gPn9m{n#g{OL_ArO8SPt!x9J*pTv}+EZ(j3|eNn9?6gx6{g?S%Op!r#FObLe0W zte(8K-?D)n#-5UN5HfbA$Id5}NCzQ_WO}<_4=c!n1(eSq2=nVR{ah6l3CUWH>d#=5WJKhb>>-4CO2ZXs zb)Wh!JOsGIiH^hEoT_tFAUs<|?dJlnbRt5Ml9{|?gHP-6fL6yW#GbDlItV$Va+Z)h z8Jmj;v18vs_Ks>tNVOwEb!$MC6Xr-%IXiADs^rD2Bkyd`5vSWwr>9j&+Q^8V_u%%0 zW?U1zePO=`?wY+Z&syhJoIgWU$)&wJmDmOSFfd`HW{fz8u72TChATJC1x_fix+`t@z?Lz*Mdm0Y8f&{_CR14uE57tg6Vo&0DGgQ8> zyShxc_;9-lCm_@MiQ!o2?Zaa-0zjpEDVM`O*86ZD0-Buw7<)K1y8@z$><<_MnzS^>FT>< zZ@_(36s(|#qvzps604h_a<7K(BxH*6_Zte#$<0frpBvRrQHouJEYj5EvJH_r2+BlP zw37uI7Pl#ZpCJ`9O*9(-DI+6hPLX%#X?8x$rYDzW&sMPCt2q`4FZ5LX@^B93scDdQ zsM5JRFR?S~U_?0FW5oU(Wl$t+^Hlw?n=t;cnwAM4J<=-691%Vakv&A%oBldgBs^3_ zWx};8>LeUhQE=3Ryi)RFjO><$o!ROtC1jLLj~y2>ZB~l!YAK3@w9f8#*>SFdb0uWo z9$~+A1KA&;?Ar+$MM7@fz>W(UJRf%A5jLM;v~os$5Agr0s6cpw&TSWGaaJp5vFlBM z_d3zhU`QsBBaj*X9`!RT_y~A73hfN@VdXW`j-FDdgs~*gyMntqCbdp8=&l3)NHgdT zGw7yQmqB-!L3iT)aN6=2kir_JP$ip9N});$$G_IPlpD+c2S_R+x&l?;w2O$4>`c*! zU9GExTmZ4t;i#Z871Cl9xqODsgLj~qGV_!h`di`qE1GhNzTLVhF;!RfQW!3{>`zv8 z&hlQ#B?$vE86zWRm{|FY-k#Ktv(A{=ieykO*G$MfpTc}*LQ-I;=z{iR$l1yWOhrV# zD-n4sHKC(@F)^_P@n>wr=h4ozo6kH-NYPYX)kVTrLX^|?gCBxb;5I<_e`B?~)zHn= z?pA~D!3BEW1$&Ub+Yw{0K;{-&gm)g7-#4xmRsm3SX_NzAO*5Ik(SCPmcD zsu$L>@CYx2$Sgd1Dz{n(qDI&ckx?Ud0fSekAx*vFI;-wxG+lC?q~xrE#kdsINVN%T zKiImUlHK&g1S4B0wCW740*aR**88;nv4Z{b{(xcCPa;$aiW;f}^mryWT*J7L z8?Iq~d|GmvWCzhMce!e2bp&c)HWkgD$jN*=>THKjZlh833k=CdBYZs6_8o7;<>oVb z*(no#5hDAFhP`{8BH?2VitIh-6bbh^%7(Wd**gwWc@K$jPJ`m^Ij1qsX`P&Zpj1*q zvNTlEI(vyyNeRi)DzZnS^5iUGo^$v&%fdPrW1Nf1`8MTTBqU2irKp_$+bI%~rB!5) zL}h^x<~fI-{Dhp_W1QQS^MgvIoiNY2T{%zHI=2&&rQzIOXOBeX7t0CroWoCg7Q^!r zjZh%GRz<;!(#;t9uBwy?pFP?xKiS`eFG6Ifmh0@zdc7(V&Q?)y!i5o2M%<|py9gP> zL`)+QyTXX6ziaODax_N|LoU;h-4(zNv__4%rfMa^OCXZJ-?^dA&XrD&%tNgV~R1w%(&p`)&lQI|h6p`%jh zs1!QdgVZ`hN1dUg;Ii0?gxe4?$$tEjrq40=f~c_#*rqMDn+|3w3sW2;t=3s4aIGgx|;+rgiqdt6@{Ze1z~; zgnU1*dzq*>moLbDi!<-uW>|u-Hk&Z^ zZ4F)~d`U$m!oByhhIX3Sn**t=2g3OcitK&LDH7hSqToV8YIi7)lyI6_brF(`T_Wtg z$te<&j8$ZheB=`ZLPsq)z)9^krB)>TFBMe?NyaWO_V|q{auY;IGFFkj%Qc4z;dKyM zMfR?DiiFI?DzfJ?E9yc3U}jtda`#WjMIiT)gv`abZ?CgAPkD3`G8e1J9{GqzLPw1k z5oH)s>mW5+8}}V`_MEQ{bpr4m%+iB8TZLx;UvMHqrYlo*Fmijf=2#$nqlzkoOjNFo zoFtMHkx%N^i0s03(gCd_Zx((ER^L$GB|?@*K5JOVxUML{dBF;}_$8*xecg16`?>hO z%-KU{$DvTM^XMNj<`NEQQswGN+-$|zcC>Q!BK%Jkl?Y#g$Sj|7oxP){+N30B!V@8~ zitK&TDf(p~kMC>Hl<;{KbrLeNai1!WIqJTXkddt-duyB`;iW1{2_4(uvPQNnm0UFm zUsSeTgn70_W%~w=*hNTwhHbIV9x2L+OE{snMMB3mxW{`OTr|D)SLrS=>T6GeVwV{@34wpJbLbA4s?A_!P z3CY?jvPa@_n@#9)2+jfK@Ey&eNcf_PDunqQikd@NsZ|JjA=)ds>@Z?xV(1p@>|LRn z6WD|Z}BMqnPN()~b(E}&c)`inb zcJXTYwlZZj?oT_ix!!&BD^3+7lJKz;|HiazVK@eF*_D*n${``~2pAaE?6N;Vx zR3;V1JWyR#2pL7r073?gzNHmGXqu5v-}hz8PiHehHFht}8e zOPjh+-}hxoO=tXiuvqywBaK@$oigF)Ac_YJ4bzxmcs7NG#h79EA?;T1xzkZ4{I^M| zhK6a(Fr*q97Gs7X)otMOYe$vv*#@fY&DL*CD-%AUqTq%xbf41w?=@nDkTJ|WrgirA zQTHjKix_+Y7)C5IA{`p{MRm_CP`3Ergop0|?B9%jWE#K;(^43|bme7DNx z)%}orCG&zesn3M|x6#TWM6(@ErHc4^QG|C&pgZB+7&dzJoA+LWYKE%rHE5Lc?OrFg$k1XMgaL7xaWJlTr;0 z)0kmMH8d>73`45q^IAuhuysEs(6AUY z45^aOX^tx4qDiTShH1<&q#7C)V}>DB@>%Yv5*8+<8XBfC!;ordSd1BlRLQ66s1mN6 zlxk?0#tcKMp>I|;wl5X8R}GlxVCQ^J)h>LMgX6Ev-}_a&!DNIq7P zy?dM@VV+$$RUx|~*{xDv-Gt<0f)*S8^Z=+UFpVlaLljoMhI?1D|e5CpN1%2yU;L=8HW4!13|>#0f|s0 zc)73@UicK2@Fsk4Qr@9q8Z!)ee?5p8@D5c1-aFvsH)!RhGU0!zD6pD`p#^C7N+wzW zwc)cI=TD+@8OGi-iOwdB-HKK!OZXLt;wvIFOk;*&Wj_%_EJC79s1np@8-j<#h8YnnP_7x;uws*VGAY>SmKw4+-Y8Z$jA>CU=bq&ypmTqO%2YzvLwUmJ(#L9FvP7R zUM_&hSp}PBMW7Nm4!tl0Y?Fj(vGkFZbQ_evhc=Sre_&`5FTV^_0xq)ivL8N^7%g%bgeOClc+`2v;?m|Q46(Lyz+OP$`_IAl5>}I^X5_3Bb)xMEErowR0wRs;KZ+K*yj!1}7@1AQkdafWQ`C`PEw~2AY55NEMFz|;%E^P?9@cdgjcAjM0gcM_FQ4_PNzutod!kr ze(MzdbjafzRY?gys-kW}Mz%)_d*617gp6zz*>gOCkA=visJ@DXA68KpAtT$aEg9a$YYmQB_&*}R^5b*Y z>BNUBu>#@Wsi;E8q-0+ZZgEQ(xXRGF}#q7vadAu@}i zTxajAPLc4Q21WK-^Z|haO~af6bTvGDzfKz1Ur*FTGUsO zkPkA-0Zd3o#(nsTlD=3_*( zmYkZOHC<6hUSRtw+hM*-J(mgZnQoP3h6(wkj4d*Ie{_n3PeWuC**j)$_2~0>{C|_j z^yWNH2Jt&xPJ5iDpVR5^u?(%5N#btxUm@&;DEs0#c0TF!eD@nL^ifqQ5YkGXI_mJv zJ`Z(fXjuIMGEP)%@A-DJENJ#eWl$odr}%bk!vvIcBFcpQDk>3miKOI0vIe=Z%nbx}RNrIHfnBZTK+7@-)8P}B%N*9b+ze1!1e zsS@E45XGw$Mo423(mEsTs}WMde1xz{VT58VLQx|erV)yS`3PZ^T4AtA4akj5gUbw>DzMo0e+=YJ^8LLXj{ZA*|947`&v0 zWx~HeWUIv9fp``{&LzUbA!>3i1rgF%gtX2Guha-BVLn1wr7(wLEJ9Hu9I6qDg!u?z zm0DqNjvAH;Tle8PT_HR{MPRl>ZlF!d6A@L3eOK_c9!?z;$YP*I8SMim8b{+y4YW0h3!YPcIJ3?RP% z{{e_{j<-N`2U;OX_+1rs68=<0!7Dn}m#Xqg``@AMtb(^;SnuUfjooOz9b-;sUa$tJ z!?h)QjF4uLm%weP-bq@;GU1aD#dqfU80}EQ!U*6xwE3r|D7(^)=~GjbUGK*9sVT~~ zxG{Zdin2T0m_9W{+5K)zpPHiVr*2H2nxgDUH`es2sYfs?PonL8rQOY!qV^rHTDY=+)3(fc6h)pGNEZAupd#Gk>?j&uhS=4efHY&HT~so3Quo{P@({ z9e?uh?T>G48?`YP7!oCDcQE8QL4r`uaN| zf9nBuNjR~%|JhS2)@$-!2_ zudiQui0xmB*4L+=ekDEp@|)y+{dV}Tpyd~}^IOfgAr8O4Jb^!cMZ52hcKm{I-_F;k z9>3muA>uJlU;lQ*zZ2~@(B6&K|K{8|hglE&rd;12zuxv4*z@~^`CYcY{wDb2x2tYN zpWmD6>wg+w|92DGThM+Ut^ZYdOI~d~@T>8BfBdq#1E9z6Fy)um`TA|Iu@ZhsTmgN4 zCz`Ln9pn6(sKVh^gI^2f>+g4@l^lqcUp>L^QMj_zj(-;IoY&d@0<^a0{-+(k(tvjN z!`^9HM7`?~KY?CL6McSJ!55+b6}0>k1mAz_YpvwO{IkuzzfVDM zBie0fe~8v^{J+HJ{}nc0pMPHMLwnhgkD$E{t*`I@&8S)U47CL<{}|O#===8Xfc!kP z?Pyc99ca7Ju0%V6*7wK1?D8ze`DcpwmtK7R!=_mY|K!I;^f#mR_5DBVan)}0e~WQ` z^5_3uiUc31%tY(QJ0J3QqV@lHs zThZQz_5rj{qV=1DrdSD|##@U%9~$)a565^bTK`!WCX&u932{m+K^`R{E_ z*7JJE--Y(QXy1pHpCey`{tT?MmL}_s9}qr=aelz0zcKC+_CLtGAM5isXg&4B|9Fdk z0ic=wF*B`)j%esR!KWPYTLC$L&M{>xod_d z=B}FPALu)=zc0ypYkS7lCUg7N4~eC1$4BkR#kJ9~{^6m9PF2Q6YXd#T0BB@jJek`+ z)Gz;!*Vf7ZtED4j!+kyDJ;~hK+Un}jp21pmZJ*e(XDzBdqoY0Rtp)J~y`pAJO&sx( zaq1cD?-d@y;$8lmn}xLQSB;G&b9;ve2Wvwz*jHOMu|_=g46Uh&+;;niRvRyNXw|CG z+QsY*^bggDrA&ka!xGHu!;?Qx|AW4{6EmMoap&HTtcJmChCP+{-Ty)1Hs< zvfYRQ?9c>n6RZ6~LFU~G+K(Ur+bi)_h7lVb+T)HmStK&!oAxj4FCF<|`ydUhI6->o zCCL&&FX#9_0LN^fgC_Yi4qJ06_-oF78*aJTE`q(`Z(@6Nl0K0!w@ekc+zHwz5s2|= z&-{lx`zLWL&6Y=!u{ZDb`1b2WW_a;rdEs@^ksr3CW2KfWv1!NTU*5j;C_Bp5za`;2 zE^IIN>^B`_?b)vVd)w##v}6AY0r~hZtg<6)aY9j@PdsV=L$K$3w)EN|w(Dl8Y6krH zANTC99I^Ur&*a3xjhXBG&t~Zu{*1rf6jXlLe%}ko`LkL{@;L$f$v=jf^O4qUXwFBn zM(5l1-2AhDy8yFgXx|3=wmr1}w%8i}v|kAOg?ng!pJ%@a_KRT8b4b*(pH{SNek{PO zUB;hrRckgheX^<5 z>Ta@)&!2W|fA87ff3>y0|7vS*dlsAjX-CP^kTL({MyGi=VqfEH*>6v{wEqU4;&kaBON^eRM*572hm)wc_>t`4LGcWT!7KZ0G z$lqXTB|nFJC*<=We-`q#Iacr%$oG?D*7zBL{Ed(w3;AZqO{ef;<7i3n1SC`3U6n{{rOOA>RiV znIok&>$8>n9oLunBG2aCEO_~8L%vHPZ@~rrDafmtA9r4DgM3)zvGw_G_}Tdm>%ZML ziTT|b6Sr`#6|fGUgM1_8i=lrfW{~h%I2>GVvR&WUP zUlMtY{|vc-j>%u$M1F$EO);~|cGa+w-{9*7hzfh9tudo7sPgEb|g?C!{&Cnl% zJoyJJm;wIprk_qLZ-IQ1$fqQ;l93TBmlHqxxdrk9EEOgtA9tPzkhQ56(EjgbtVtbc{3dI zGn(nk>2g&oJlJMKCJ(Q_S7!1TfGO$I@!9!_G{32 zAd`m%zkvS80y}2T3-j{_k;nW`pYG0Tt7-o?e+RZTF92SY>C1W5Wd$cgZvIZI;o0_g zR&WpEjzPY%V&$C2Dtq|`ZSZpxY<^66$qJbJpPKle zwRgM@M`v=m-r+!^pM{WbD_H;7PRR<$w&O<$q0j5=?ILfH`!|2R-3|Tis2lIVew@h>EY~~({jJbv+t0QeT@@4z}Kis*oi{+f)2qLE`uRZln?C-1`@dxJkk41( zr>)11@hkTpfP4q)$vexRHu3Xk=oe5QuD5A3W7e{cCbggTg~U5$Lv zDl5MOsjO+@=O3HMuZEwNGpqxC9l#eLPrUu|jV68`hJM?8>t}(S}TJ7;&m`j1)IFKpJw%z<9slW4jOq2{PT`~Ipiy`e%8VN8puahS^>(JY@i=+ zzh9rpVVrBsBYxaFIdO7t6S;YuGN%7x6Zx!H#`Rw(a#Ob*Ufs^l^kv?0KCA_)mC)zw zGS>NhP5l2Kk+qIUe+0P#!PcUCt4#`w`;bi7gzY=x00Qx_Ky!FjiFazuMG03+Tteo}P)x`hdv*K~vGP%62#Q8%%XF|Tno0oTr zyan9y%}5jfS7iEfKmY00yn6CM_~#!RqtAbVeDgV0y?}G+R>*gH*M$e*-@ooW4E?Pu ztsiXrxgWh?B^Bx+4Zy0J6B%|dBLm0o2iewabGMGIdg&Qtm%Y)+e#~4WSe9p zlcSSsKHfzB*(UNkMBbXSC;Wb=E3eKKk;meWG?8z}{Nz1bT!;TQ_~-r4-6B6KR8M}E>4)#v{j!Pwm*IaW&O@H_ zhY9)E{yjnD2Snl~y`$q};}fe_&+SdB)pO2hub#WKlvb-rUv0Furhja_Hd-AYto9BJ z56MT*5mB|;H(XsaFubZ~pxQS+JUUkGnOK*|N7o|*weect+yy6{JYPIVl&a9u2(f5#5breJFB#~x>lYA7^r2;mn~Ud9jlF3heyV%<7-E2 zJ$==&p4ByRlOGxESy!zM)T%=hgWgDO-AMmvtvcR6=-XUaTVFNl^o&ME=JyZv)z*2w z%t!dbzUJ#AhCH#*zuF|2XJ(@GO^igO5l3Ua;(Vw>JXs2S{yet9vE}#;fL;hpISg zAT=^N+?!QpY+!i2I@W(_6ISEvNBj}rLR}3`$RiObq8~h~l}W=*P<3KNN|GyXwYIL- zTOC_l8yG;sCdrIt&&2p}pub#FMHWrQIrY04&J$m@q`F2H%&MN=3&%%$dTUjARHA=q zjXZTRG&U~rM&_Q9xWTN})m0O#H8m7lR$b3%@7e}&ZJ>X!e@IeJvMMYuq}64M&s+TV zvuuArwpI0_iQ0r&x|zmBQnjJJ>Loq0$}c_%TA7AS?m%^DxDT!_s>)Lw7aE;vwYO)e z+HW?NJfi|HCZTLB>kn8D*=ATPJAzsl4EGPYFhYhjGGe{M6GJL76;Y~~)X|Ho6GMi^ zT-g)jbLTO2k(&ghD=1IS*%>N1b)lQ<8CudUw4arxE2?Lg&pRVsy8OIqb?zx`Z3~(V z%zJZ_fs@*r4K#C=9aFL3CogQ`>Xeh43@k9g&u(A5?5yfp=bb5ssXbYV`^JW4uMYJM z$c8&}#d(XDEj>dt(q(5*`|am0KYj7J)#YcOT{+= zjMJSdfSJi^EqhGNncAvkiAwpfQuIu5`J6O&ey-d(E6xH5kJoy~v-2^_cC}pp$6~81 zm&{<#_=H>{8|OZkw>w!b~>S@Mtm;Dd5;pEO6dyzubM93+Jl!*?mbi zl#$P9SF1f^W3|!o>S|f^&8f+~f?kO-V>|#^t(?_fG6(GZwt1(toihK;iL7Bc5Sn|k zNmhGTHB7RZGP$Q3%0eK{gNB3g!y5{$;_NTc%`eOzw=lvqoP1;NC2E zL2*|yB$Gd1b0?TyA*xk*XxJ_nn{;lw8CDIIF#FG3eexGGH)XrIlsak|kLpRWqj}!z2EYzd96E&$+nTju)tB448v(EXdjszLmDS0vT_lYQTsK&5RoE@fdklFEuufiS s%1ey_d0ppG&QfgjZ&qNVR<@jTQ^a+VOpFcGYThwBHZC`Tqm2&!KYPfZApigX literal 0 HcmV?d00001