From 1d3ed789b5cbc6dd114d6865117b564c12e32c59 Mon Sep 17 00:00:00 2001 From: aschwarz Date: Tue, 25 Jul 2023 19:16:12 +0200 Subject: [PATCH] =?UTF-8?q?msd=20Backup=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- msd/.gitignore | 16 + msd/.htaccess | 7 + msd/.htpasswd | 1 + msd/README.md | 76 + msd/ReadMe/ReadMe.txt | 18 + msd/ReadMe/license_english.txt | 88 + msd/composer.json | 14 + msd/composer.lock | 755 +++ msd/config.php | 163 + msd/config_overview.php | 1273 ++++ msd/css/mod/icons/arrow_down.gif | Bin 0 -> 132 bytes msd/css/mod/icons/arrow_up.gif | Bin 0 -> 131 bytes msd/css/mod/icons/arrowdown.gif | Bin 0 -> 211 bytes msd/css/mod/icons/arrowleft.gif | Bin 0 -> 131 bytes msd/css/mod/icons/arrowup.gif | Bin 0 -> 208 bytes msd/css/mod/icons/blank.gif | Bin 0 -> 43 bytes msd/css/mod/icons/browse.gif | Bin 0 -> 991 bytes msd/css/mod/icons/close.gif | Bin 0 -> 131 bytes msd/css/mod/icons/delete.gif | Bin 0 -> 145 bytes msd/css/mod/icons/edit.gif | Bin 0 -> 985 bytes msd/css/mod/icons/gz.gif | Bin 0 -> 1468 bytes msd/css/mod/icons/index.gif | Bin 0 -> 1003 bytes msd/css/mod/icons/key_fulltext.gif | Bin 0 -> 998 bytes msd/css/mod/icons/key_nokey.gif | Bin 0 -> 547 bytes msd/css/mod/icons/key_primary.gif | Bin 0 -> 994 bytes msd/css/mod/icons/key_unique.gif | Bin 0 -> 1001 bytes msd/css/mod/icons/mysql_help.gif | Bin 0 -> 359 bytes msd/css/mod/icons/notok.gif | Bin 0 -> 74 bytes msd/css/mod/icons/ok.gif | Bin 0 -> 138 bytes msd/css/mod/icons/openfile.gif | Bin 0 -> 154 bytes msd/css/mod/icons/progressbar_dump.gif | Bin 0 -> 780 bytes msd/css/mod/icons/progressbar_restore.gif | Bin 0 -> 680 bytes msd/css/mod/icons/progressbar_speed.gif | Bin 0 -> 108 bytes msd/css/mod/icons/rename.gif | Bin 0 -> 296 bytes msd/css/mod/icons/search.gif | Bin 0 -> 991 bytes msd/css/mod/icons/table_truncate.gif | Bin 0 -> 979 bytes msd/css/mod/icons/table_truncate_reset.gif | Bin 0 -> 923 bytes msd/css/mod/pics/bg-body.gif | Bin 0 -> 1249 bytes msd/css/mod/pics/bg-buttons.gif | Bin 0 -> 1298 bytes msd/css/mod/pics/bg-headings.gif | Bin 0 -> 583 bytes msd/css/mod/pics/blank.gif | Bin 0 -> 43 bytes msd/css/mod/pics/h1_logo.gif | Bin 0 -> 3505 bytes msd/css/mod/pics/loveyourdata.gif | Bin 0 -> 6340 bytes msd/css/mod/pics/navi_bg.jpg | Bin 0 -> 16812 bytes msd/css/mod/style.css | 664 ++ msd/css/mod_green/icons/arrow_down.gif | Bin 0 -> 859 bytes msd/css/mod_green/icons/arrow_up.gif | Bin 0 -> 857 bytes msd/css/mod_green/icons/arrowdown.gif | Bin 0 -> 65 bytes msd/css/mod_green/icons/arrowleft.gif | Bin 0 -> 878 bytes msd/css/mod_green/icons/arrowup.gif | Bin 0 -> 64 bytes msd/css/mod_green/icons/blank.gif | Bin 0 -> 43 bytes msd/css/mod_green/icons/browse.gif | Bin 0 -> 995 bytes msd/css/mod_green/icons/close.gif | Bin 0 -> 880 bytes msd/css/mod_green/icons/delete.gif | Bin 0 -> 944 bytes msd/css/mod_green/icons/edit.gif | Bin 0 -> 985 bytes msd/css/mod_green/icons/gz.gif | Bin 0 -> 1468 bytes msd/css/mod_green/icons/index.gif | Bin 0 -> 1003 bytes msd/css/mod_green/icons/key_fulltext.gif | Bin 0 -> 998 bytes msd/css/mod_green/icons/key_nokey.gif | Bin 0 -> 547 bytes msd/css/mod_green/icons/key_primary.gif | Bin 0 -> 994 bytes msd/css/mod_green/icons/key_unique.gif | Bin 0 -> 1001 bytes msd/css/mod_green/icons/mysql_help.gif | Bin 0 -> 359 bytes msd/css/mod_green/icons/nokey.gif | Bin 0 -> 1299 bytes msd/css/mod_green/icons/notok.gif | Bin 0 -> 74 bytes msd/css/mod_green/icons/ok.gif | Bin 0 -> 138 bytes msd/css/mod_green/icons/openfile.gif | Bin 0 -> 941 bytes msd/css/mod_green/icons/progressbar_dump.gif | Bin 0 -> 502 bytes .../mod_green/icons/progressbar_restore.gif | Bin 0 -> 471 bytes msd/css/mod_green/icons/progressbar_speed.gif | Bin 0 -> 484 bytes msd/css/mod_green/icons/rename.gif | Bin 0 -> 296 bytes msd/css/mod_green/icons/search.gif | Bin 0 -> 540 bytes msd/css/mod_green/icons/table_truncate.gif | Bin 0 -> 979 bytes .../mod_green/icons/table_truncate_reset.gif | Bin 0 -> 923 bytes msd/css/mod_green/pics/body_bg.gif | Bin 0 -> 53 bytes msd/css/mod_green/pics/h1_logo.gif | Bin 0 -> 4780 bytes msd/css/mod_green/pics/loveyourdata.gif | Bin 0 -> 43 bytes msd/css/mod_green/pics/mainnavi.gif | Bin 0 -> 1748 bytes msd/css/mod_green/pics/navi_bg.jpg | Bin 0 -> 21264 bytes msd/css/mod_green/pics/overall_bg.gif | Bin 0 -> 51 bytes msd/css/mod_green/pics/pagetitle.gif | Bin 0 -> 200 bytes msd/dump.php | 478 ++ msd/filemanagement.php | 550 ++ msd/help.php | 33 + msd/images/qrcode.png | Bin 0 -> 37957 bytes msd/inc/define_icons.php | 44 + msd/inc/functions.php | 841 +++ msd/inc/functions_dump.php | 568 ++ msd/inc/functions_files.php | 486 ++ msd/inc/functions_global.php | 1507 +++++ msd/inc/functions_imexport.php | 418 ++ msd/inc/functions_restore.php | 420 ++ msd/inc/functions_sql.php | 1268 ++++ msd/inc/header.php | 50 + msd/inc/home/apr1_md5/apr1_md5.php | 79 + msd/inc/home/apr1_md5/license.txt | 22 + msd/inc/home/apr1_md5/origin_url.txt | 1 + msd/inc/home/databases.php | 230 + msd/inc/home/home.php | 144 + msd/inc/home/mysql_variables.php | 118 + msd/inc/home/protection_create.php | 167 + msd/inc/home/protection_delete.php | 26 + msd/inc/home/protection_edit.php | 93 + msd/inc/home/system.php | 101 + msd/inc/home/update.php | 87 + msd/inc/mysqli.php | 543 ++ msd/inc/runtime.php | 150 + msd/inc/sql_importexport.php | 290 + msd/inc/sql_tools.php | 197 + msd/inc/sqlbrowser/mysql_search.php | 387 ++ msd/inc/sqlbrowser/sql_commands.php | 114 + msd/inc/sqlbrowser/sql_dataview.php | 406 ++ .../sql_record_insert_inputmask.php | 60 + .../sql_record_update_inputmask.php | 66 + msd/inc/sqlbrowser/sql_tables.php | 583 ++ msd/inc/sqlbrowser/sqlbox.php | 88 + msd/inc/sqllib.php | 69 + msd/inc/table_query.php | 181 + msd/inc/template.php | 456 ++ msd/index.php | 51 + msd/install.php | 553 ++ msd/js/script.js | 54 + msd/language/ar/help.html | 147 + msd/language/ar/lang.php | 109 + msd/language/ar/lang_config_overview.php | 129 + msd/language/ar/lang_dump.php | 62 + msd/language/ar/lang_filemanagement.php | 80 + msd/language/ar/lang_help.php | 37 + msd/language/ar/lang_install.php | 93 + msd/language/ar/lang_log.php | 9 + msd/language/ar/lang_main.php | 89 + msd/language/ar/lang_restore.php | 21 + msd/language/ar/lang_sql.php | 190 + msd/language/ch/help.html | 146 + msd/language/ch/lang.php | 109 + msd/language/ch/lang_config_overview.php | 125 + msd/language/ch/lang_dump.php | 50 + msd/language/ch/lang_filemanagement.php | 75 + msd/language/ch/lang_help.php | 34 + msd/language/ch/lang_install.php | 80 + msd/language/ch/lang_log.php | 7 + msd/language/ch/lang_main.php | 85 + msd/language/ch/lang_restore.php | 19 + msd/language/ch/lang_sql.php | 190 + msd/language/da/help.html | 147 + msd/language/da/lang.php | 109 + msd/language/da/lang_config_overview.php | 131 + msd/language/da/lang_dump.php | 61 + msd/language/da/lang_filemanagement.php | 79 + msd/language/da/lang_help.php | 40 + msd/language/da/lang_install.php | 82 + msd/language/da/lang_log.php | 10 + msd/language/da/lang_main.php | 85 + msd/language/da/lang_restore.php | 19 + msd/language/da/lang_sql.php | 190 + msd/language/de/help.html | 149 + msd/language/de/lang.php | 109 + msd/language/de/lang_config_overview.php | 129 + msd/language/de/lang_dump.php | 57 + msd/language/de/lang_filemanagement.php | 80 + msd/language/de/lang_help.php | 41 + msd/language/de/lang_install.php | 86 + msd/language/de/lang_log.php | 7 + msd/language/de/lang_main.php | 89 + msd/language/de/lang_restore.php | 19 + msd/language/de/lang_sql.php | 190 + msd/language/de_du/help.html | 150 + msd/language/de_du/lang.php | 109 + msd/language/de_du/lang_config_overview.php | 129 + msd/language/de_du/lang_dump.php | 57 + msd/language/de_du/lang_filemanagement.php | 80 + msd/language/de_du/lang_help.php | 41 + msd/language/de_du/lang_install.php | 86 + msd/language/de_du/lang_log.php | 7 + msd/language/de_du/lang_main.php | 90 + msd/language/de_du/lang_restore.php | 19 + msd/language/de_du/lang_sql.php | 190 + msd/language/el/help.html | 149 + msd/language/el/lang.php | 112 + msd/language/el/lang_config_overview.php | 128 + msd/language/el/lang_dump.php | 58 + msd/language/el/lang_filemanagement.php | 79 + msd/language/el/lang_help.php | 40 + msd/language/el/lang_install.php | 86 + msd/language/el/lang_log.php | 9 + msd/language/el/lang_main.php | 89 + msd/language/el/lang_restore.php | 22 + msd/language/el/lang_sql.php | 190 + msd/language/en/help.html | 147 + msd/language/en/lang.php | 109 + msd/language/en/lang_config_overview.php | 127 + msd/language/en/lang_dump.php | 58 + msd/language/en/lang_filemanagement.php | 79 + msd/language/en/lang_help.php | 37 + msd/language/en/lang_install.php | 86 + msd/language/en/lang_log.php | 7 + msd/language/en/lang_main.php | 89 + msd/language/en/lang_restore.php | 19 + msd/language/en/lang_sql.php | 190 + msd/language/es/help.html | 149 + msd/language/es/lang.php | 109 + msd/language/es/lang_config_overview.php | 128 + msd/language/es/lang_dump.php | 57 + msd/language/es/lang_filemanagement.php | 79 + msd/language/es/lang_help.php | 36 + msd/language/es/lang_install.php | 86 + msd/language/es/lang_log.php | 7 + msd/language/es/lang_main.php | 89 + msd/language/es/lang_restore.php | 19 + msd/language/es/lang_sql.php | 190 + msd/language/flags/ar.gif | Bin 0 -> 226 bytes msd/language/flags/ch.gif | Bin 0 -> 1765 bytes msd/language/flags/da.gif | Bin 0 -> 117 bytes msd/language/flags/de.gif | Bin 0 -> 158 bytes msd/language/flags/de_du.gif | Bin 0 -> 158 bytes msd/language/flags/el.gif | Bin 0 -> 696 bytes msd/language/flags/en.gif | Bin 0 -> 206 bytes msd/language/flags/es.gif | Bin 0 -> 141 bytes msd/language/flags/fr.gif | Bin 0 -> 140 bytes msd/language/flags/index.htm | 10 + msd/language/flags/it.gif | Bin 0 -> 140 bytes msd/language/flags/lu.gif | Bin 0 -> 232 bytes msd/language/flags/nl.gif | Bin 0 -> 141 bytes msd/language/flags/pl.gif | Bin 0 -> 1121 bytes msd/language/flags/pt.gif | Bin 0 -> 3316 bytes msd/language/flags/pt_br.gif | Bin 0 -> 222 bytes msd/language/flags/sw.gif | Bin 0 -> 117 bytes msd/language/flags/tr.gif | Bin 0 -> 612 bytes msd/language/flags/vn.gif | Bin 0 -> 207 bytes msd/language/fr/help.html | 148 + msd/language/fr/lang.php | 109 + msd/language/fr/lang_config_overview.php | 130 + msd/language/fr/lang_dump.php | 57 + msd/language/fr/lang_filemanagement.php | 75 + msd/language/fr/lang_help.php | 41 + msd/language/fr/lang_install.php | 85 + msd/language/fr/lang_log.php | 7 + msd/language/fr/lang_main.php | 85 + msd/language/fr/lang_restore.php | 19 + msd/language/fr/lang_sql.php | 190 + msd/language/it/help.html | 149 + msd/language/it/lang.php | 109 + msd/language/it/lang_config_overview.php | 126 + msd/language/it/lang_dump.php | 57 + msd/language/it/lang_filemanagement.php | 75 + msd/language/it/lang_help.php | 30 + msd/language/it/lang_install.php | 80 + msd/language/it/lang_log.php | 7 + msd/language/it/lang_main.php | 85 + msd/language/it/lang_restore.php | 19 + msd/language/it/lang_sql.php | 190 + msd/language/lang_list.php | 128 + msd/language/pt_br/help.html | 147 + msd/language/pt_br/lang.php | 109 + msd/language/pt_br/lang_config_overview.php | 128 + msd/language/pt_br/lang_dump.php | 58 + msd/language/pt_br/lang_filemanagement.php | 79 + msd/language/pt_br/lang_help.php | 40 + msd/language/pt_br/lang_install.php | 86 + msd/language/pt_br/lang_log.php | 7 + msd/language/pt_br/lang_main.php | 89 + msd/language/pt_br/lang_restore.php | 19 + msd/language/pt_br/lang_sql.php | 190 + msd/language/sw/help.html | 146 + msd/language/sw/lang.php | 109 + msd/language/sw/lang_config_overview.php | 126 + msd/language/sw/lang_dump.php | 53 + msd/language/sw/lang_filemanagement.php | 75 + msd/language/sw/lang_help.php | 37 + msd/language/sw/lang_install.php | 79 + msd/language/sw/lang_log.php | 7 + msd/language/sw/lang_main.php | 85 + msd/language/sw/lang_restore.php | 19 + msd/language/sw/lang_sql.php | 190 + msd/language/tr/help.html | 147 + msd/language/tr/lang.php | 109 + msd/language/tr/lang_config_overview.php | 123 + msd/language/tr/lang_dump.php | 57 + msd/language/tr/lang_filemanagement.php | 76 + msd/language/tr/lang_help.php | 40 + msd/language/tr/lang_install.php | 85 + msd/language/tr/lang_log.php | 7 + msd/language/tr/lang_main.php | 85 + msd/language/tr/lang_restore.php | 19 + msd/language/tr/lang_sql.php | 190 + msd/language/vn/help.html | 147 + msd/language/vn/lang.php | 112 + msd/language/vn/lang_config_overview.php | 130 + msd/language/vn/lang_dump.php | 59 + msd/language/vn/lang_filemanagement.php | 79 + msd/language/vn/lang_help.php | 40 + msd/language/vn/lang_install.php | 86 + msd/language/vn/lang_log.php | 10 + msd/language/vn/lang_main.php | 89 + msd/language/vn/lang_restore.php | 22 + msd/language/vn/lang_sql.php | 190 + msd/license.txt | 278 + msd/log.php | 165 + msd/main.php | 118 + msd/menu.php | 151 + msd/mod_cron/crondump.pl | 1426 +++++ msd/mod_cron/perltest.pl | 162 + msd/mod_cron/simpletest.pl | 52 + msd/refresh_dblist.php | 43 + msd/restore.php | 340 ++ msd/sql.php | 356 ++ msd/tpl/dump_select_tables.tpl | 58 + msd/tpl/home/databases_list_dbs.tpl | 47 + msd/tpl/home/databases_list_tables.tpl | 109 + msd/tpl/home/headnavi.tpl | 9 + msd/tpl/home/home.tpl | 74 + msd/tpl/home/protection_create.tpl | 146 + msd/tpl/menu/content.tpl | 44 + msd/tpl/menu/footer.tpl | 10 + msd/tpl/menu/header.tpl | 33 + msd/tpl/restore_select_tables.tpl | 65 + msd/tpl/sqlbrowser/mysql_search.tpl | 73 + .../sql_record_insert_inputmask.tpl | 52 + .../sql_record_update_inputmask.tpl | 53 + msd/tpl/sqlbrowser/sqlbox.tpl | 56 + msd/vendor/autoload.php | 25 + msd/vendor/composer/ClassLoader.php | 581 ++ msd/vendor/composer/InstalledVersions.php | 352 ++ msd/vendor/composer/LICENSE | 21 + msd/vendor/composer/autoload_classmap.php | 10 + msd/vendor/composer/autoload_files.php | 10 + msd/vendor/composer/autoload_namespaces.php | 9 + msd/vendor/composer/autoload_psr4.php | 19 + msd/vendor/composer/autoload_real.php | 50 + msd/vendor/composer/autoload_static.php | 103 + msd/vendor/composer/installed.json | 763 +++ msd/vendor/composer/installed.php | 125 + msd/vendor/composer/platform_check.php | 26 + msd/vendor/composer/semver/CHANGELOG.md | 209 + msd/vendor/composer/semver/LICENSE | 19 + msd/vendor/composer/semver/README.md | 98 + msd/vendor/composer/semver/composer.json | 59 + msd/vendor/composer/semver/src/Comparator.php | 113 + .../composer/semver/src/CompilingMatcher.php | 94 + .../composer/semver/src/Constraint/Bound.php | 122 + .../semver/src/Constraint/Constraint.php | 435 ++ .../src/Constraint/ConstraintInterface.php | 75 + .../src/Constraint/MatchAllConstraint.php | 85 + .../src/Constraint/MatchNoneConstraint.php | 83 + .../semver/src/Constraint/MultiConstraint.php | 325 + msd/vendor/composer/semver/src/Interval.php | 98 + msd/vendor/composer/semver/src/Intervals.php | 478 ++ msd/vendor/composer/semver/src/Semver.php | 129 + .../composer/semver/src/VersionParser.php | 586 ++ msd/vendor/desarrolla2/cache/.formatter.yml | 18 + .../cache/.github/workflows/php.yml | 73 + msd/vendor/desarrolla2/cache/.gitignore | 9 + msd/vendor/desarrolla2/cache/.scrutinizer.yml | 31 + msd/vendor/desarrolla2/cache/LICENSE | 19 + msd/vendor/desarrolla2/cache/README.md | 174 + msd/vendor/desarrolla2/cache/build.xml | 100 + msd/vendor/desarrolla2/cache/composer.json | 67 + .../cache/docs/implementations/apcu.md | 25 + .../cache/docs/implementations/chain.md | 37 + .../cache/docs/implementations/file.md | 82 + .../cache/docs/implementations/memcached.md | 28 + .../cache/docs/implementations/memory.md | 23 + .../cache/docs/implementations/mongodb.md | 45 + .../cache/docs/implementations/mysqli.md | 47 + .../cache/docs/implementations/notcache.md | 13 + .../cache/docs/implementations/phpfile.md | 74 + .../cache/docs/implementations/predis.md | 31 + .../desarrolla2/cache/docs/performance.md | 40 + msd/vendor/desarrolla2/cache/phpcs.xml | 8 + msd/vendor/desarrolla2/cache/phpstan.neon | 8 + msd/vendor/desarrolla2/cache/phpunit.xml.dist | 39 + .../desarrolla2/cache/src/AbstractCache.php | 296 + .../desarrolla2/cache/src/AbstractFile.php | 216 + msd/vendor/desarrolla2/cache/src/Apcu.php | 88 + .../desarrolla2/cache/src/CacheInterface.php | 58 + msd/vendor/desarrolla2/cache/src/Chain.php | 192 + .../src/Exception/BadMethodCallException.php | 25 + .../cache/src/Exception/CacheException.php | 25 + .../Exception/InvalidArgumentException.php | 25 + .../Exception/UnexpectedValueException.php | 25 + msd/vendor/desarrolla2/cache/src/File.php | 175 + .../cache/src/File/BasicFilename.php | 68 + .../cache/src/File/TrieFilename.php | 121 + .../desarrolla2/cache/src/Memcached.php | 218 + msd/vendor/desarrolla2/cache/src/Memory.php | 165 + msd/vendor/desarrolla2/cache/src/MongoDB.php | 273 + msd/vendor/desarrolla2/cache/src/Mysqli.php | 312 + msd/vendor/desarrolla2/cache/src/NotCache.php | 93 + .../cache/src/Option/FilenameTrait.php | 91 + .../cache/src/Option/InitializeTrait.php | 65 + .../cache/src/Option/PrefixTrait.php | 49 + .../desarrolla2/cache/src/Option/TtlTrait.php | 54 + .../cache/src/Packer/JsonPacker.php | 69 + .../cache/src/Packer/MongoDBBinaryPacker.php | 77 + .../cache/src/Packer/NopPacker.php | 57 + .../cache/src/Packer/PackerInterface.php | 47 + .../cache/src/Packer/PackingTrait.php | 86 + .../cache/src/Packer/SerializePacker.php | 78 + msd/vendor/desarrolla2/cache/src/PhpFile.php | 111 + msd/vendor/desarrolla2/cache/src/Predis.php | 245 + .../cache/tests/AbstractCacheTest.php | 93 + .../desarrolla2/cache/tests/ApcuCacheTest.php | 44 + .../desarrolla2/cache/tests/ChainTest.php | 216 + .../desarrolla2/cache/tests/FileTest.php | 40 + .../desarrolla2/cache/tests/FileTrieTest.php | 43 + .../cache/tests/FileTtlFileTest.php | 42 + .../desarrolla2/cache/tests/MemcachedTest.php | 47 + .../desarrolla2/cache/tests/MemoryTest.php | 44 + .../desarrolla2/cache/tests/MongoDBTest.php | 53 + .../desarrolla2/cache/tests/MysqliTest.php | 75 + .../desarrolla2/cache/tests/NotCacheTest.php | 88 + .../desarrolla2/cache/tests/PhpFileTest.php | 40 + .../desarrolla2/cache/tests/PredisTest.php | 52 + .../cache/tests/performance/Apc.php | 21 + .../cache/tests/performance/File.php | 21 + .../cache/tests/performance/Mongo.php | 21 + .../cache/tests/performance/NoCache.php | 21 + .../cache/tests/performance/common.php | 43 + .../flysystem-sftp/ConnectionProvider.php | 12 + .../flysystem-sftp/ConnectivityChecker.php | 12 + .../FixatedConnectivityChecker.php | 36 + msd/vendor/league/flysystem-sftp/README.md | 7 + .../league/flysystem-sftp/SftpAdapter.php | 334 + .../flysystem-sftp/SftpConnectionProvider.php | 236 + .../SimpleConnectivityChecker.php | 15 + .../StubSftpConnectionProvider.php | 59 + .../flysystem-sftp/UnableToAuthenticate.php | 26 + .../UnableToConnectToSftpHost.php | 16 + .../UnableToEstablishAuthenticityOfHost.php | 16 + .../flysystem-sftp/UnableToLoadPrivateKey.php | 16 + .../league/flysystem-sftp/composer.json | 23 + msd/vendor/league/flysystem/INFO.md | 2 + msd/vendor/league/flysystem/LICENSE | 19 + msd/vendor/league/flysystem/composer.json | 48 + .../flysystem/config.subsplit-publish.json | 49 + .../league/flysystem/docker-compose.yml | 58 + msd/vendor/league/flysystem/readme.md | 45 + msd/vendor/league/flysystem/src/Config.php | 43 + .../flysystem/src/CorruptedPathDetected.php | 13 + .../flysystem/src/DirectoryAttributes.php | 110 + .../league/flysystem/src/DirectoryListing.php | 84 + .../league/flysystem/src/FileAttributes.php | 139 + .../league/flysystem/src/Filesystem.php | 163 + .../flysystem/src/FilesystemAdapter.php | 108 + .../flysystem/src/FilesystemException.php | 11 + .../src/FilesystemOperationFailed.php | 22 + .../flysystem/src/FilesystemOperator.php | 9 + .../league/flysystem/src/FilesystemReader.php | 66 + .../league/flysystem/src/FilesystemWriter.php | 58 + .../flysystem/src/InvalidStreamProvided.php | 11 + .../src/InvalidVisibilityProvided.php | 20 + .../src/Local/LocalFilesystemAdapter.php | 419 ++ .../league/flysystem/src/MountManager.php | 334 + .../league/flysystem/src/PathNormalizer.php | 10 + .../league/flysystem/src/PathPrefixer.php | 60 + .../flysystem/src/PathTraversalDetected.php | 28 + .../flysystem/src/PortableVisibilityGuard.php | 19 + .../src/ProxyArrayAccessToProperties.php | 62 + .../flysystem/src/StorageAttributes.php | 40 + .../flysystem/src/SymbolicLinkEncountered.php | 28 + .../src/UnableToCheckFileExistence.php | 21 + .../league/flysystem/src/UnableToCopyFile.php | 48 + .../flysystem/src/UnableToCreateDirectory.php | 44 + .../flysystem/src/UnableToDeleteDirectory.php | 48 + .../flysystem/src/UnableToDeleteFile.php | 45 + .../flysystem/src/UnableToMountFilesystem.php | 32 + .../league/flysystem/src/UnableToMoveFile.php | 48 + .../league/flysystem/src/UnableToReadFile.php | 45 + .../src/UnableToResolveFilesystemMount.php | 20 + .../src/UnableToRetrieveMetadata.php | 76 + .../flysystem/src/UnableToSetVisibility.php | 49 + .../flysystem/src/UnableToWriteFile.php | 45 + .../PortableVisibilityConverter.php | 109 + .../UnixVisibility/VisibilityConverter.php | 14 + .../src/UnreadableFileEncountered.php | 28 + .../league/flysystem/src/Visibility.php | 11 + .../src/WhitespacePathNormalizer.php | 49 + .../league/mime-type-detection/CHANGELOG.md | 31 + msd/vendor/league/mime-type-detection/LICENSE | 19 + .../league/mime-type-detection/composer.json | 34 + .../src/EmptyExtensionToMimeTypeMap.php | 13 + .../src/ExtensionMimeTypeDetector.php | 42 + .../src/ExtensionToMimeTypeMap.php | 10 + .../src/FinfoMimeTypeDetector.php | 92 + .../src/GeneratedExtensionToMimeTypeMap.php | 1227 ++++ .../src/MimeTypeDetector.php | 19 + .../src/OverridingExtensionToMimeTypeMap.php | 30 + msd/vendor/monolog/monolog/CHANGELOG.md | 608 ++ msd/vendor/monolog/monolog/LICENSE | 19 + msd/vendor/monolog/monolog/README.md | 112 + msd/vendor/monolog/monolog/UPGRADE.md | 72 + msd/vendor/monolog/monolog/composer.json | 81 + .../Monolog/Attribute/AsMonologProcessor.php | 46 + .../monolog/src/Monolog/DateTimeImmutable.php | 49 + .../monolog/src/Monolog/ErrorHandler.php | 307 + .../Monolog/Formatter/ChromePHPFormatter.php | 83 + .../Monolog/Formatter/ElasticaFormatter.php | 89 + .../Formatter/ElasticsearchFormatter.php | 89 + .../Monolog/Formatter/FlowdockFormatter.php | 111 + .../Monolog/Formatter/FluentdFormatter.php | 88 + .../Monolog/Formatter/FormatterInterface.php | 42 + .../Formatter/GelfMessageFormatter.php | 160 + .../Formatter/GoogleCloudLoggingFormatter.php | 39 + .../src/Monolog/Formatter/HtmlFormatter.php | 142 + .../src/Monolog/Formatter/JsonFormatter.php | 224 + .../src/Monolog/Formatter/LineFormatter.php | 246 + .../src/Monolog/Formatter/LogglyFormatter.php | 45 + .../Monolog/Formatter/LogmaticFormatter.php | 66 + .../Monolog/Formatter/LogstashFormatter.php | 101 + .../Monolog/Formatter/MongoDBFormatter.php | 162 + .../Monolog/Formatter/NormalizerFormatter.php | 287 + .../src/Monolog/Formatter/ScalarFormatter.php | 51 + .../Monolog/Formatter/WildfireFormatter.php | 139 + .../src/Monolog/Handler/AbstractHandler.php | 112 + .../Handler/AbstractProcessingHandler.php | 69 + .../Monolog/Handler/AbstractSyslogHandler.php | 106 + .../src/Monolog/Handler/AmqpHandler.php | 170 + .../Monolog/Handler/BrowserConsoleHandler.php | 293 + .../src/Monolog/Handler/BufferHandler.php | 167 + .../src/Monolog/Handler/ChromePHPHandler.php | 196 + .../src/Monolog/Handler/CouchDBHandler.php | 77 + .../src/Monolog/Handler/CubeHandler.php | 167 + .../monolog/src/Monolog/Handler/Curl/Util.php | 71 + .../Monolog/Handler/DeduplicationHandler.php | 186 + .../Handler/DoctrineCouchDBHandler.php | 47 + .../src/Monolog/Handler/DynamoDbHandler.php | 104 + .../src/Monolog/Handler/ElasticaHandler.php | 129 + .../Monolog/Handler/ElasticsearchHandler.php | 218 + .../src/Monolog/Handler/ErrorLogHandler.php | 91 + .../Monolog/Handler/FallbackGroupHandler.php | 71 + .../src/Monolog/Handler/FilterHandler.php | 212 + .../ActivationStrategyInterface.php | 29 + .../ChannelLevelActivationStrategy.php | 77 + .../ErrorLevelActivationStrategy.php | 46 + .../Monolog/Handler/FingersCrossedHandler.php | 252 + .../src/Monolog/Handler/FirePHPHandler.php | 180 + .../src/Monolog/Handler/FleepHookHandler.php | 135 + .../src/Monolog/Handler/FlowdockHandler.php | 132 + .../Handler/FormattableHandlerInterface.php | 37 + .../Handler/FormattableHandlerTrait.php | 60 + .../src/Monolog/Handler/GelfHandler.php | 57 + .../src/Monolog/Handler/GroupHandler.php | 132 + .../monolog/src/Monolog/Handler/Handler.php | 62 + .../src/Monolog/Handler/HandlerInterface.php | 85 + .../src/Monolog/Handler/HandlerWrapper.php | 136 + .../src/Monolog/Handler/IFTTTHandler.php | 74 + .../src/Monolog/Handler/InsightOpsHandler.php | 76 + .../src/Monolog/Handler/LogEntriesHandler.php | 70 + .../src/Monolog/Handler/LogglyHandler.php | 160 + .../src/Monolog/Handler/LogmaticHandler.php | 106 + .../src/Monolog/Handler/MailHandler.php | 95 + .../src/Monolog/Handler/MandrillHandler.php | 83 + .../Handler/MissingExtensionException.php | 21 + .../src/Monolog/Handler/MongoDBHandler.php | 86 + .../Monolog/Handler/NativeMailerHandler.php | 174 + .../src/Monolog/Handler/NewRelicHandler.php | 199 + .../src/Monolog/Handler/NoopHandler.php | 40 + .../src/Monolog/Handler/NullHandler.php | 60 + .../src/Monolog/Handler/OverflowHandler.php | 149 + .../src/Monolog/Handler/PHPConsoleHandler.php | 263 + .../src/Monolog/Handler/ProcessHandler.php | 191 + .../Handler/ProcessableHandlerInterface.php | 44 + .../Handler/ProcessableHandlerTrait.php | 77 + .../src/Monolog/Handler/PsrHandler.php | 95 + .../src/Monolog/Handler/PushoverHandler.php | 246 + .../src/Monolog/Handler/RedisHandler.php | 101 + .../Monolog/Handler/RedisPubSubHandler.php | 67 + .../src/Monolog/Handler/RollbarHandler.php | 131 + .../Monolog/Handler/RotatingFileHandler.php | 207 + .../src/Monolog/Handler/SamplingHandler.php | 132 + .../src/Monolog/Handler/SendGridHandler.php | 102 + .../src/Monolog/Handler/Slack/SlackRecord.php | 387 ++ .../src/Monolog/Handler/SlackHandler.php | 256 + .../Monolog/Handler/SlackWebhookHandler.php | 130 + .../src/Monolog/Handler/SocketHandler.php | 448 ++ .../src/Monolog/Handler/SqsHandler.php | 62 + .../src/Monolog/Handler/StreamHandler.php | 221 + .../Monolog/Handler/SwiftMailerHandler.php | 115 + .../Monolog/Handler/SymfonyMailerHandler.php | 111 + .../src/Monolog/Handler/SyslogHandler.php | 68 + .../Monolog/Handler/SyslogUdp/UdpSocket.php | 88 + .../src/Monolog/Handler/SyslogUdpHandler.php | 150 + .../Monolog/Handler/TelegramBotHandler.php | 274 + .../src/Monolog/Handler/TestHandler.php | 231 + .../Handler/WebRequestRecognizerTrait.php | 24 + .../Handler/WhatFailureGroupHandler.php | 67 + .../Monolog/Handler/ZendMonitorHandler.php | 101 + .../monolog/monolog/src/Monolog/LogRecord.php | 34 + .../monolog/monolog/src/Monolog/Logger.php | 701 +++ .../src/Monolog/Processor/GitProcessor.php | 77 + .../Monolog/Processor/HostnameProcessor.php | 36 + .../Processor/IntrospectionProcessor.php | 123 + .../Processor/MemoryPeakUsageProcessor.php | 37 + .../src/Monolog/Processor/MemoryProcessor.php | 61 + .../Processor/MemoryUsageProcessor.php | 37 + .../Monolog/Processor/MercurialProcessor.php | 77 + .../Monolog/Processor/ProcessIdProcessor.php | 30 + .../Monolog/Processor/ProcessorInterface.php | 30 + .../Processor/PsrLogMessageProcessor.php | 86 + .../src/Monolog/Processor/TagProcessor.php | 61 + .../src/Monolog/Processor/UidProcessor.php | 59 + .../src/Monolog/Processor/WebProcessor.php | 111 + .../monolog/monolog/src/Monolog/Registry.php | 134 + .../src/Monolog/ResettableInterface.php | 34 + .../monolog/src/Monolog/SignalHandler.php | 120 + .../monolog/src/Monolog/Test/TestCase.php | 85 + .../monolog/monolog/src/Monolog/Utils.php | 284 + msd/vendor/phpseclib/phpseclib/AUTHORS | 7 + msd/vendor/phpseclib/phpseclib/BACKERS.md | 14 + msd/vendor/phpseclib/phpseclib/LICENSE | 20 + msd/vendor/phpseclib/phpseclib/README.md | 101 + msd/vendor/phpseclib/phpseclib/appveyor.yml | 27 + msd/vendor/phpseclib/phpseclib/composer.json | 76 + .../phpseclib/phpseclib/Crypt/AES.php | 126 + .../phpseclib/phpseclib/Crypt/Base.php | 2907 +++++++++ .../phpseclib/phpseclib/Crypt/Blowfish.php | 992 +++ .../phpseclib/phpseclib/Crypt/DES.php | 1449 +++++ .../phpseclib/phpseclib/Crypt/Hash.php | 893 +++ .../phpseclib/phpseclib/Crypt/RC2.php | 694 +++ .../phpseclib/phpseclib/Crypt/RC4.php | 348 ++ .../phpseclib/phpseclib/Crypt/RSA.php | 3342 ++++++++++ .../phpseclib/phpseclib/Crypt/Random.php | 280 + .../phpseclib/phpseclib/Crypt/Rijndael.php | 941 +++ .../phpseclib/phpseclib/Crypt/TripleDES.php | 460 ++ .../phpseclib/phpseclib/Crypt/Twofish.php | 852 +++ .../phpseclib/phpseclib/File/ANSI.php | 577 ++ .../phpseclib/phpseclib/File/ASN1.php | 1469 +++++ .../phpseclib/phpseclib/File/ASN1/Element.php | 47 + .../phpseclib/phpseclib/File/X509.php | 5102 ++++++++++++++++ .../phpseclib/phpseclib/Math/BigInteger.php | 3787 ++++++++++++ .../phpseclib/phpseclib/phpseclib/Net/SCP.php | 349 ++ .../phpseclib/phpseclib/Net/SFTP.php | 3901 ++++++++++++ .../phpseclib/phpseclib/Net/SFTP/Stream.php | 796 +++ .../phpseclib/phpseclib/Net/SSH1.php | 1662 +++++ .../phpseclib/phpseclib/Net/SSH2.php | 5423 +++++++++++++++++ .../phpseclib/phpseclib/System/SSH/Agent.php | 361 ++ .../phpseclib/System/SSH/Agent/Identity.php | 241 + .../phpseclib/phpseclib/bootstrap.php | 17 + .../phpseclib/phpseclib/phpseclib/openssl.cnf | 6 + msd/vendor/psr/log/LICENSE | 19 + msd/vendor/psr/log/Psr/Log/AbstractLogger.php | 128 + .../log/Psr/Log/InvalidArgumentException.php | 7 + msd/vendor/psr/log/Psr/Log/LogLevel.php | 18 + .../psr/log/Psr/Log/LoggerAwareInterface.php | 18 + .../psr/log/Psr/Log/LoggerAwareTrait.php | 26 + .../psr/log/Psr/Log/LoggerInterface.php | 125 + msd/vendor/psr/log/Psr/Log/LoggerTrait.php | 142 + msd/vendor/psr/log/Psr/Log/NullLogger.php | 30 + msd/vendor/psr/log/Psr/Log/Test/DummyTest.php | 18 + .../log/Psr/Log/Test/LoggerInterfaceTest.php | 138 + .../psr/log/Psr/Log/Test/TestLogger.php | 147 + msd/vendor/psr/log/README.md | 58 + msd/vendor/psr/log/composer.json | 26 + .../psr/simple-cache/src/CacheException.php | 10 + .../psr/simple-cache/src/CacheInterface.php | 114 + .../src/InvalidArgumentException.php | 13 + .../php-auto-update/.editorconfig | 11 + .../.github/workflows/phpunit.yml | 39 + .../visualappeal/php-auto-update/.gitignore | 5 + .../visualappeal/php-auto-update/Dockerfile | 12 + .../visualappeal/php-auto-update/LICENSE.md | 7 + .../visualappeal/php-auto-update/README.md | 29 + .../visualappeal/php-auto-update/SECURITY.md | 16 + .../php-auto-update/composer.json | 37 + .../php-auto-update/docker-compose.yaml | 7 + .../example/client/files/file1.php | 1 + .../example/client/files/file2.php | 1 + .../php-auto-update/example/client/index.php | 19 + .../example/client/somefile.php | 1 + .../example/client/update/.gitignore | 2 + .../php-auto-update/src/AutoUpdate.php | 1019 ++++ .../src/Exceptions/DownloadException.php | 5 + .../src/Exceptions/ParserException.php | 5 + .../php-auto-update/tests/AutoUpdateTest.php | 181 + .../php-auto-update/tests/UnitTests.xml | 7 + .../tests/fixtures/noUpdateAvailable.ini | 8 + .../tests/fixtures/noUpdateAvailable.json | 5 + .../fixtures/noUpdateAvailable.json.master | 5 + .../tests/fixtures/updateAvailable.ini | 8 + .../tests/fixtures/updateAvailable.json | 5 + .../tests/fixtures/updateAvailable.json.dev | 5 + 680 files changed, 103120 insertions(+) create mode 100644 msd/.gitignore create mode 100644 msd/.htaccess create mode 100644 msd/.htpasswd create mode 100644 msd/README.md create mode 100644 msd/ReadMe/ReadMe.txt create mode 100644 msd/ReadMe/license_english.txt create mode 100644 msd/composer.json create mode 100644 msd/composer.lock create mode 100644 msd/config.php create mode 100644 msd/config_overview.php create mode 100644 msd/css/mod/icons/arrow_down.gif create mode 100644 msd/css/mod/icons/arrow_up.gif create mode 100644 msd/css/mod/icons/arrowdown.gif create mode 100644 msd/css/mod/icons/arrowleft.gif create mode 100644 msd/css/mod/icons/arrowup.gif create mode 100644 msd/css/mod/icons/blank.gif create mode 100644 msd/css/mod/icons/browse.gif create mode 100644 msd/css/mod/icons/close.gif create mode 100644 msd/css/mod/icons/delete.gif create mode 100644 msd/css/mod/icons/edit.gif create mode 100644 msd/css/mod/icons/gz.gif create mode 100644 msd/css/mod/icons/index.gif create mode 100644 msd/css/mod/icons/key_fulltext.gif create mode 100644 msd/css/mod/icons/key_nokey.gif create mode 100644 msd/css/mod/icons/key_primary.gif create mode 100644 msd/css/mod/icons/key_unique.gif create mode 100644 msd/css/mod/icons/mysql_help.gif create mode 100644 msd/css/mod/icons/notok.gif create mode 100644 msd/css/mod/icons/ok.gif create mode 100644 msd/css/mod/icons/openfile.gif create mode 100644 msd/css/mod/icons/progressbar_dump.gif create mode 100644 msd/css/mod/icons/progressbar_restore.gif create mode 100644 msd/css/mod/icons/progressbar_speed.gif create mode 100644 msd/css/mod/icons/rename.gif create mode 100644 msd/css/mod/icons/search.gif create mode 100644 msd/css/mod/icons/table_truncate.gif create mode 100644 msd/css/mod/icons/table_truncate_reset.gif create mode 100644 msd/css/mod/pics/bg-body.gif create mode 100644 msd/css/mod/pics/bg-buttons.gif create mode 100644 msd/css/mod/pics/bg-headings.gif create mode 100644 msd/css/mod/pics/blank.gif create mode 100644 msd/css/mod/pics/h1_logo.gif create mode 100644 msd/css/mod/pics/loveyourdata.gif create mode 100644 msd/css/mod/pics/navi_bg.jpg create mode 100644 msd/css/mod/style.css create mode 100644 msd/css/mod_green/icons/arrow_down.gif create mode 100644 msd/css/mod_green/icons/arrow_up.gif create mode 100644 msd/css/mod_green/icons/arrowdown.gif create mode 100644 msd/css/mod_green/icons/arrowleft.gif create mode 100644 msd/css/mod_green/icons/arrowup.gif create mode 100644 msd/css/mod_green/icons/blank.gif create mode 100644 msd/css/mod_green/icons/browse.gif create mode 100644 msd/css/mod_green/icons/close.gif create mode 100644 msd/css/mod_green/icons/delete.gif create mode 100644 msd/css/mod_green/icons/edit.gif create mode 100644 msd/css/mod_green/icons/gz.gif create mode 100644 msd/css/mod_green/icons/index.gif create mode 100644 msd/css/mod_green/icons/key_fulltext.gif create mode 100644 msd/css/mod_green/icons/key_nokey.gif create mode 100644 msd/css/mod_green/icons/key_primary.gif create mode 100644 msd/css/mod_green/icons/key_unique.gif create mode 100644 msd/css/mod_green/icons/mysql_help.gif create mode 100644 msd/css/mod_green/icons/nokey.gif create mode 100644 msd/css/mod_green/icons/notok.gif create mode 100644 msd/css/mod_green/icons/ok.gif create mode 100644 msd/css/mod_green/icons/openfile.gif create mode 100644 msd/css/mod_green/icons/progressbar_dump.gif create mode 100644 msd/css/mod_green/icons/progressbar_restore.gif create mode 100644 msd/css/mod_green/icons/progressbar_speed.gif create mode 100644 msd/css/mod_green/icons/rename.gif create mode 100644 msd/css/mod_green/icons/search.gif create mode 100644 msd/css/mod_green/icons/table_truncate.gif create mode 100644 msd/css/mod_green/icons/table_truncate_reset.gif create mode 100644 msd/css/mod_green/pics/body_bg.gif create mode 100644 msd/css/mod_green/pics/h1_logo.gif create mode 100644 msd/css/mod_green/pics/loveyourdata.gif create mode 100644 msd/css/mod_green/pics/mainnavi.gif create mode 100644 msd/css/mod_green/pics/navi_bg.jpg create mode 100644 msd/css/mod_green/pics/overall_bg.gif create mode 100644 msd/css/mod_green/pics/pagetitle.gif create mode 100644 msd/dump.php create mode 100644 msd/filemanagement.php create mode 100644 msd/help.php create mode 100644 msd/images/qrcode.png create mode 100644 msd/inc/define_icons.php create mode 100644 msd/inc/functions.php create mode 100644 msd/inc/functions_dump.php create mode 100644 msd/inc/functions_files.php create mode 100644 msd/inc/functions_global.php create mode 100644 msd/inc/functions_imexport.php create mode 100644 msd/inc/functions_restore.php create mode 100644 msd/inc/functions_sql.php create mode 100644 msd/inc/header.php create mode 100644 msd/inc/home/apr1_md5/apr1_md5.php create mode 100644 msd/inc/home/apr1_md5/license.txt create mode 100644 msd/inc/home/apr1_md5/origin_url.txt create mode 100644 msd/inc/home/databases.php create mode 100644 msd/inc/home/home.php create mode 100644 msd/inc/home/mysql_variables.php create mode 100644 msd/inc/home/protection_create.php create mode 100644 msd/inc/home/protection_delete.php create mode 100644 msd/inc/home/protection_edit.php create mode 100644 msd/inc/home/system.php create mode 100644 msd/inc/home/update.php create mode 100644 msd/inc/mysqli.php create mode 100644 msd/inc/runtime.php create mode 100644 msd/inc/sql_importexport.php create mode 100644 msd/inc/sql_tools.php create mode 100644 msd/inc/sqlbrowser/mysql_search.php create mode 100644 msd/inc/sqlbrowser/sql_commands.php create mode 100644 msd/inc/sqlbrowser/sql_dataview.php create mode 100644 msd/inc/sqlbrowser/sql_record_insert_inputmask.php create mode 100644 msd/inc/sqlbrowser/sql_record_update_inputmask.php create mode 100644 msd/inc/sqlbrowser/sql_tables.php create mode 100644 msd/inc/sqlbrowser/sqlbox.php create mode 100644 msd/inc/sqllib.php create mode 100644 msd/inc/table_query.php create mode 100644 msd/inc/template.php create mode 100644 msd/index.php create mode 100644 msd/install.php create mode 100644 msd/js/script.js create mode 100644 msd/language/ar/help.html create mode 100644 msd/language/ar/lang.php create mode 100644 msd/language/ar/lang_config_overview.php create mode 100644 msd/language/ar/lang_dump.php create mode 100644 msd/language/ar/lang_filemanagement.php create mode 100644 msd/language/ar/lang_help.php create mode 100644 msd/language/ar/lang_install.php create mode 100644 msd/language/ar/lang_log.php create mode 100644 msd/language/ar/lang_main.php create mode 100644 msd/language/ar/lang_restore.php create mode 100644 msd/language/ar/lang_sql.php create mode 100644 msd/language/ch/help.html create mode 100644 msd/language/ch/lang.php create mode 100644 msd/language/ch/lang_config_overview.php create mode 100644 msd/language/ch/lang_dump.php create mode 100644 msd/language/ch/lang_filemanagement.php create mode 100644 msd/language/ch/lang_help.php create mode 100644 msd/language/ch/lang_install.php create mode 100644 msd/language/ch/lang_log.php create mode 100644 msd/language/ch/lang_main.php create mode 100644 msd/language/ch/lang_restore.php create mode 100644 msd/language/ch/lang_sql.php create mode 100644 msd/language/da/help.html create mode 100644 msd/language/da/lang.php create mode 100644 msd/language/da/lang_config_overview.php create mode 100644 msd/language/da/lang_dump.php create mode 100644 msd/language/da/lang_filemanagement.php create mode 100644 msd/language/da/lang_help.php create mode 100644 msd/language/da/lang_install.php create mode 100644 msd/language/da/lang_log.php create mode 100644 msd/language/da/lang_main.php create mode 100644 msd/language/da/lang_restore.php create mode 100644 msd/language/da/lang_sql.php create mode 100644 msd/language/de/help.html create mode 100644 msd/language/de/lang.php create mode 100644 msd/language/de/lang_config_overview.php create mode 100644 msd/language/de/lang_dump.php create mode 100644 msd/language/de/lang_filemanagement.php create mode 100644 msd/language/de/lang_help.php create mode 100644 msd/language/de/lang_install.php create mode 100644 msd/language/de/lang_log.php create mode 100644 msd/language/de/lang_main.php create mode 100644 msd/language/de/lang_restore.php create mode 100644 msd/language/de/lang_sql.php create mode 100644 msd/language/de_du/help.html create mode 100644 msd/language/de_du/lang.php create mode 100644 msd/language/de_du/lang_config_overview.php create mode 100644 msd/language/de_du/lang_dump.php create mode 100644 msd/language/de_du/lang_filemanagement.php create mode 100644 msd/language/de_du/lang_help.php create mode 100644 msd/language/de_du/lang_install.php create mode 100644 msd/language/de_du/lang_log.php create mode 100644 msd/language/de_du/lang_main.php create mode 100644 msd/language/de_du/lang_restore.php create mode 100644 msd/language/de_du/lang_sql.php create mode 100644 msd/language/el/help.html create mode 100644 msd/language/el/lang.php create mode 100644 msd/language/el/lang_config_overview.php create mode 100644 msd/language/el/lang_dump.php create mode 100644 msd/language/el/lang_filemanagement.php create mode 100644 msd/language/el/lang_help.php create mode 100644 msd/language/el/lang_install.php create mode 100644 msd/language/el/lang_log.php create mode 100644 msd/language/el/lang_main.php create mode 100644 msd/language/el/lang_restore.php create mode 100644 msd/language/el/lang_sql.php create mode 100644 msd/language/en/help.html create mode 100644 msd/language/en/lang.php create mode 100644 msd/language/en/lang_config_overview.php create mode 100644 msd/language/en/lang_dump.php create mode 100644 msd/language/en/lang_filemanagement.php create mode 100644 msd/language/en/lang_help.php create mode 100644 msd/language/en/lang_install.php create mode 100644 msd/language/en/lang_log.php create mode 100644 msd/language/en/lang_main.php create mode 100644 msd/language/en/lang_restore.php create mode 100644 msd/language/en/lang_sql.php create mode 100644 msd/language/es/help.html create mode 100644 msd/language/es/lang.php create mode 100644 msd/language/es/lang_config_overview.php create mode 100644 msd/language/es/lang_dump.php create mode 100644 msd/language/es/lang_filemanagement.php create mode 100644 msd/language/es/lang_help.php create mode 100644 msd/language/es/lang_install.php create mode 100644 msd/language/es/lang_log.php create mode 100644 msd/language/es/lang_main.php create mode 100644 msd/language/es/lang_restore.php create mode 100644 msd/language/es/lang_sql.php create mode 100644 msd/language/flags/ar.gif create mode 100644 msd/language/flags/ch.gif create mode 100644 msd/language/flags/da.gif create mode 100644 msd/language/flags/de.gif create mode 100644 msd/language/flags/de_du.gif create mode 100644 msd/language/flags/el.gif create mode 100644 msd/language/flags/en.gif create mode 100644 msd/language/flags/es.gif create mode 100644 msd/language/flags/fr.gif create mode 100644 msd/language/flags/index.htm create mode 100644 msd/language/flags/it.gif create mode 100644 msd/language/flags/lu.gif create mode 100644 msd/language/flags/nl.gif create mode 100644 msd/language/flags/pl.gif create mode 100644 msd/language/flags/pt.gif create mode 100644 msd/language/flags/pt_br.gif create mode 100644 msd/language/flags/sw.gif create mode 100644 msd/language/flags/tr.gif create mode 100644 msd/language/flags/vn.gif create mode 100644 msd/language/fr/help.html create mode 100644 msd/language/fr/lang.php create mode 100644 msd/language/fr/lang_config_overview.php create mode 100644 msd/language/fr/lang_dump.php create mode 100644 msd/language/fr/lang_filemanagement.php create mode 100644 msd/language/fr/lang_help.php create mode 100644 msd/language/fr/lang_install.php create mode 100644 msd/language/fr/lang_log.php create mode 100644 msd/language/fr/lang_main.php create mode 100644 msd/language/fr/lang_restore.php create mode 100644 msd/language/fr/lang_sql.php create mode 100644 msd/language/it/help.html create mode 100644 msd/language/it/lang.php create mode 100644 msd/language/it/lang_config_overview.php create mode 100644 msd/language/it/lang_dump.php create mode 100644 msd/language/it/lang_filemanagement.php create mode 100644 msd/language/it/lang_help.php create mode 100644 msd/language/it/lang_install.php create mode 100644 msd/language/it/lang_log.php create mode 100644 msd/language/it/lang_main.php create mode 100644 msd/language/it/lang_restore.php create mode 100644 msd/language/it/lang_sql.php create mode 100644 msd/language/lang_list.php create mode 100644 msd/language/pt_br/help.html create mode 100644 msd/language/pt_br/lang.php create mode 100644 msd/language/pt_br/lang_config_overview.php create mode 100644 msd/language/pt_br/lang_dump.php create mode 100644 msd/language/pt_br/lang_filemanagement.php create mode 100644 msd/language/pt_br/lang_help.php create mode 100644 msd/language/pt_br/lang_install.php create mode 100644 msd/language/pt_br/lang_log.php create mode 100644 msd/language/pt_br/lang_main.php create mode 100644 msd/language/pt_br/lang_restore.php create mode 100644 msd/language/pt_br/lang_sql.php create mode 100644 msd/language/sw/help.html create mode 100644 msd/language/sw/lang.php create mode 100644 msd/language/sw/lang_config_overview.php create mode 100644 msd/language/sw/lang_dump.php create mode 100644 msd/language/sw/lang_filemanagement.php create mode 100644 msd/language/sw/lang_help.php create mode 100644 msd/language/sw/lang_install.php create mode 100644 msd/language/sw/lang_log.php create mode 100644 msd/language/sw/lang_main.php create mode 100644 msd/language/sw/lang_restore.php create mode 100644 msd/language/sw/lang_sql.php create mode 100644 msd/language/tr/help.html create mode 100644 msd/language/tr/lang.php create mode 100644 msd/language/tr/lang_config_overview.php create mode 100644 msd/language/tr/lang_dump.php create mode 100644 msd/language/tr/lang_filemanagement.php create mode 100644 msd/language/tr/lang_help.php create mode 100644 msd/language/tr/lang_install.php create mode 100644 msd/language/tr/lang_log.php create mode 100644 msd/language/tr/lang_main.php create mode 100644 msd/language/tr/lang_restore.php create mode 100644 msd/language/tr/lang_sql.php create mode 100644 msd/language/vn/help.html create mode 100644 msd/language/vn/lang.php create mode 100644 msd/language/vn/lang_config_overview.php create mode 100644 msd/language/vn/lang_dump.php create mode 100644 msd/language/vn/lang_filemanagement.php create mode 100644 msd/language/vn/lang_help.php create mode 100644 msd/language/vn/lang_install.php create mode 100644 msd/language/vn/lang_log.php create mode 100644 msd/language/vn/lang_main.php create mode 100644 msd/language/vn/lang_restore.php create mode 100644 msd/language/vn/lang_sql.php create mode 100644 msd/license.txt create mode 100644 msd/log.php create mode 100644 msd/main.php create mode 100644 msd/menu.php create mode 100644 msd/mod_cron/crondump.pl create mode 100644 msd/mod_cron/perltest.pl create mode 100644 msd/mod_cron/simpletest.pl create mode 100644 msd/refresh_dblist.php create mode 100644 msd/restore.php create mode 100644 msd/sql.php create mode 100644 msd/tpl/dump_select_tables.tpl create mode 100644 msd/tpl/home/databases_list_dbs.tpl create mode 100644 msd/tpl/home/databases_list_tables.tpl create mode 100644 msd/tpl/home/headnavi.tpl create mode 100644 msd/tpl/home/home.tpl create mode 100644 msd/tpl/home/protection_create.tpl create mode 100644 msd/tpl/menu/content.tpl create mode 100644 msd/tpl/menu/footer.tpl create mode 100644 msd/tpl/menu/header.tpl create mode 100644 msd/tpl/restore_select_tables.tpl create mode 100644 msd/tpl/sqlbrowser/mysql_search.tpl create mode 100644 msd/tpl/sqlbrowser/sql_record_insert_inputmask.tpl create mode 100644 msd/tpl/sqlbrowser/sql_record_update_inputmask.tpl create mode 100644 msd/tpl/sqlbrowser/sqlbox.tpl create mode 100644 msd/vendor/autoload.php create mode 100644 msd/vendor/composer/ClassLoader.php create mode 100644 msd/vendor/composer/InstalledVersions.php create mode 100644 msd/vendor/composer/LICENSE create mode 100644 msd/vendor/composer/autoload_classmap.php create mode 100644 msd/vendor/composer/autoload_files.php create mode 100644 msd/vendor/composer/autoload_namespaces.php create mode 100644 msd/vendor/composer/autoload_psr4.php create mode 100644 msd/vendor/composer/autoload_real.php create mode 100644 msd/vendor/composer/autoload_static.php create mode 100644 msd/vendor/composer/installed.json create mode 100644 msd/vendor/composer/installed.php create mode 100644 msd/vendor/composer/platform_check.php create mode 100644 msd/vendor/composer/semver/CHANGELOG.md create mode 100644 msd/vendor/composer/semver/LICENSE create mode 100644 msd/vendor/composer/semver/README.md create mode 100644 msd/vendor/composer/semver/composer.json create mode 100644 msd/vendor/composer/semver/src/Comparator.php create mode 100644 msd/vendor/composer/semver/src/CompilingMatcher.php create mode 100644 msd/vendor/composer/semver/src/Constraint/Bound.php create mode 100644 msd/vendor/composer/semver/src/Constraint/Constraint.php create mode 100644 msd/vendor/composer/semver/src/Constraint/ConstraintInterface.php create mode 100644 msd/vendor/composer/semver/src/Constraint/MatchAllConstraint.php create mode 100644 msd/vendor/composer/semver/src/Constraint/MatchNoneConstraint.php create mode 100644 msd/vendor/composer/semver/src/Constraint/MultiConstraint.php create mode 100644 msd/vendor/composer/semver/src/Interval.php create mode 100644 msd/vendor/composer/semver/src/Intervals.php create mode 100644 msd/vendor/composer/semver/src/Semver.php create mode 100644 msd/vendor/composer/semver/src/VersionParser.php create mode 100644 msd/vendor/desarrolla2/cache/.formatter.yml create mode 100644 msd/vendor/desarrolla2/cache/.github/workflows/php.yml create mode 100644 msd/vendor/desarrolla2/cache/.gitignore create mode 100644 msd/vendor/desarrolla2/cache/.scrutinizer.yml create mode 100644 msd/vendor/desarrolla2/cache/LICENSE create mode 100644 msd/vendor/desarrolla2/cache/README.md create mode 100644 msd/vendor/desarrolla2/cache/build.xml create mode 100644 msd/vendor/desarrolla2/cache/composer.json create mode 100644 msd/vendor/desarrolla2/cache/docs/implementations/apcu.md create mode 100644 msd/vendor/desarrolla2/cache/docs/implementations/chain.md create mode 100644 msd/vendor/desarrolla2/cache/docs/implementations/file.md create mode 100644 msd/vendor/desarrolla2/cache/docs/implementations/memcached.md create mode 100644 msd/vendor/desarrolla2/cache/docs/implementations/memory.md create mode 100644 msd/vendor/desarrolla2/cache/docs/implementations/mongodb.md create mode 100644 msd/vendor/desarrolla2/cache/docs/implementations/mysqli.md create mode 100644 msd/vendor/desarrolla2/cache/docs/implementations/notcache.md create mode 100644 msd/vendor/desarrolla2/cache/docs/implementations/phpfile.md create mode 100644 msd/vendor/desarrolla2/cache/docs/implementations/predis.md create mode 100644 msd/vendor/desarrolla2/cache/docs/performance.md create mode 100644 msd/vendor/desarrolla2/cache/phpcs.xml create mode 100644 msd/vendor/desarrolla2/cache/phpstan.neon create mode 100644 msd/vendor/desarrolla2/cache/phpunit.xml.dist create mode 100644 msd/vendor/desarrolla2/cache/src/AbstractCache.php create mode 100644 msd/vendor/desarrolla2/cache/src/AbstractFile.php create mode 100644 msd/vendor/desarrolla2/cache/src/Apcu.php create mode 100644 msd/vendor/desarrolla2/cache/src/CacheInterface.php create mode 100644 msd/vendor/desarrolla2/cache/src/Chain.php create mode 100644 msd/vendor/desarrolla2/cache/src/Exception/BadMethodCallException.php create mode 100644 msd/vendor/desarrolla2/cache/src/Exception/CacheException.php create mode 100644 msd/vendor/desarrolla2/cache/src/Exception/InvalidArgumentException.php create mode 100644 msd/vendor/desarrolla2/cache/src/Exception/UnexpectedValueException.php create mode 100644 msd/vendor/desarrolla2/cache/src/File.php create mode 100644 msd/vendor/desarrolla2/cache/src/File/BasicFilename.php create mode 100644 msd/vendor/desarrolla2/cache/src/File/TrieFilename.php create mode 100644 msd/vendor/desarrolla2/cache/src/Memcached.php create mode 100644 msd/vendor/desarrolla2/cache/src/Memory.php create mode 100644 msd/vendor/desarrolla2/cache/src/MongoDB.php create mode 100644 msd/vendor/desarrolla2/cache/src/Mysqli.php create mode 100644 msd/vendor/desarrolla2/cache/src/NotCache.php create mode 100644 msd/vendor/desarrolla2/cache/src/Option/FilenameTrait.php create mode 100644 msd/vendor/desarrolla2/cache/src/Option/InitializeTrait.php create mode 100644 msd/vendor/desarrolla2/cache/src/Option/PrefixTrait.php create mode 100644 msd/vendor/desarrolla2/cache/src/Option/TtlTrait.php create mode 100644 msd/vendor/desarrolla2/cache/src/Packer/JsonPacker.php create mode 100644 msd/vendor/desarrolla2/cache/src/Packer/MongoDBBinaryPacker.php create mode 100644 msd/vendor/desarrolla2/cache/src/Packer/NopPacker.php create mode 100644 msd/vendor/desarrolla2/cache/src/Packer/PackerInterface.php create mode 100644 msd/vendor/desarrolla2/cache/src/Packer/PackingTrait.php create mode 100644 msd/vendor/desarrolla2/cache/src/Packer/SerializePacker.php create mode 100644 msd/vendor/desarrolla2/cache/src/PhpFile.php create mode 100644 msd/vendor/desarrolla2/cache/src/Predis.php create mode 100644 msd/vendor/desarrolla2/cache/tests/AbstractCacheTest.php create mode 100644 msd/vendor/desarrolla2/cache/tests/ApcuCacheTest.php create mode 100644 msd/vendor/desarrolla2/cache/tests/ChainTest.php create mode 100644 msd/vendor/desarrolla2/cache/tests/FileTest.php create mode 100644 msd/vendor/desarrolla2/cache/tests/FileTrieTest.php create mode 100644 msd/vendor/desarrolla2/cache/tests/FileTtlFileTest.php create mode 100644 msd/vendor/desarrolla2/cache/tests/MemcachedTest.php create mode 100644 msd/vendor/desarrolla2/cache/tests/MemoryTest.php create mode 100644 msd/vendor/desarrolla2/cache/tests/MongoDBTest.php create mode 100644 msd/vendor/desarrolla2/cache/tests/MysqliTest.php create mode 100644 msd/vendor/desarrolla2/cache/tests/NotCacheTest.php create mode 100644 msd/vendor/desarrolla2/cache/tests/PhpFileTest.php create mode 100644 msd/vendor/desarrolla2/cache/tests/PredisTest.php create mode 100644 msd/vendor/desarrolla2/cache/tests/performance/Apc.php create mode 100644 msd/vendor/desarrolla2/cache/tests/performance/File.php create mode 100644 msd/vendor/desarrolla2/cache/tests/performance/Mongo.php create mode 100644 msd/vendor/desarrolla2/cache/tests/performance/NoCache.php create mode 100644 msd/vendor/desarrolla2/cache/tests/performance/common.php create mode 100644 msd/vendor/league/flysystem-sftp/ConnectionProvider.php create mode 100644 msd/vendor/league/flysystem-sftp/ConnectivityChecker.php create mode 100644 msd/vendor/league/flysystem-sftp/FixatedConnectivityChecker.php create mode 100644 msd/vendor/league/flysystem-sftp/README.md create mode 100644 msd/vendor/league/flysystem-sftp/SftpAdapter.php create mode 100644 msd/vendor/league/flysystem-sftp/SftpConnectionProvider.php create mode 100644 msd/vendor/league/flysystem-sftp/SimpleConnectivityChecker.php create mode 100644 msd/vendor/league/flysystem-sftp/StubSftpConnectionProvider.php create mode 100644 msd/vendor/league/flysystem-sftp/UnableToAuthenticate.php create mode 100644 msd/vendor/league/flysystem-sftp/UnableToConnectToSftpHost.php create mode 100644 msd/vendor/league/flysystem-sftp/UnableToEstablishAuthenticityOfHost.php create mode 100644 msd/vendor/league/flysystem-sftp/UnableToLoadPrivateKey.php create mode 100644 msd/vendor/league/flysystem-sftp/composer.json create mode 100644 msd/vendor/league/flysystem/INFO.md create mode 100644 msd/vendor/league/flysystem/LICENSE create mode 100644 msd/vendor/league/flysystem/composer.json create mode 100644 msd/vendor/league/flysystem/config.subsplit-publish.json create mode 100644 msd/vendor/league/flysystem/docker-compose.yml create mode 100644 msd/vendor/league/flysystem/readme.md create mode 100644 msd/vendor/league/flysystem/src/Config.php create mode 100644 msd/vendor/league/flysystem/src/CorruptedPathDetected.php create mode 100644 msd/vendor/league/flysystem/src/DirectoryAttributes.php create mode 100644 msd/vendor/league/flysystem/src/DirectoryListing.php create mode 100644 msd/vendor/league/flysystem/src/FileAttributes.php create mode 100644 msd/vendor/league/flysystem/src/Filesystem.php create mode 100644 msd/vendor/league/flysystem/src/FilesystemAdapter.php create mode 100644 msd/vendor/league/flysystem/src/FilesystemException.php create mode 100644 msd/vendor/league/flysystem/src/FilesystemOperationFailed.php create mode 100644 msd/vendor/league/flysystem/src/FilesystemOperator.php create mode 100644 msd/vendor/league/flysystem/src/FilesystemReader.php create mode 100644 msd/vendor/league/flysystem/src/FilesystemWriter.php create mode 100644 msd/vendor/league/flysystem/src/InvalidStreamProvided.php create mode 100644 msd/vendor/league/flysystem/src/InvalidVisibilityProvided.php create mode 100644 msd/vendor/league/flysystem/src/Local/LocalFilesystemAdapter.php create mode 100644 msd/vendor/league/flysystem/src/MountManager.php create mode 100644 msd/vendor/league/flysystem/src/PathNormalizer.php create mode 100644 msd/vendor/league/flysystem/src/PathPrefixer.php create mode 100644 msd/vendor/league/flysystem/src/PathTraversalDetected.php create mode 100644 msd/vendor/league/flysystem/src/PortableVisibilityGuard.php create mode 100644 msd/vendor/league/flysystem/src/ProxyArrayAccessToProperties.php create mode 100644 msd/vendor/league/flysystem/src/StorageAttributes.php create mode 100644 msd/vendor/league/flysystem/src/SymbolicLinkEncountered.php create mode 100644 msd/vendor/league/flysystem/src/UnableToCheckFileExistence.php create mode 100644 msd/vendor/league/flysystem/src/UnableToCopyFile.php create mode 100644 msd/vendor/league/flysystem/src/UnableToCreateDirectory.php create mode 100644 msd/vendor/league/flysystem/src/UnableToDeleteDirectory.php create mode 100644 msd/vendor/league/flysystem/src/UnableToDeleteFile.php create mode 100644 msd/vendor/league/flysystem/src/UnableToMountFilesystem.php create mode 100644 msd/vendor/league/flysystem/src/UnableToMoveFile.php create mode 100644 msd/vendor/league/flysystem/src/UnableToReadFile.php create mode 100644 msd/vendor/league/flysystem/src/UnableToResolveFilesystemMount.php create mode 100644 msd/vendor/league/flysystem/src/UnableToRetrieveMetadata.php create mode 100644 msd/vendor/league/flysystem/src/UnableToSetVisibility.php create mode 100644 msd/vendor/league/flysystem/src/UnableToWriteFile.php create mode 100644 msd/vendor/league/flysystem/src/UnixVisibility/PortableVisibilityConverter.php create mode 100644 msd/vendor/league/flysystem/src/UnixVisibility/VisibilityConverter.php create mode 100644 msd/vendor/league/flysystem/src/UnreadableFileEncountered.php create mode 100644 msd/vendor/league/flysystem/src/Visibility.php create mode 100644 msd/vendor/league/flysystem/src/WhitespacePathNormalizer.php create mode 100644 msd/vendor/league/mime-type-detection/CHANGELOG.md create mode 100644 msd/vendor/league/mime-type-detection/LICENSE create mode 100644 msd/vendor/league/mime-type-detection/composer.json create mode 100644 msd/vendor/league/mime-type-detection/src/EmptyExtensionToMimeTypeMap.php create mode 100644 msd/vendor/league/mime-type-detection/src/ExtensionMimeTypeDetector.php create mode 100644 msd/vendor/league/mime-type-detection/src/ExtensionToMimeTypeMap.php create mode 100644 msd/vendor/league/mime-type-detection/src/FinfoMimeTypeDetector.php create mode 100644 msd/vendor/league/mime-type-detection/src/GeneratedExtensionToMimeTypeMap.php create mode 100644 msd/vendor/league/mime-type-detection/src/MimeTypeDetector.php create mode 100644 msd/vendor/league/mime-type-detection/src/OverridingExtensionToMimeTypeMap.php create mode 100644 msd/vendor/monolog/monolog/CHANGELOG.md create mode 100644 msd/vendor/monolog/monolog/LICENSE create mode 100644 msd/vendor/monolog/monolog/README.md create mode 100644 msd/vendor/monolog/monolog/UPGRADE.md create mode 100644 msd/vendor/monolog/monolog/composer.json create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Attribute/AsMonologProcessor.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/DateTimeImmutable.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/ErrorHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/ElasticsearchFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/LogmaticFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/Handler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/LogRecord.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Logger.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Registry.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/ResettableInterface.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/SignalHandler.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Test/TestCase.php create mode 100644 msd/vendor/monolog/monolog/src/Monolog/Utils.php create mode 100644 msd/vendor/phpseclib/phpseclib/AUTHORS create mode 100644 msd/vendor/phpseclib/phpseclib/BACKERS.md create mode 100644 msd/vendor/phpseclib/phpseclib/LICENSE create mode 100644 msd/vendor/phpseclib/phpseclib/README.md create mode 100644 msd/vendor/phpseclib/phpseclib/appveyor.yml create mode 100644 msd/vendor/phpseclib/phpseclib/composer.json create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/File/X509.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php create mode 100644 msd/vendor/phpseclib/phpseclib/phpseclib/openssl.cnf create mode 100644 msd/vendor/psr/log/LICENSE create mode 100644 msd/vendor/psr/log/Psr/Log/AbstractLogger.php create mode 100644 msd/vendor/psr/log/Psr/Log/InvalidArgumentException.php create mode 100644 msd/vendor/psr/log/Psr/Log/LogLevel.php create mode 100644 msd/vendor/psr/log/Psr/Log/LoggerAwareInterface.php create mode 100644 msd/vendor/psr/log/Psr/Log/LoggerAwareTrait.php create mode 100644 msd/vendor/psr/log/Psr/Log/LoggerInterface.php create mode 100644 msd/vendor/psr/log/Psr/Log/LoggerTrait.php create mode 100644 msd/vendor/psr/log/Psr/Log/NullLogger.php create mode 100644 msd/vendor/psr/log/Psr/Log/Test/DummyTest.php create mode 100644 msd/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php create mode 100644 msd/vendor/psr/log/Psr/Log/Test/TestLogger.php create mode 100644 msd/vendor/psr/log/README.md create mode 100644 msd/vendor/psr/log/composer.json create mode 100644 msd/vendor/psr/simple-cache/src/CacheException.php create mode 100644 msd/vendor/psr/simple-cache/src/CacheInterface.php create mode 100644 msd/vendor/psr/simple-cache/src/InvalidArgumentException.php create mode 100644 msd/vendor/visualappeal/php-auto-update/.editorconfig create mode 100644 msd/vendor/visualappeal/php-auto-update/.github/workflows/phpunit.yml create mode 100644 msd/vendor/visualappeal/php-auto-update/.gitignore create mode 100644 msd/vendor/visualappeal/php-auto-update/Dockerfile create mode 100644 msd/vendor/visualappeal/php-auto-update/LICENSE.md create mode 100644 msd/vendor/visualappeal/php-auto-update/README.md create mode 100644 msd/vendor/visualappeal/php-auto-update/SECURITY.md create mode 100644 msd/vendor/visualappeal/php-auto-update/composer.json create mode 100644 msd/vendor/visualappeal/php-auto-update/docker-compose.yaml create mode 100644 msd/vendor/visualappeal/php-auto-update/example/client/files/file1.php create mode 100644 msd/vendor/visualappeal/php-auto-update/example/client/files/file2.php create mode 100644 msd/vendor/visualappeal/php-auto-update/example/client/index.php create mode 100644 msd/vendor/visualappeal/php-auto-update/example/client/somefile.php create mode 100644 msd/vendor/visualappeal/php-auto-update/example/client/update/.gitignore create mode 100644 msd/vendor/visualappeal/php-auto-update/src/AutoUpdate.php create mode 100644 msd/vendor/visualappeal/php-auto-update/src/Exceptions/DownloadException.php create mode 100644 msd/vendor/visualappeal/php-auto-update/src/Exceptions/ParserException.php create mode 100644 msd/vendor/visualappeal/php-auto-update/tests/AutoUpdateTest.php create mode 100644 msd/vendor/visualappeal/php-auto-update/tests/UnitTests.xml create mode 100644 msd/vendor/visualappeal/php-auto-update/tests/fixtures/noUpdateAvailable.ini create mode 100644 msd/vendor/visualappeal/php-auto-update/tests/fixtures/noUpdateAvailable.json create mode 100644 msd/vendor/visualappeal/php-auto-update/tests/fixtures/noUpdateAvailable.json.master create mode 100644 msd/vendor/visualappeal/php-auto-update/tests/fixtures/updateAvailable.ini create mode 100644 msd/vendor/visualappeal/php-auto-update/tests/fixtures/updateAvailable.json create mode 100644 msd/vendor/visualappeal/php-auto-update/tests/fixtures/updateAvailable.json.dev diff --git a/msd/.gitignore b/msd/.gitignore new file mode 100644 index 0000000..3884b09 --- /dev/null +++ b/msd/.gitignore @@ -0,0 +1,16 @@ +/.php-cs-fixer.cache +/work/config/myoosdumper.conf.php +/work/config/myoosdumper.php +/work/config/sql_statements +/work/log/*.gz +*.bak +/work/config/*.php +/work/backup/*.gz +/work/structure/*.gz +/.project +/.buildpath +/.settings +/work/cache +/work/temp +/work/log/update.log +/nbproject diff --git a/msd/.htaccess b/msd/.htaccess new file mode 100644 index 0000000..9e27f66 --- /dev/null +++ b/msd/.htaccess @@ -0,0 +1,7 @@ + +RewriteEngine off + +AuthName "MyOOS-Dumper" +AuthType Basic +AuthUserFile "/var/www/web360/htdocs/survey/msd/.htpasswd" +require valid-user \ No newline at end of file diff --git a/msd/.htpasswd b/msd/.htpasswd new file mode 100644 index 0000000..9b28579 --- /dev/null +++ b/msd/.htpasswd @@ -0,0 +1 @@ +schwaral:$1$h024lujD$i1BMtYcTlTuMHaFqH0Wcr0 \ No newline at end of file diff --git a/msd/README.md b/msd/README.md new file mode 100644 index 0000000..5befd68 --- /dev/null +++ b/msd/README.md @@ -0,0 +1,76 @@ +# [MyOOS [Dumper]](https://www.oos-shop.de) +=============== + +MyOOS [Dumper] is an open source community project. MyOOS [Dumper] is a backup program for MySQL databases. With it, backup copies of the data (forum, store, blog, etc.) can be created and restored if necessary. Especially for web space without shell access MyOOS [Dumper] is a useful alternative. + +Requirements +------------ + +- PHP version 7.4 or higher. +- MySQL version 5.6 or higher. +- Apache version 2.4 or higher. + + +Installation +------------ + +1. [Download MyOOS [Dumper]](https://github.com/r23/MyOOS-Dumper/releases) +2. Unzip the downloaded zip file +3. Extract the file into an empty directory and upload everything from the directory Myoos-Dumper to the server. +4. Go to install.php with your browser. The installation routine guides you through the individual steps and helps you to create the access data and database. + + +Documentation +------------- + +### MyOOS [Dumper] +Use your browser to open the [doku.oos-shop.de](https://doku.oos-shop.de/myoos-benutzerhandbuch/ueber-myoos/eine-einfuehrung-in-das-myoos-shopsystem/ueber-mysqldumper/) page for links to documentation. + + +Support +------------- +For free support, visit our support site: (https://foren.myoos.de/viewforum.php?f=40) + + + +Wish list / Future attractions +------------- +Do you have any suggestions for improvement? Feel free to contact the development team via the forum: (https://foren.myoos.de/viewforum.php?f=40) + + + + +Contribute +------------- +If you would like to help us improve the MyOOS project, we welcome your pull requests via GitHub here. +https://github.com/r23/MyOOS-Dumper + + +License +------------- +MyOOS [Dumper] Copyright (c) 2013 - by the MyOOS Development Team. + +based on MySQLDumper 1.24.4 +MySqlDumper Copyright (C)2004-2009 Daniel Schlichtholz (admin@mysqldumper.de) + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 675 Mass +Ave, Cambridge, MA 02139, USA. + +Released under the GNU General Public License. You can find the whole license text in the `license.txt` file. + + +## Further reading + +* [MyOOS](https://www.oos-shop.de) - Homepage of MyOOS +* [MyOOS Forum](https://foren.myoos.de/viewforum.php?f=40) - Community forum diff --git a/msd/ReadMe/ReadMe.txt b/msd/ReadMe/ReadMe.txt new file mode 100644 index 0000000..1bd0263 --- /dev/null +++ b/msd/ReadMe/ReadMe.txt @@ -0,0 +1,18 @@ +MyOOS [Dumper] Copyright (c) 2013 - by the MyOOS Development Team. + +based on MySQLDumper 1.24.4 +MySqlDumper Copyright (C)2004-2009 Daniel Schlichtholz (admin@mysqldumper.de) + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 675 Mass +Ave, Cambridge, MA 02139, USA. diff --git a/msd/ReadMe/license_english.txt b/msd/ReadMe/license_english.txt new file mode 100644 index 0000000..ba835c3 --- /dev/null +++ b/msd/ReadMe/license_english.txt @@ -0,0 +1,88 @@ +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +Preamble +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + +a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + +b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + +c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + +a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + +END OF TERMS AND CONDITIONS diff --git a/msd/composer.json b/msd/composer.json new file mode 100644 index 0000000..b894f82 --- /dev/null +++ b/msd/composer.json @@ -0,0 +1,14 @@ +{ + "require": { + "php": "^7.4 || ^8.0", + "ext-ftp": "*", + "ext-mysqli": "*", + "ext-openssl": "*", + "ext-curl": "*", + "ext-json": "*", + "ext-zip": "*", + "ext-zlib": "*", + "league/flysystem-sftp": "^2.0", + "visualappeal/php-auto-update": "^1.0.2" + } +} diff --git a/msd/composer.lock b/msd/composer.lock new file mode 100644 index 0000000..5df68cc --- /dev/null +++ b/msd/composer.lock @@ -0,0 +1,755 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "531d14fef9748689e0b5fca816d3f8f1", + "packages": [ + { + "name": "composer/semver", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-04-01T19:23:25+00:00" + }, + { + "name": "desarrolla2/cache", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/desarrolla2/Cache.git", + "reference": "0b8f985d09abfc04a2c1535943f6f07b7206161a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/desarrolla2/Cache/zipball/0b8f985d09abfc04a2c1535943f6f07b7206161a", + "reference": "0b8f985d09abfc04a2c1535943f6f07b7206161a", + "shasum": "" + }, + "require": { + "php": ">=7.2.0", + "psr/simple-cache": "^1.0" + }, + "provide": { + "psr/simple-cache-implementation": "1.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "ext-apcu": "*", + "ext-json": "*", + "ext-memcached": "*", + "ext-mysqli": "*", + "mikey179/vfsstream": "v1.6.8", + "mongodb/mongodb": "^1.3", + "phpstan/phpstan": "^0.12.29", + "phpunit/phpunit": "^8.3 || ^9.0", + "predis/predis": "~1.0.0", + "symfony/phpunit-bridge": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Desarrolla2\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel González", + "homepage": "http://desarrolla2.com/" + }, + { + "name": "Arnold Daniels", + "homepage": "https://jasny.net/" + } + ], + "description": "Provides an cache interface for several adapters Apc, Apcu, File, Mongo, Memcache, Memcached, Mysql, Mongo, Redis is supported.", + "homepage": "https://github.com/desarrolla2/Cache/", + "keywords": [ + "apc", + "apcu", + "cache", + "file", + "memcache", + "memcached", + "mongo", + "mysql", + "psr-16", + "redis", + "simple-cache" + ], + "support": { + "issues": "https://github.com/desarrolla2/Cache/issues", + "source": "https://github.com/desarrolla2/Cache/tree/v3.0.2" + }, + "time": "2022-03-27T23:04:06+00:00" + }, + { + "name": "league/flysystem", + "version": "2.5.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "8aaffb653c5777781b0f7f69a5d937baf7ab6cdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/8aaffb653c5777781b0f7f69a5d937baf7ab6cdb", + "reference": "8aaffb653c5777781b0f7f69a5d937baf7ab6cdb", + "shasum": "" + }, + "require": { + "ext-json": "*", + "league/mime-type-detection": "^1.0.0", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "guzzlehttp/ringphp": "<1.1.1" + }, + "require-dev": { + "async-aws/s3": "^1.5", + "async-aws/simple-s3": "^1.0", + "aws/aws-sdk-php": "^3.132.4", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "friendsofphp/php-cs-fixer": "^3.2", + "google/cloud-storage": "^1.23", + "phpseclib/phpseclib": "^2.0", + "phpstan/phpstan": "^0.12.26", + "phpunit/phpunit": "^8.5 || ^9.4", + "sabre/dav": "^4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/2.5.0" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2022-09-17T21:02:32+00:00" + }, + { + "name": "league/flysystem-sftp", + "version": "2.5.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-sftp.git", + "reference": "e30acbc9be024bb7df6ee4b159f2c8db3efcb6a7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-sftp/zipball/e30acbc9be024bb7df6ee4b159f2c8db3efcb6a7", + "reference": "e30acbc9be024bb7df6ee4b159f2c8db3efcb6a7", + "shasum": "" + }, + "require": { + "league/flysystem": "^2.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^7.2 || ^8.0", + "phpseclib/phpseclib": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\PhpseclibV2\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "SFTP filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "sftp" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem-sftp/issues", + "source": "https://github.com/thephpleague/flysystem-sftp/tree/2.5.0" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "abandoned": "league/flysystem-sftp-v3", + "time": "2022-04-27T17:27:27+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2022-04-17T13:12:02+00:00" + }, + { + "name": "monolog/monolog", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "720488632c590286b88b80e62aa3d3d551ad4a50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/720488632c590286b88b80e62aa3d3d551ad4a50", + "reference": "720488632c590286b88b80e62aa3d3d551ad4a50", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2", + "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpspec/prophecy": "^1.15", + "phpstan/phpstan": "^0.12.91", + "phpunit/phpunit": "^8.5.14", + "predis/predis": "^1.1 || ^2.0", + "rollbar/rollbar": "^1.3 || ^2 || ^3", + "ruflin/elastica": "^7", + "swiftmailer/swiftmailer": "^5.3|^6.0", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/2.8.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2022-07-24T11:55:47+00:00" + }, + { + "name": "phpseclib/phpseclib", + "version": "2.0.41", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "7e763c6f97ec1fcb37c46aa8ecfc20a2c71d9c1b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/7e763c6f97ec1fcb37c46aa8ecfc20a2c71d9c1b", + "reference": "7e763c6f97ec1fcb37c46aa8ecfc20a2c71d9c1b", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phing/phing": "~2.7", + "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4", + "squizlabs/php_codesniffer": "~2.0" + }, + "suggest": { + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations.", + "ext-xml": "Install the XML extension to load XML formatted public keys." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/2.0.41" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2022-12-23T16:44:18+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "psr/simple-cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/master" + }, + "time": "2017-10-23T01:57:42+00:00" + }, + { + "name": "visualappeal/php-auto-update", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/VisualAppeal/PHP-Auto-Update.git", + "reference": "4454361a0fa346c7fb179deef11585c79715b645" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/VisualAppeal/PHP-Auto-Update/zipball/4454361a0fa346c7fb179deef11585c79715b645", + "reference": "4454361a0fa346c7fb179deef11585c79715b645", + "shasum": "" + }, + "require": { + "composer/semver": "^3.0", + "desarrolla2/cache": "^3.0", + "ext-curl": "*", + "ext-json": "*", + "ext-zip": "*", + "monolog/monolog": "^2.1", + "php": ">=7.2.0", + "psr/log": "1.1.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "roave/security-advisories": "dev-master" + }, + "type": "library", + "autoload": { + "psr-4": { + "VisualAppeal\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tim Helfensdörfer", + "email": "tim@visualappeal.de" + } + ], + "description": "Autoupdater for PHP", + "support": { + "issues": "https://github.com/VisualAppeal/PHP-Auto-Update/issues", + "source": "https://github.com/VisualAppeal/PHP-Auto-Update/tree/1.0.2" + }, + "time": "2021-11-27T22:39:05+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^7.4 || ^8.0", + "ext-ftp": "*", + "ext-mysqli": "*", + "ext-openssl": "*", + "ext-curl": "*", + "ext-json": "*", + "ext-zip": "*", + "ext-zlib": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/msd/config.php b/msd/config.php new file mode 100644 index 0000000..5651de8 --- /dev/null +++ b/msd/config.php @@ -0,0 +1,163 @@ +1000 +$config['processlist_refresh'] = 3000; + +$config['empty_db_before_restore'] = 0; +$config['optimize_tables_beforedump'] = 0; +$config['use_binary_container'] = 0; +$config['stop_with_error'] = 1; +$config['ignore_enable_keys'] = 0; + +// For sending a mail after backup set send_mail to 1, otherless set to 0 +$config['send_mail'] = 0; +// Attach the backup 0=no 1=yes +$config['send_mail_dump'] = 0; +// set the recieve adress for the mail +$config['email_recipient'] = ''; +$config['email_recipient_cc'] = ''; +// set the sender adress (the script) +$config['email_sender'] = ''; + +//max. Size of Email-Attach, here 3 MB +$config['email_maxsize1'] = 3; +$config['email_maxsize2'] = 2; + +// FTP Server Configuration for Transfer +$config['ftp_transfer'][0] = 0; +$config['ftp_timeout'][0] = 30; +$config['ftp_useSSL'][0] = 0; +$config['ftp_mode'][0] = 0; +$config['ftp_server'][0] = ''; // Adress of FTP-Server +$config['ftp_port'][0] = '21'; // Port +$config['ftp_user'][0] = ''; // Username +$config['ftp_pass'][0] = ''; // Password +$config['ftp_dir'][0] = ''; // Upload-Directory + +$config['ftp_transfer'][1] = 0; +$config['ftp_timeout'][1] = 30; +$config['ftp_useSSL'][1] = 0; +$config['ftp_mode'][1] = 0; +$config['ftp_server'][1] = ''; +$config['ftp_port'][1] = '21'; +$config['ftp_user'][1] = ''; +$config['ftp_pass'][1] = ''; +$config['ftp_dir'][1] = ''; + +$config['ftp_transfer'][2] = 0; +$config['ftp_timeout'][2] = 30; +$config['ftp_useSSL'][2] = 0; +$config['ftp_mode'][2] = 0; +$config['ftp_server'][2] = ''; +$config['ftp_port'][2] = '21'; +$config['ftp_user'][2] = ''; +$config['ftp_pass'][2] = ''; +$config['ftp_dir'][2] = ''; + +// SFTP Server Configuration for Transfer +$config['sftp_transfer'][0] = 0; +$config['sftp_timeout'][0] = 30; +$config['sftp_server'][0] = ''; // Adress of SFTP-Server +$config['sftp_port'][0] = '22'; // Port +$config['sftp_user'][0] = ''; // Username +$config['sftp_pass'][0] = ''; // Password +$config['sftp_dir'][0] = ''; // Upload-Directory +$config['sftp_path_to_private_key'][0] = null; // private key (optional, default: null) can be used instead of password, set to null if password is set +$config['sftp_secret_passphrase_for_private_key'][0] = null; // passphrase (optional, default: null), set to null if privateKey is not used or has no passphrase +$config['sftp_fingerprint'][0] = null; // host fingerprint (optional, default: null), + +$config['sftp_transfer'][1] = 0; +$config['sftp_timeout'][1] = 30; +$config['sftp_server'][1] = ''; +$config['sftp_port'][1] = '22'; +$config['sftp_user'][1] = ''; +$config['sftp_pass'][1] = ''; +$config['sftp_dir'][1] = ''; +$config['sftp_path_to_private_key'][1] = null; +$config['sftp_secret_passphrase_for_private_key'][1] = null; +$config['sftp_fingerprint'][1] = null; + +$config['sftp_transfer'][2] = 0; +$config['sftp_timeout'][2] = 30; +$config['sftp_server'][2] = ''; +$config['sftp_port'][2] = '22'; +$config['sftp_user'][2] = ''; +$config['sftp_pass'][2] = ''; +$config['sftp_dir'][2] = ''; +$config['sftp_path_to_private_key'][2] = null; +$config['sftp_secret_passphrase_for_private_key'][2] = null; +$config['sftp_fingerprint'][2] = null; + +//Multipart 0=off 1=on +$config['multi_part'] = 0; +$config['multipartgroesse1'] = 1; +$config['multipartgroesse2'] = 2; +$config['multipart_groesse'] = 0; + +//Auto-Delete 0=off 1=on +$config['auto_delete'] = 0; +$config['max_backup_files'] = 3; + +//configuration file +$config['cron_configurationfile'] = 'myoosdumper.conf.php'; +//path to perl, for windows use e.g. C:perlbinperl.exe +$config['cron_perlpath'] = '/usr/bin/perl'; +//mailer use sendmail(1) or SMTP(0) +$config['cron_use_sendmail'] = 1; +//path to sendmail +$sendmail_path = ini_get('sendmail_path'); +$config['cron_sendmail'] = $sendmail_path > '' ? $sendmail_path : '/usr/lib/sendmail -t -oi -oem'; + +//adress of smtp-server +$config['cron_smtp'] = 'localhost'; +//smtp-port +$config['cron_smtp_port'] = 25; +$config['cron_extender'] = 0; +$config['cron_compression'] = 1; +$config['cron_printout'] = 1; +$config['cron_completelog'] = 1; +$config['cron_comment'] = ''; +$config['multi_dump'] = 0; +$config['logcompression'] = 1; +$config['log_maxsize1'] = 1; +$config['log_maxsize2'] = 2; +$config['log_maxsize'] = 1048576; diff --git a/msd/config_overview.php b/msd/config_overview.php new file mode 100644 index 0000000..34f0863 --- /dev/null +++ b/msd/config_overview.php @@ -0,0 +1,1273 @@ +'; + + return $t; +} + +// if new language was selected already before include +if (isset($_POST['save']) && $_POST['language'] != $_POST['lang_old']) { + $config['language'] = $_POST['language']; + $temp_lang = $config['language']; + include_once './inc/header.php'; // Normal prodecure (resets config[language]) + $config['language'] = $temp_lang; // re-set language + include './language/lang_list.php'; // This re-initializes $lang[] and loads appropiate language files +} else { + include_once './inc/header.php'; // language not changed, go on as usual +} +include_once './inc/runtime.php'; +include_once './inc/functions_sql.php'; +include_once './language/'.$config['language'].'/lang_help.php'; +include_once './language/'.$config['language'].'/lang_config_overview.php'; +include_once './language/'.$config['language'].'/lang_sql.php'; + +$msg = ''; +$sel = (isset($_POST['sel'])) ? $_POST['sel'] : 'db'; + +if (isset($_GET['sel'])) { + $sel = $_GET['sel']; +} + +$old_config_file = $config['config_file']; +if (isset($_GET['config'])) { + unset($databases); + $databases = []; + if (isset($_POST['save'])) { + unset($_POST['save']); + } + if (read_config($_GET['config'])) { + $config['config_file'] = $_GET['config']; + $_SESSION['config_file'] = $config['config_file']; + $msg = ''.sprintf($lang['L_CONFIG_LOADED'], $config['config_file']).''; + $msg .= ''; + } else { + read_config($old_config_file); + $msg = '

'.sprintf($lang['L_ERROR_LOADING_CONFIGFILE'], $config['config_file']).'

'; + } +} + +if (isset($_GET['config_delete'])) { + $del_config = urldecode($_GET['config_delete']); + if ($del_config == $config['config_file']) { + // currently selected configuration was deleted + $config['config_file'] = 'myoosdumper'; + $_SESSION['config_file'] = $config['config_file']; + read_config($config['config_file']); // Load standard + } + + $del = @unlink($config['paths']['config'].$del_config.'.php'); + if ($del) { + $del = @unlink($config['paths']['config'].$del_config.'.conf.php'); + } + if (false === $del) { + $msg = '

'.sprintf($lang['L_ERROR_DELETING_CONFIGFILE'], $del_config).'

'; + } else { + $msg = '

'.sprintf($lang['L_SUCCESS_DELETING_CONFIGFILE'], $del_config).'

'.''; //refresh menu-frame + } + $sel = 'configs'; +} + +include_once './inc/define_icons.php'; + +$config['files']['parameter'] = $config['paths']['config'].$config['config_file'].'.php'; +$config['theme'] = (!isset($config['theme'])) ? 'mod' : $config['theme']; +$config['cron_smtp_port'] = (!isset($config['cron_smtp_port'])) ? 25 : $config['cron_smtp_port']; + +if (!isset($command)) { + $command = 0; +} + +$checkFTP = [ + ' 

 
 ', + ' 

 
 ', + ' 

 
 ', +]; +$checkFTP[$i] = ''; +$ftptested = -1; + +if ((isset($_POST['testFTP0'])) || (isset($_POST['testFTP1'])) || (isset($_POST['testFTP2']))) { + $config['ftp_transfer'] = []; + $config['ftp_timeout'] = []; + $config['ftp_mode'] = []; + $config['ftp_useSSL'] = []; + + for ($i = 0; $i < 3; ++$i) { + $config['ftp_transfer'][$i] = (isset($_POST['ftp_transfer'][$i])) ? $_POST['ftp_transfer'][$i] : 0; + $config['ftp_timeout'][$i] = (isset($_POST['ftp_timeout'][$i])) ? $_POST['ftp_timeout'][$i] : 30; + $config['ftp_useSSL'][$i] = (isset($_POST['ftp_useSSL'][$i])) ? $_POST['ftp_useSSL'][$i] : 0; + $config['ftp_mode'][$i] = (isset($_POST['ftp_mode'][$i])) ? 1 : 0; + $config['ftp_server'][$i] = (isset($_POST['ftp_server'][$i])) ? $_POST['ftp_server'][$i] : ''; + $config['ftp_port'][$i] = (isset($_POST['ftp_port'][$i])) ? $_POST['ftp_port'][$i] : 21; + $config['ftp_user'][$i] = (isset($_POST['ftp_user'][$i])) ? $_POST['ftp_user'][$i] : ''; + $config['ftp_pass'][$i] = (isset($_POST['ftp_pass'][$i])) ? $_POST['ftp_pass'][$i] : ''; + $config['ftp_dir'][$i] = (isset($_POST['ftp_dir'][$i])) ? stripslashes($_POST['ftp_dir'][$i]) : '/'; + if ('' == $config['ftp_dir'][$i] || (strlen($config['ftp_dir'][$i]) > 1 && '/' != substr($config['ftp_dir'][$i], -1))) { + $config['ftp_dir'][$i] .= '/'; + } + if (isset($_POST['testFTP'.$i])) { + $checkFTP[$i] = '
'.$lang['L_TESTCONNECTION'].' FTP-Connection '.($i + 1).'

'.TesteFTP($i).'
'; + $ftptested = $i; + } + } +} + +// SFTP +$checkSFTP = [ + ' 

 
 ', + ' 

 
 ', + ' 

 
 ', +]; +$checkSFTP[$i] = ''; +$sftptested = -1; +if ((isset($_POST['testSFTP0'])) || (isset($_POST['testSFTP1'])) || (isset($_POST['testSFTP2']))) { + $config['sftp_transfer'] = []; + + $config['sftp_timeout'] = []; + + for ($i = 0; $i < 3; ++$i) { + $config['sftp_transfer'][$i] = (isset($_POST['sftp_transfer'][$i])) ? $_POST['sftp_transfer'][$i] : 0; + $config['sftp_timeout'][$i] = (isset($_POST['sftp_timeout'][$i])) ? $_POST['sftp_timeout'][$i] : 30; + $config['sftp_server'][$i] = (isset($_POST['sftp_server'][$i])) ? $_POST['sftp_server'][$i] : ''; + $config['sftp_port'][$i] = (isset($_POST['sftp_port'][$i])) ? $_POST['sftp_port'][$i] : 22; + $config['sftp_user'][$i] = (isset($_POST['sftp_user'][$i])) ? $_POST['sftp_user'][$i] : ''; + $config['sftp_pass'][$i] = (isset($_POST['sftp_pass'][$i])) ? $_POST['sftp_pass'][$i] : ''; + $config['sftp_dir'][$i] = (isset($_POST['sftp_dir'][$i])) ? stripslashes($_POST['sftp_dir'][$i]) : '/'; + + $config['sftp_path_to_private_key'][$i] = (isset($_POST['sftp_path_to_private_key'][$i])) ? stripslashes($_POST['sftp_path_to_private_key'][$i]) : null; + $config['sftp_secret_passphrase_for_private_key'][$i] = (isset($_POST['sftp_secret_passphrase_for_private_key'][$i])) ? stripslashes($_POST['sftp_secret_passphrase_for_private_key'][$i]) : null; + $config['sftp_fingerprint'][$i] = (isset($_POST['sftp_fingerprint'][$i])) ? stripslashes($_POST['sftp_fingerprint'][$i]) : null; + + if ('' == $config['sftp_dir'][$i] || (strlen($config['sftp_dir'][$i]) > 1 && '/' != substr($config['sftp_dir'][$i], -1))) { + $config['sftp_dir'][$i] .= '/'; + } + + if (isset($_POST['testSFTP'.$i])) { + $checkSFTP[$i] = '
'.$lang['L_TESTCONNECTION'].' SFTP-Connection '.($i + 1).'

'.TesteSFTP($i).'
'; + $sftptested = $i; + } + } +} + +$showVP = false; +$oldtheme = $config['theme'] ?? 'mod'; +$oldscposition = $config['interface_server_caption_position'] ?? ''; + +if ($ftptested > -1) { + $ftp_server[$ftptested] = $_POST['ftp_server'][$ftptested]; + $ftp_port[$ftptested] = $_POST['ftp_port'][$ftptested]; + $ftp_user[$ftptested] = $_POST['ftp_user'][$ftptested]; + $ftp_pass[$ftptested] = $_POST['ftp_pass'][$ftptested]; + $ftp_dir_s = 'ftp_dir['.$ftptested.']'; + $f = $_POST['ftp_dir']; + $ftp_dir[$ftptested] = stripslashes($f[$ftptested]); + // Remember inputs + $config['ftp_transfer'][$ftptested] = (isset($_POST['ftp_transfer'][$ftptested])) ? $_POST['ftp_transfer'][$ftptested] : 0; + $config['sftp'][$ftptested] = (isset($_POST['sftp'][$ftptested])) ? $_POST['sftp'][$ftptested] : 0; + $config['ftp_timeout'][$ftptested] = (isset($_POST['ftp_timeout'][$ftptested])) ? $_POST['ftp_timeout'][$ftptested] : 30; + $config['ftp_useSSL'][$ftptested] = (isset($_POST['ftp_useSSL'][$ftptested])) ? $_POST['ftp_useSSL'][$ftptested] : 0; + $config['ftp_mode'][$ftptested] = (isset($_POST['ftp_mode'][$ftptested])) ? 1 : 0; + $config['ftp_server'][$ftptested] = $ftp_server[$ftptested]; + $config['ftp_port'][$ftptested] = $ftp_port[$ftptested]; + $config['ftp_user'][$ftptested] = $ftp_user[$ftptested]; + $config['ftp_pass'][$ftptested] = $ftp_pass[$ftptested]; + $config['ftp_dir'][$ftptested] = $ftp_dir[$ftptested]; + + if ('' == $ftp_dir[$ftptested] || (strlen($ftp_dir[$ftptested]) > 1 && '/' != substr($ftp_dir[$ftptested], -1))) { + $ftp_dir[$ftptested] .= '/'; + } + WriteParams(); +} + +if ($sftptested > -1) { + $sftp_server[$sftptested] = $_POST['sftp_server'][$sftptested]; + $sftp_port[$sftptested] = $_POST['sftp_port'][$sftptested]; + $sftp_user[$sftptested] = $_POST['sftp_user'][$sftptested]; + $sftp_pass[$sftptested] = $_POST['sftp_pass'][$sftptested]; + $sftp_dir_s = 'sftp_dir['.$sftptested.']'; + $f = $_POST['sftp_dir']; + $sftp_dir[$sftptested] = stripslashes($f[$sftptested]); + if ('' == $sftp_dir[$sftptested] || (strlen($sftp_dir[$sftptested]) > 1 && '/' != substr($sftp_dir[$sftptested], -1))) { + $sftp_dir[$sftptested] .= '/'; + } + + $sftp_path_to_private_key[$sftptested] = stripslashes($_POST['sftp_path_to_private_key'][$sftptested]); + $sftp_secret_passphrase_for_private_key[$sftptested] = stripslashes($_POST['sftp_secret_passphrase_for_private_key'][$sftptested]); + $sftp_fingerprint[$sftptested] = stripslashes($_POST['sftp_fingerprint'][$sftptested]); + + // Remember inputs + $config['sftp_transfer'][$sftptested] = (isset($_POST['sftp_transfer'][$sftptested])) ? $_POST['sftp_transfer'][$sftptested] : 0; + $config['sftp_timeout'][$sftptested] = (isset($_POST['sftp_timeout'][$sftptested])) ? $_POST['sftp_timeout'][$sftptested] : 30; + $config['sftp_server'][$sftptested] = $sftp_server[$sftptested]; + $config['sftp_port'][$sftptested] = $sftp_port[$sftptested]; + $config['sftp_user'][$sftptested] = $sftp_user[$sftptested]; + $config['sftp_pass'][$sftptested] = $sftp_pass[$sftptested]; + $config['sftp_dir'][$sftptested] = $sftp_dir[$sftptested]; + $config['sftp_path_to_private_key'][$sftptested] = $sftp_path_to_private_key[$sftptested]; + $config['sftp_secret_passphrase_for_private_key'][$sftptested] = $sftp_secret_passphrase_for_private_key[$sftptested]; + $config['sftp_fingerprint'][$sftptested] = $sftp_fingerprint[$sftptested]; + + WriteParams(); +} + +echo MODHeader(); + +if (isset($_POST['load'])) { + $msg = SetDefault(true); + $msg = nl2br($msg).'
'.$lang['L_LOAD_SUCCESS'].'
'; + echo ''; +} + +if (isset($_POST['save'])) { + $save_config = true; + //Read parameters + $config['multi_dump'] = $_POST['MultiDBDump'] ?? 0; + $config['compression'] = $_POST['compression'] ?? 0; + $config['language'] = $_POST['language']; + $config['interface_server_caption'] = $_POST['server_caption'] ?? 0; + $config['interface_server_caption_position'] = isset($_POST['server_caption_position']) ? $_POST['server_caption_position'] : 0; + $config['interface_sqlboxsize'] = $_POST['sqlboxsize']; + $config['theme'] = $_POST['theme']; + $config['interface_table_compact'] = (isset($_POST['interface_table_compact'])) ? $_POST['interface_table_compact'] : 1; + + // if (isset($_POST['selected_config'])) $new_config = $_POST['selected_config']; + + if (isset($_POST['email0'])) { + $config['email_recipient'] = $_POST['email0']; + } + if (isset($_POST['email_recipient_cc'])) { + $config['email_recipient_cc'] = $_POST['email_recipient_cc']; + } + if (isset($_POST['email1'])) { + $config['email_sender'] = $_POST['email1']; + } + $config['send_mail'] = isset($_POST['send_mail']) ? $_POST['send_mail'] : 0; + $config['send_mail_dump'] = isset($_POST['send_mail_dump']) ? $_POST['send_mail_dump'] : 0; + + if (isset($_POST['email_maxsize1'])) { + $config['email_maxsize1'] = $_POST['email_maxsize1']; + } + if ('' == $config['email_maxsize1']) { + $config['email_maxsize1'] = 0; + } + if (isset($_POST['email_maxsize2'])) { + $config['email_maxsize2'] = $_POST['email_maxsize2']; + } + $config['email_maxsize'] = $config['email_maxsize1'] * ((1 == $config['email_maxsize2']) ? 1024 : 1024 * 1024); + + if (isset($_POST['memory_limit'])) { + $config['memory_limit'] = $_POST['memory_limit']; + } + if ('' == $config['memory_limit']) { + $config['memory_limit'] = 0; + } + if (isset($_POST['minspeed'])) { + $config['minspeed'] = $_POST['minspeed']; + } + if ($config['minspeed'] < 5) { + $config['minspeed'] = 5; + } + if (isset($_POST['maxspeed'])) { + $config['maxspeed'] = $_POST['maxspeed']; + } + if ($config['maxspeed'] < $config['minspeed']) { + $config['maxspeed'] = $config['minspeed'] * 2; + } + if (isset($_POST['stop_with_error'])) { + $config['stop_with_error'] = $_POST['stop_with_error']; + } + $config['ignore_enable_keys'] = isset($_POST['ignore_enable_keys']) ? (int) $_POST['ignore_enable_keys'] : 0; + + $config['multi_part'] = isset($_POST['multi_part']) ? $_POST['multi_part'] : 0; + if (1 == $config['multi_part']) { + $config['multipartgroesse1'] = isset($_POST['multipartgroesse1']) ? floatval(str_replace(',', '.', $_POST['multipartgroesse1'])) : 0; + $config['multipartgroesse2'] = isset($_POST['multipartgroesse2']) ? intval($_POST['multipartgroesse2']) : 0; + } + if ($config['multipartgroesse1'] < 100 && 1 == $config['multipartgroesse2']) { + $config['multipartgroesse1'] = 100; + } + if ($config['multipartgroesse1'] < 1 && 2 == $config['multipartgroesse2']) { + $config['multipartgroesse1'] = 1; + } + + $config['logcompression'] = isset($config['logcompression']) ? $config['logcompression'] : 0; + $oldlogcompression = $config['logcompression']; + $config['logcompression'] = (isset($_POST['logcompression']) && 1 == $_POST['logcompression']) ? 1 : 0; + if (isset($_POST['log_maxsize1'])) { + $config['log_maxsize1'] = $_POST['log_maxsize1']; + } + if ('' == $config['log_maxsize1']) { + $config['log_maxsize1'] = 0; + } + if (isset($_POST['log_maxsize2'])) { + $config['log_maxsize2'] = $_POST['log_maxsize2']; + } + $config['log_maxsize'] = $config['log_maxsize1'] * ((1 == $config['log_maxsize2']) ? 1024 : 1024 * 1024); + + $config['auto_delete'] = isset($_POST['auto_delete']) ? $_POST['auto_delete'] : 0; + if (isset($_POST['max_backup_files'])) { + $config['max_backup_files'] = $_POST['max_backup_files']; + } + + $config['empty_db_before_restore'] = isset($_POST['empty_db_before_restore']) ? $_POST['empty_db_before_restore'] : 0; + $config['optimize_tables_beforedump'] = isset($_POST['optimize_tables']) ? $_POST['optimize_tables'] : 0; + $config['use_binary_container'] = isset($_POST['binary_container']) ? $_POST['binary_container'] : 0; + if (isset($_POST['cron_dbindex'])) { + $config['cron_dbindex'] = $_POST['cron_dbindex']; + } + if (isset($_POST['cron_comment'])) { + $config['cron_comment'] = $_POST['cron_comment']; + } + + if (isset($_POST['cron_extender'])) { + $config['cron_extender'] = $_POST['cron_extender']; + } + // cron_select_savepath/ + if (!isset($_POST['cron_select_savepath'])) { + $_POST['cron_select_savepath'] = $config['config_file']; + } + if (isset($_POST['cron_savepath_new']) && !empty($_POST['cron_savepath_new'])) { + $tmp_configfilename = utf8_decode(trim($_POST['cron_savepath_new'])); + if (!preg_match('/^[a-z.-_]+$/i', $tmp_configfilename, $matches)) { + $save_config = false; + $msg .= '

'.sprintf($lang['L_ERROR_CONFIGFILE_NAME'], $_POST['cron_savepath_new']).'

'; + } else { + $config['config_file'] = $_POST['cron_savepath_new']; + $config['cron_configurationfile'] = $_POST['cron_savepath_new'].'.conf.php'; + } + } + + if (isset($_POST['cron_execution_path'])) { + $config['cron_execution_path'] = $_POST['cron_execution_path']; + } + if ('' == $config['cron_execution_path']) { + $config['cron_execution_path'] = 'mod_cron/'; + } + if (strlen($config['cron_execution_path']) > 1 && '/' != substr($config['cron_execution_path'], -1)) { + $config['cron_execution_path'] .= '/'; + } + + if (isset($_POST['cron_use_sendmail'])) { + $config['cron_use_sendmail'] = $_POST['cron_use_sendmail']; + } + if (isset($_POST['cron_sendmail'])) { + $config['cron_sendmail'] = $_POST['cron_sendmail']; + } + $config['cron_smtp'] = isset($_POST['cron_smtp']) ? $_POST['cron_smtp'] : 'localhost'; + + $config['cron_printout'] = isset($_POST['cron_printout']) ? $_POST['cron_printout'] : 0; + $config['cron_completelog'] = isset($_POST['cron_completelog']) ? $_POST['cron_completelog'] : 0; + $config['cron_compression'] = isset($_POST['compression']) ? $_POST['compression'] : 0; + if (isset($_POST['cron_completelog'])) { + $config['cron_completelog'] = $_POST['cron_completelog']; + } + + $databases['multi'] = []; + $databases['multi_praefix'] = []; + $databases['multi_commandbeforedump'] = []; + $databases['multi_commandafterdump'] = []; + + if (isset($databases['Name'][0]) && $databases['Name'][0] > '') { + for ($i = 0; $i < count($databases['Name']); ++$i) { + $databases['praefix'][$i] = isset($_POST['dbpraefix_'.$i]) ? $_POST['dbpraefix_'.$i] : ''; + $databases['command_before_dump'][$i] = (!isset($_POST['command_before_'.$i])) ? '' : $_POST['command_before_'.$i]; + $databases['command_after_dump'][$i] = (!isset($_POST['command_after_'.$i])) ? '' : $_POST['command_after_'.$i]; + if (isset($_POST['db_multidump_'.$i]) && $_POST['db_multidump_'.$i] == "db_multidump_$i") { + $databases['multi'][] = $databases['Name'][$i]; + $databases['multi_praefix'][] = $databases['praefix'][$i]; + $databases['multi_commandbeforedump'][] = $databases['command_before_dump'][$i]; + $databases['multi_commandafterdump'][] = $databases['command_after_dump'][$i]; + } + } + } + $databases['multisetting'] = (count($databases['multi']) > 0) ? implode(';', $databases['multi']) : ''; + $databases['multisetting_praefix'] = (count($databases['multi']) > 0) ? implode(';', $databases['multi_praefix']) : ''; + $databases['multisetting_commandbeforedump'] = (count($databases['multi']) > 0) ? implode(';', $databases['multi_commandbeforedump']) : ''; + $databases['multisetting_commandafterdump'] = (count($databases['multi']) > 0) ? implode(';', $databases['multi_commandafterdump']) : ''; + + + if (-2 == $config['cron_dbindex']) { + $datenbanken = count($databases['Name']); + $cron_db_array = str_replace(';', '|', $databases['multisetting']); + $cron_dbpraefix_array = str_replace(';', '|', $databases['multisetting_praefix']); + $cron_db_cbd_array = str_replace(';', '|', $databases['multisetting_commandbeforedump']); + $cron_db_cad_array = str_replace(';', '|', $databases['multisetting_commandafterdump']); + } elseif (-3 == $config['cron_dbindex']) { + $cron_db_array = implode('|', $databases['Name']); + $cron_dbpraefix_array = implode('|', $databases['praefix']); + $cron_db_cbd_array = isset($databases['command_before_dump']) && !empty($databases['command_before_dump']) ? implode('|', $databases['command_before_dump']) : ''; + $cron_db_cad_array = isset($databases['command_after_dump']) && !empty($databases['command_after_dump']) ? implode('|', $databases['command_after_dump']) : ''; + } + + $config['ftp_transfer'] = []; + $config['ftp_timeout'] = []; + $config['ftp_mode'] = []; + $config['ftp_useSSL'] = []; + + $config['ftp_server'] = []; + $config['ftp_port'] = []; + $config['ftp_user'] = []; + $config['ftp_pass'] = []; + $config['ftp_dir'] = []; + + for ($i = 0; $i < 3; ++$i) { + $checkFTP[$i] = ''; + $config['ftp_transfer'][$i] = isset($_POST['ftp_transfer'][$i]) ? $_POST['ftp_transfer'][$i] : 0; + $config['ftp_timeout'][$i] = isset($_POST['ftp_timeout'][$i]) ? $_POST['ftp_timeout'][$i] : 30; + $config['ftp_useSSL'][$i] = isset($_POST['ftp_useSSL'][$i]) ? 1 : 0; + + $config['ftp_mode'][$i] = isset($_POST['ftp_mode'][$i]) ? 1 : 0; + $config['ftp_server'][$i] = isset($_POST['ftp_server'][$i]) ? $_POST['ftp_server'][$i] : ''; + $config['ftp_port'][$i] = isset($_POST['ftp_port'][$i]) ? $_POST['ftp_port'][$i] : 0; + $config['ftp_user'][$i] = isset($_POST['ftp_user'][$i]) ? $_POST['ftp_user'][$i] : ''; + $config['ftp_pass'][$i] = isset($_POST['ftp_pass'][$i]) ? $_POST['ftp_pass'][$i] : ''; + $config['ftp_dir'][$i] = isset($_POST['ftp_dir'][$i]) ? stripslashes($_POST['ftp_dir'][$i]) : ''; + if (0 == $config['ftp_port'][$i]) { + $config['ftp_port'][$i] = 21; + } + if ('' == $config['ftp_dir'][$i] || (strlen($config['ftp_dir'][$i]) > 1 && '/' != substr($config['ftp_dir'][$i], -1))) { + $config['ftp_dir'][$i] .= '/'; + } + } + + $config['sftp_transfer'] = []; + $config['sftp_timeout'] = []; + + $config['sftp_server'] = []; + $config['sftp_port'] = []; + $config['sftp_user'] = []; + $config['sftp_pass'] = []; + $config['sftp_dir'] = []; + + $config['sftp_path_to_private_key'] = []; + $config['sftp_secret_passphrase_for_private_key'] = []; + $config['sftp_fingerprint'] = []; + + for ($i = 0; $i < 3; ++$i) { + $checkFTP[$i] = ''; + $config['sftp_transfer'][$i] = isset($_POST['sftp_transfer'][$i]) ? $_POST['sftp_transfer'][$i] : 0; + $config['sftp_timeout'][$i] = isset($_POST['sftp_timeout'][$i]) ? $_POST['sftp_timeout'][$i] : 30; + + $config['sftp_server'][$i] = isset($_POST['sftp_server'][$i]) ? $_POST['sftp_server'][$i] : ''; + $config['sftp_port'][$i] = isset($_POST['sftp_port'][$i]) ? $_POST['sftp_port'][$i] : 0; + $config['sftp_user'][$i] = isset($_POST['sftp_user'][$i]) ? $_POST['sftp_user'][$i] : ''; + $config['sftp_pass'][$i] = isset($_POST['sftp_pass'][$i]) ? $_POST['sftp_pass'][$i] : ''; + $config['sftp_dir'][$i] = isset($_POST['sftp_dir'][$i]) ? stripslashes($_POST['sftp_dir'][$i]) : ''; + + $config['sftp_path_to_private_key'][$i] = (isset($_POST['sftp_path_to_private_key'][$i])) ? stripslashes($_POST['sftp_path_to_private_key'][$i]) : null; + $config['sftp_secret_passphrase_for_private_key'][$i] = (isset($_POST['sftp_secret_passphrase_for_private_key'][$i])) ? stripslashes($_POST['sftp_secret_passphrase_for_private_key'][$i]) : null; + $config['sftp_fingerprint'][$i] = (isset($_POST['sftp_fingerprint'][$i])) ? stripslashes($_POST['sftp_fingerprint'][$i]) : null; + + if (0 == $config['sftp_port'][$i]) { + $config['sftp_port'][$i] = 22; + } + if ('' == $config['sftp_dir'][$i] || (strlen($config['sftp_dir'][$i]) > 1 && '/' != substr($config['sftp_dir'][$i], -1))) { + $config['sftp_dir'][$i] .= '/'; + } + } + + $config['bb_width'] = $_POST['bb_width']; + $config['bb_textcolor'] = $_POST['bb_textcolor']; + $config['sql_limit'] = $_POST['sql_limit']; + + if ($config['dbhost'] != $_POST['dbhost'] || $config['dbuser'] != $_POST['dbuser'] || $config['dbpass'] != $_POST['dbpass'] || $config['dbport'] != $_POST['dbport'] || $config['dbsocket'] != $_POST['dbsocket']) { + // new connection parameters + $show_VP = true; + + // Save old parameters + $old['dbhost'] = $config['dbhost']; + $old['dbuser'] = $config['dbuser']; + $old['dbpass'] = $config['dbpass']; + $old['dbport'] = $config['dbport']; + $old['dbsocket'] = $config['dbsocket']; + + //set new + $config['dbhost'] = $_POST['dbhost']; + $config['dbuser'] = $_POST['dbuser']; + $config['dbpass'] = $_POST['dbpass']; + $config['dbport'] = $_POST['dbport']; + $config['dbsocket'] = $_POST['dbsocket']; + if (mod_mysqli_connect()) { + // new connection data was accepted -> manually delete DB list from other user + SetDefault(); + $msg .= ''; + } else { + // Get old values + $config['dbhost'] = $old['dbhost']; + $config['dbuser'] = $old['dbuser']; + $config['dbpass'] = $old['dbpass']; + $config['dbport'] = $old['dbport']; + $config['dbsocket'] = $old['dbsocket']; + $msg .= '

'.$lang['L_WRONG_CONNECTIONPARS'].'

'; + } + } + + // Manuelles hinzufügen einer Datenbank + if ($_POST['add_db_manual'] > '') { + $to_add = trim($_POST['add_db_manual']); + $found = false; + // Check if the DB already exists in the list + if (isset($databases['Name'][0])) { + foreach ($databases['Name'] as $existing_db) { + if ($existing_db == $to_add) { + $found = true; + } + } + } + if ($found) { + $add_db_message = sprintf($lang['L_DB_IN_LIST'], $to_add); + } else { + if (mod_mysqli_connect()) { + $res = mysqli_select_db($config['dbconnection'], $to_add); + if (false === !$res) { + $databases['Name'][] = $to_add; + // Refresh menu so that the DB appears in the select list + echo ''; + } else { + $add_db_message = sprintf($lang['L_DB_MANUAL_ERROR'], $to_add); + } + $showVP = true; + } + } + } + + // After a transfer of a new configuration remove superfluous indexes before writing + $number_databases = sizeof($databases['Name']); + if (sizeof($databases['praefix']) > $number_databases) { + for ($i = sizeof($databases['praefix']); $i >= $number_databases; --$i) { + unset($databases['praefix'][$i]); + unset($databases['command_before_dump'][$i]); + unset($databases['command_after_dump'][$i]); + } + if ($databases['db_selected_index'] >= $number_databases) { + $databases['db_selected_index'] = 0; + } + } + + // and write away + if ($save_config) { + if (true == WriteParams(false)) { + // new language? Then also update menu on the left + if ($_SESSION['config']['language'] != $config['language'] || $_POST['scaption_old'] != $config['interface_server_caption'] || $oldtheme != $config['theme'] || $oldscposition != $config['interface_server_caption_position']) { + $msg .= ''; + if (isset($_POST['cron_savepath_new']) && $_POST['cron_savepath_new'] > '') { + $msg .= '

'.$lang['L_SUCCESS_CONFIGFILE_CREATED'].'

'; + } + } + // Load parameters + read_config($config['config_file']); + if ($config['logcompression'] != $oldlogcompression) { + DeleteLog(); + } + $msg .= '

'.sprintf($lang['L_SAVE_SUCCESS'], $config['config_file']).'

'; + $msg .= ''; + } else { + $msg .= '

'.$lang['L_SAVE_ERROR'].'

'; + } + } +} + +ReadSQL(); +?> + +'.$nl; +$aus['formstart'] .= '
'; +$aus['formstart'] .= '
'.$nl; +$aus['formstart'] .= '
'.$nl; +$aus['formstart'] .= '
'.$nl; +$aus['formstart'] .= '
'.$nl; +$aus['formstart'] .= '
'.$nl; +$aus['formstart'] .= '
'.$nl; +$aus['formstart'] .= '
'.$nl; +$aus['formstart'] .= '
'.$nl; +$aus['formstart'] .= '
'.$nl; +//$aus['formstart'] .= '
'.$nl; + +//$aus['formstart'] .= ''; +$aus['formstart'] .= '


'.$nl; +$aus['formstart'] .= ''.$nl; +//$aus['formstart'] .= ''.$nl; +$aus['formstart'] .= '
'.$msg.$nl; + +// Configuration files +$aus['conf'] = '
'.$lang['L_CONFIGFILES'].''.$nl.$nl; + +$aus['conf'] .= ''; +$aus['conf'] .= ''; +$aus['conf'] .= ''; +$aus['conf'] .= ''; +$aus['conf'] .= '
'.$lang['L_CREATE_CONFIGFILE'].':'.print_save_button().'
'; + +$aus['conf'] .= '
'; +$aus['conf'] .= ''; + +$i = 0; +$old_config = $config; +$configs = get_config_filenames(); + +if (sizeof($configs) > 0) { + foreach ($configs as $c) { + ++$i; + unset($databases); + read_config($c); + $aus['conf'] .= ''; + + $aus['conf'] .= ''; + + // Settings + $aus['conf'] .= ''; + + $aus['conf'] .= ''; + } +} + +$configfile = $old_config['config_file']; +$config = $old_config; +unset($databases); +$databases = []; +read_config($configfile); + +$aus['conf'] .= '
#'.$lang['L_CONFIGFILE'].' / '.$lang['L_MYSQL_DATA'].''.$lang['L_CONFIGURATIONS'].''.$lang['L_ACTION'].'
'.$i.'.'; + + $aus['conf'] .= ''; + $aus['conf'] .= ''; // filename + + $aus['conf'] .= ''; + $aus['conf'] .= ''; + $aus['conf'] .= ''; + + // Show database list + $aus['conf'] .= ''; + + $aus['conf'] .= '
'.$lang['L_NAME'].':'.$c.'
'.$lang['L_DB_HOST'].':'.$config['dbhost'].'
'.$lang['L_DB_USER'].':'.$config['dbuser'].'
'; + + $aus['conf'] .= $lang['L_DBS'].':'; + $aus['conf'] .= ''; + $aus['conf'] .= $icon['search'].''.sizeof($databases['Name']).''; + $aus['conf'] .= '
'; + $aus['conf'] .= '
'; + + // Build string from multidump DBs + $toolboxstring = ''; + $databases['multi'] = []; + if (isset($databases['multisetting'])) { + $databases['multi'] = explode(';', $databases['multisetting']); + } + $multi_praefixe = []; + if (isset($databases['multisetting_praefix'])) { + $multi_praefixe = explode(';', $databases['multisetting_praefix']); + } + if (is_array($databases['multi'])) { + for ($x = 0; $x < sizeof($databases['multi']); ++$x) { + if ($x > 0) { + $toolboxstring .= ', '; + } + $toolboxstring .= $databases['multi'][$x]; + if (isset($multi_praefixe[$x]) && $multi_praefixe[$x] > '') { + $toolboxstring .= ' (\''.$multi_praefixe[$x].'\')'; + } + } + } + + // DB list for PHP + if (isset($config['multi_dump']) && (1 == $config['multi_dump'])) { // Multidump + $aus['conf'] .= table_output($lang['L_BACKUP_DBS_PHP'], $toolboxstring); + } else { + // Current DB + $text = isset($databases['db_actual']) ? $databases['db_actual'] : ''; + if (isset($databases['db_selected_index']) && isset($databases['praefix'][$databases['db_selected_index']]) && $databases['praefix'][$databases['db_selected_index']] > '') { + $text .= " ('".$databases['praefix'][$databases['db_selected_index']]."')"; + } + $aus['conf'] .= table_output($lang['L_BACKUP_DBS_PHP'], $text); + } + + // DB list for Perl + // Fallback if index is not yet set from old configuration files -> save all DBs + if (!isset($config['cron_dbindex'])) { + $config['cron_dbindex'] = -3; + } + if (-2 == $config['cron_dbindex']) { + $aus['conf'] .= table_output($lang['L_BACKUP_DBS_PERL'], $toolboxstring); + } elseif (-3 == $config['cron_dbindex']) { + $text = $lang['L_ALL']; + $aus['conf'] .= table_output($lang['L_BACKUP_DBS_PERL'], $text); + } else { + $text = isset($databases['Name'][$config['cron_dbindex']]) ? $databases['Name'][$config['cron_dbindex']] : ''; + if (isset($databases['praefix'][$config['cron_dbindex']]) && $databases['praefix'][$config['cron_dbindex']] > '') { + $text .= " ('".$databases['praefix'][$config['cron_dbindex']]."')"; + } + $aus['conf'] .= table_output($lang['L_BACKUP_DBS_PERL'], $text); + } + + if (isset($config['multi_part']) && (1 == $config['multi_part'])) { // Multipart + $aus['conf'] .= table_output($lang['L_MULTI_PART'], $lang['L_YES'].', '.$lang['L_FILESIZE'].' '.byte_output($config['multipart_groesse'])); + } + + if (isset($config['send_mail']) && (1 == $config['send_mail'])) { // Email + $aus['conf'] .= table_output($lang['L_SEND_MAIL_FORM'], $lang['L_YES'].', '.$lang['L_EMAIL_ADRESS'].': '.$config['email_recipient']); + if ($config['email_recipient_cc'] > '') { + $aus['conf'] .= table_output($lang['L_EMAIL_CC'], $config['email_recipient_cc']); + } + $text = $lang['L_YES'].', '.$lang['L_MAX_UPLOAD_SIZE'].': '; + $bytes = $config['email_maxsize1'] * 1024; + if (2 == $config['email_maxsize2']) { + $bytes = $bytes * 1024; + } + $text .= byte_output($bytes); + if (1 == $config['send_mail_dump']) { + $aus['conf'] .= table_output($lang['L_SEND_MAIL_DUMP'], $text); + } + } + + for ($x = 0; $x < 3; ++$x) { + // FTP + if (isset($config['ftp_transfer'][$x]) && $config['ftp_transfer'][$x] > 0) { + $aus['conf'] .= table_output($lang['L_FTP'], sprintf($lang['L_FTP_SEND_TO'], $config['ftp_server'][$x], $config['ftp_dir'][$x])); + } + // SFTP + if (isset($config['sftp_transfer'][$x]) && $config['sftp_transfer'][$x] > 0) { + $aus['conf'] .= table_output($lang['L_SFTP'], sprintf($lang['L_SFTP_SEND_TO'], $config['sftp_server'][$x], $config['sftp_dir'][$x])); + } + } + $aus['conf'] .= '
'; + $aus['conf'] .= ''.$icon['edit'].''; + + if ('myoosdumper' != $c) { // && $old_config['config_file']!= $c) + $aus['conf'] .= ''.$icon['delete'].''; + } else { + $aus['conf'] .= ' '; + } + + $aus['conf'] .= '
'; +$aus['conf'] .= '
'.$nl.$nl; + +// Access data +$aus['db'] = '
'.$lang['L_CONNECTIONPARS'].''.$nl.$nl; +$aus['db'] .= '
'.$lang['L_FADE_IN_OUT'].'
'.$lang['L_DB_BACKUPPARS'].''; + +$aus['db'] .= ''; + +// If databases are available +if (isset($databases['Name'][0]) && $databases['Name'][0] > '') { + if (!isset($databases['multi']) || (!is_array($databases['multi']))) { + $databases['multi'] = []; + } + if (1 == count($databases['Name'])) { + $databases['db_actual'] = $databases['Name'][0]; + $databases['db_selected_index'] = 0; + $aus['db'] .= ''; + $aus['db'] .= ''; + $aus['db'] .= ''; + $aus['db'] .= ''; + $aus['db'] .= ''; + $aus['db'] .= ''; + $aus['db'] .= ''; + } else { + $disabled = ''; + if (in_array($databases['db_actual'], $dontBackupDatabases)) { + $disabled = ' disabled="disabled"'; + } + + $aus['db'] .= ''; + $aus['db'] .= ''; +$aus['db'] .= '
'.Help($lang['L_HELP_DB'], 'conf1').$lang['L_LIST_DB'].''.$databases['db_actual'].'
'.Help($lang['L_HELP_PRAEFIX'], 'conf2').$lang['L_PRAEFIX'].'
'.Help($lang['L_HELP_COMMANDS'], '').'Command before Dump'.ComboCommandDump(0, $databases['db_selected_index']).'
'.Help($lang['L_HELP_COMMANDS'], '').'Command after Dump'.ComboCommandDump(1, $databases['db_selected_index']).''.$lang['L_SQL_BEFEHLE'].'
'.Help($lang['L_HELP_DB'], 'conf1').$lang['L_LIST_DB'].' '.$lang['L_ACTIVATE_MULTIDUMP'].'
'; + $aus['db'] .= ''; + $aus['db'] .= ''; + + //erst die aktuelle DB + $aus['db'] .= ''; + $aus['db'] .= ''; + $aus['db'] .= ''; + $aus['db'] .= ''; + $aus['db'] .= ''; + $aus['db'] .= ''; + + $dbacombo = $dbbcombo = ''; + $j = 0; + for ($i = 0; $i < count($databases['Name']); ++$i) { + if ($i != $databases['db_selected_index']) { + ++$j; + $disabled = ''; + if (in_array($databases['Name'][$i], $dontBackupDatabases)) { + $disabled = ' disabled="disabled"'; + } + if (!isset($databases['praefix'][$i])) { + $databases['praefix'][$i] = ''; + } + $aus['db'] .= ''; + $aus['db'] .= ''; + $aus['db'] .= ''; + $aus['db'] .= ''; + $aus['db'] .= ''; + } + } + } +} else { + $aus['db'] .= ''; +} +$aus['db'] .= '
'.$lang['L_DB'].'Multidump
('.$lang['L_ALL'].' '.$lang['L_NONE'].')
'.Help($lang['L_HELP_PRAEFIX'], 'conf2').$lang['L_PRAEFIX'].''.Help($lang['L_HELP_COMMANDS'], '', 11).'Command before Dump'.Help($lang['L_HELP_COMMANDS'], '', 11).'Command after Dump'.$lang['L_SQL_BEFEHLE'].'
'.$databases['db_actual'].''.ComboCommandDump(0, $databases['db_selected_index'], $disabled) + .''.ComboCommandDump(1, $databases['db_selected_index'], $disabled).''.$lang['L_SQL_BEFEHLE'].'
'.$databases['Name'][$i].''.ComboCommandDump(0, $i, $disabled).'' + .ComboCommandDump(1, $i, $disabled).''.$lang['L_SQL_BEFEHLE'].'
'.$lang['L_NO_DB_FOUND'].'
'; + +// sonstige Einstellungen +$aus['global1'] = '
'.$lang['L_GENERAL'].''; + +$aus['global1'] .= ''; +$aus['global1'] .= ''; + +$aus['global1'] .= ''; +$aus['global1'] .= ''; + +$aus['global1'] .= ''; +$aus['global1'] .= ''; + +$aus['global1'] .= '
'.Help('', '').'Logfiles:  '.$lang['L_COMPRESSED'].'
'; +$aus['global1'] .= ''.$lang['L_MAXSIZE'].':    '; +$aus['global1'] .= ''; +$aus['global1'] .= '
'.Help($lang['L_HELP_MEMORYLIMIT'], '').$lang['L_MEMORY_LIMIT'].':   Bytes   '.$lang['L_AUTODETECT'].''; +$aus['global1'] .= '
'.Help($lang['L_HELP_SPEED'], '').$lang['L_SPEED'].':  '.$lang['L_TO'].' 
'.$lang['L_DUMP'].''; + +$aus['global1'] .= ''; +$aus['global1'] .= ''; +//Multipart-Backup --> + +$aus['global1'] .= ''; +$aus['global1'] .= ''; + +$aus['global1'] .= ''; +$aus['global1'] .= ''; + +$aus['global1'] .= ''; +$aus['global1'] .= ''; + +$aus['global1'] .= ''; +$aus['global1'] .= ''; + +$aus['global1'] .= '
'.Help($lang['L_HELP_ZIP'], 'conf3').$lang['L_GZIP'].': '; +$aus['global1'] .= '
'.Help($lang['L_HELP_MULTIPART'], '').$lang['L_MULTI_PART'].': '; +$aus['global1'] .= '
'.Help($lang['L_HELP_MULTIPARTGROESSE'], '').$lang['L_MULTI_PART_GROESSE'].': Kilobytes'; +$aus['global1'] .= '
'.Help($lang['L_HELP_OPTIMIZE'], '').$lang['L_OPTIMIZE'].':'; +$aus['global1'] .= '
'.Help($lang['L_HELP_BINARY'], '').$lang['L_BINARY'].':'; +$aus['global1'] .= '
'.$lang['L_RESTORE'].''; +$aus['global1'] .= ''; +$aus['global1'] .= ''; + +$aus['global1'] .= ''; +$aus['global1'] .= ''; + +if (!isset($config['ignore_enable_keys'])) { + $config['ignore_enable_keys'] = 0; +} +$aus['global1'] .= ''; +$aus['global1'] .= ''; + +$aus['global1'] .= '
'.Help($lang['L_HELP_EMPTY_DB_BEFORE_RESTORE'], 'conf4').$lang['L_EMPTY_DB_BEFORE_RESTORE'].': '; +$aus['global1'] .= '
'.Help('', '').$lang['L_ERRORHANDLING_RESTORE'].': '.$lang['L_EHRESTORE_CONTINUE']; +$aus['global1'] .= '    '.$lang['L_EHRESTORE_STOP']; +$aus['global1'] .= '
Ignore "ENABLE KEYS":'; +$aus['global1'] .= '
'; +$aus['global1'] .= print_save_button(); +$aus['global1'] .= '
'; + +//Interface --> +$aus['global3'] = '
'.$lang['L_CONFIG_INTERFACE'].''; +$aus['global3'] .= ''; +$aus['global3'] .= ''; + +$aus['global3'] .= ''; +$aus['global3'] .= ''; + +$aus['global3'] .= ''; + +$aus['global3'] .= '
'.Help($lang['L_HELP_LANG'], 'conf11').$lang['L_LANGUAGE'].': 
'.Help($lang['L_HELP_SERVERCAPTION'], '').$lang['L_SERVERCAPTION'].':
'; +$aus['global3'] .= ' '.$lang['L_IN_MAINFRAME'].'   '.$lang['L_IN_LEFTFRAME'].''; +$aus['global3'] .= '
'.Help('', '').'Theme:
'.$lang['L_SQL_BROWSER'].''; +$aus['global3'] .= ''; + +$config['interface_sqlboxsize'] = isset($config['interface_sqlboxsize']) ? $config['interface_sqlboxsize'] : ''; +$aus['global3'] .= ''; + +$aus['global3'] .= ''; +$aus['global3'] .= ''; +$aus['global3'] .= ''; +$aus['global3'] .= ''; +$aus['global3'] .= ''; + +$aus['global3'] .= '
'.Help('', '').$lang['L_SQLBOXHEIGHT'].':  Pixel
'.Help('', '').$lang['L_SQLLIMIT'].':  
'.Help('', '').$lang['L_BBPARAMS'].': '; +$aus['global3'] .= ''; +$aus['global3'] .= ''; +$aus['global3'] .= '
'.$lang['L_WIDTH'].': pixel
'.$lang['L_BBTEXTCOLOR'].': 
'; +$aus['global3'] .= '
'.Help('', '').'SQL-Grid:  normal   '; +$aus['global3'] .= ' compact
'.print_save_button().'
'; + +//automatic delete--> +$aus['global2'] = '
'.$lang['L_CONFIG_AUTODELETE'].''; +$aus['global2'] .= ''; +$aus['global2'] .= ''; + +$aus['global2'] .= ''; +$aus['global2'] .= '
'.Help($lang['L_HELP_AD1'], 'conf8').$lang['L_AUTODELETE'].': '; +$aus['global2'] .= '
'.Help($lang['L_HELP_AD3'], 'conf10').$lang['L_NUMBER_OF_FILES_FORM'].':  '; +$aus['global2'] .= '
'.print_save_button().'
'; + +//Email--> +if (!isset($config['email_recipient_cc'])) { + $config['email_recipient_cc'] = ''; +} // backwards compatibility if field is undefined +$aus['transfer1'] = '
'.$lang['L_EMAIL_NOTIFICATION'].''; +$aus['transfer1'] .= ''; +$aus['transfer1'] .= ''; + +$aus['transfer1'] .= ''; +$aus['transfer1'] .= ''; + +$aus['transfer1'] .= ''; + +$aus['transfer1'] .= ''; + +$aus['transfer1'] .= ''; + +$aus['transfer1'] .= ''; +$aus['transfer1'] .= '
'.$lang['L_SEND_MAIL_FORM'].': '; +$aus['global3'] .= '
'.$lang['L_EMAIL_ADRESS'].': 
'.$lang['L_EMAIL_CC'].': '; +$aus['global3'] .= '
'.$lang['L_EMAIL_SENDER'].': '; +$aus['global3'] .= '
'.$lang['L_SEND_MAIL_DUMP'].': '; +$aus['transfer1'] .= ''; +$aus['global3'] .= '
'.$lang['L_EMAIL_MAXSIZE'].': '; +$aus['transfer1'] .= '  '; +$aus['transfer1'] .= '
'.$lang['L_CRON_MAILPRG'].': '; +$aus['transfer1'] .= ''; +$aus['transfer1'] .= '
 sendmail
 SMTP
 SMTP-Port: '.$config['cron_smtp_port'].''; +$aus['global3'] .= '
'.print_save_button().'
'; + +//FTP--> +$aus['transfer2'] = '
'.$lang['L_CONFIG_FTP'].''; +for ($i = 0; $i < 3; ++$i) { + $aus['transfer2'] .= '
FTP-Connection '.($i + 1).''; + + $aus['transfer2'] .= ''; + $aus['transfer2'] .= ''; + + $aus['transfer2'] .= ''; + $aus['transfer2'] .= ''; + + $aus['transfer2'] .= ''; + $aus['transfer2'] .= ''; + $aus['transfer2'] .= '
'.Help($lang['L_HELP_FTPTRANSFER'], '').$lang['L_FTP_TRANSFER'].': 
'.Help($lang['SFTP'], '').$lang['L_FTP_TIMEOUT'].':  sec
'.Help($lang['L_HELP_FTP_MODE'], '').$lang['L_FTP_CHOOSE_MODE'].':  '; + $aus['transfer2'] .= $lang['L_FTP_PASSIVE'].'
'; + + $aus['transfer2'] .= '
'.Help($lang['L_HELP_FTPSSL'], '').$lang['L_FTP_SSL'].': '; + $aus['transfer2'] .= ' '.$lang['L_FTP_USESSL'].'
'; + + $aus['transfer2'] .= '

'.$checkFTP[$i].'
'; + $aus['transfer2'] .= ''; + $aus['transfer2'] .= ''; + $aus['transfer2'] .= ''; + $aus['transfer2'] .= ''; + $aus['transfer2'] .= ''; + $aus['transfer2'] .= '
'.Help($lang['L_HELP_FTPSERVER'], 'conf14', 12).$lang['L_FTP_SERVER'].': 
'.Help($lang['L_HELP_FTPPORT'], 'conf15', 12).$lang['L_FTP_PORT'].': 
'.Help($lang['L_HELP_FTPUSER'], 'conf16', 12).$lang['L_FTP_USER'].': 
'.Help($lang['L_HELP_FTPPASS'], 'conf17', 12).$lang['L_FTP_PASS'].': 
'.Help($lang['L_HELP_FTPDIR'], 'conf18', 12).$lang['L_FTP_DIR'].': 
'.print_save_button().'
'; +} +$aus['transfer2'] .= '
'; + +// SFTP--> +$aus['transfer3'] = '
'.$lang['L_CONFIG_SFTP'].''; +for ($i = 0; $i < 3; ++$i) { + $aus['transfer3'] .= '
SFTP-Connection '.($i + 1).''; + + $aus['transfer3'] .= ''; + $aus['transfer3'] .= ''; + $aus['transfer3'] .= ''; + $aus['transfer3'] .= ''; + + $aus['transfer3'] .= '
'.Help($lang['L_HELP_SFTPTRANSFER'], '').$lang['L_SFTP_TRANSFER'].': 
'.Help($lang['SFTP'], '').$lang['L_SFTP_TIMEOUT'].':  sec

'.$checkSFTP[$i].'
'; + $aus['transfer3'] .= ''; + $aus['transfer3'] .= ''; + $aus['transfer3'] .= ''; + $aus['transfer3'] .= ''; + $aus['transfer3'] .= ''; + + $aus['transfer3'] .= ''; + + $aus['transfer3'] .= ''; + + $aus['transfer3'] .= ''; + + $aus['transfer3'] .= '
'.Help($lang['L_HELP_SFTPSERVER'], 'conf14', 12).$lang['L_SFTP_SERVER'].': 
'.Help($lang['L_HELP_SFTPPORT'], 'conf15', 12).$lang['L_SFTP_PORT'].': 
'.Help($lang['L_HELP_SFTPUSER'], 'conf16', 12).$lang['L_SFTP_USER'].': 
'.Help($lang['L_HELP_SFTPPASS'], 'conf17', 12).$lang['L_SFTP_PASS'].': 
'.Help($lang['L_HELP_SFTPDIR'], 'conf18', 12).$lang['L_SFTP_DIR'].': 
'.$lang['L_SFTP_SFTP_PATH_TO_PRIVATE_KEY'].':  (optional, default: null)
'.$lang['L_SFTP_SECRET_PASSPHRASE_FOR_PRIVATE_KEY'].':  (optional, default: null)
'.$lang['L_SFTP_FINGERPRINT'].':  (optional, default: null)
'.print_save_button().'
'; +} +$aus['transfer3'] .= '
'; + +// Crondump +$aus['cron'] = '
'.$lang['L_CONFIG_CRONPERL'].''; +$aus['cron'] .= ''; +$aus['cron'] .= ''; +$aus['cron'] .= ''; + +$aus['cron'] .= ''; +$aus['cron'] .= ''; +$aus['cron'] .= ''; +$aus['cron'] .= ''; + +// comment +$config['cron_comment'] = isset($config['cron_comment']) ? $config['cron_comment'] : ''; +$aus['cron'] .= ''; +$aus['cron'] .= ''; +$aus['cron'] .= '
'.Help($lang['L_HELP_CRONEXTENDER'], '').$lang['L_CRON_EXTENDER'].':  .pl'; +$aus['cron'] .= '    .cgi'; + +$aus['cron'] .= '
'.Help($lang['L_HELP_CRONEXECPATH'], '').$lang['L_CRON_EXECPATH'].': 
'.Help($lang['L_HELP_CRONPRINTOUT'], '').$lang['L_CRON_PRINTOUT'].': '; + +$aus['cron'] .= '
'.Help($lang['L_HELP_CRONCOMPLETELOG'], '').$lang['L_CRON_COMPLETELOG'].': '; + +$aus['cron'] .= '
'.Help($lang['L_HELP_CRONDBINDEX'], 'conf14').$lang['L_CRON_CRONDBINDEX'].': '."\n"; +$aus['cron'] .= '
'.$lang['L_CRON_COMMENT'].': 
'.print_save_button().'
'; + +//Formular-Buttons --> +$aus['formende'] = '

'; + +// AUSGABE +echo $aus['formstart']; +echo $aus['db']; +echo $aus['global1']; +echo $aus['global2']; +echo $aus['global3']; +echo $aus['transfer1']; +echo $aus['transfer2']; +echo $aus['transfer3']; +echo $aus['cron']; +echo $aus['conf']; + +echo $aus['formende']; + +echo ''; +echo MODFooter(); +$_SESSION['config'] = $config; +ob_end_flush(); +exit(); diff --git a/msd/css/mod/icons/arrow_down.gif b/msd/css/mod/icons/arrow_down.gif new file mode 100644 index 0000000000000000000000000000000000000000..f96cd2ad86d98be22ef294d8b66dc499a757c116 GIT binary patch literal 132 zcmZ?wbhEHb6krfw*v!KaJ7rhFj!$(b{+90hR(1GS)~0ujr~j|K`D4MwFH5d|OI`D7 z?zzwZ|Nm!z0mYvzj0_As3_2hIkQodth7(SDuHI{rd|+o!ki^+0y$Ns&Uf~@ eC){q2X7qfhZpF>8(19hs`|-2GIaa!Y4AuZX|26#p literal 0 HcmV?d00001 diff --git a/msd/css/mod/icons/arrow_up.gif b/msd/css/mod/icons/arrow_up.gif new file mode 100644 index 0000000000000000000000000000000000000000..7646f6b7fb31f2e2394a8bfe15a092e953e4154f GIT binary patch literal 131 zcmZ?wbhEHb6krfw*v!QcJ7rhZ;a|J%|E@dnH*3?o)HSb4_kAna@oB^5&yA=50|5gn zQ2fcl$iTqGpaT*HnZdwf5OC6S^^vzQjE=Kri7Ifo{n;d(Aiu-6+GYRt V)(7XCHTbf3C8wV}uK*bD11I3>#j0_Bd3_2hkAUhdY0}@pFQZnbMEm)@$ zz~rcNT8~r1Wu35>EmKcZfP#)ejnaWb2Sf~bxl>pkcyO${-r~&FAhWOOJ>#KTg>y~D aj}GSY-233zY`wZTT6ap_`S$Rb%)guD*SE#Dl=tF~hipaYrvmcSYqFnl@$0yrhh6d1Y4Ou%@ziVP z*_E)HZLphe@WeyMk`}nAd(p&{lX_E-ZZ+`6MUHe%n}k~Fr~>-+!r`F;x|?K*aZ1vb z3FGSc)WCR`rn~5<0`SUE+vNAInr*I_YTf4d@Y{js?fTowsK29J`S|m?pKZ^$gy7l1 zy{m%o#YXJLP>`q8uer^9Uq0j7sokn1|NsA4Sy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW003A3EC2ui01yBW06+-50I>)hNU$J51pp8Lz)+xqg%K4B&=45l z2ZIC;C^P_IkRXl^D>68cFl0iB1wJ?m1VP2akq!tt3KXE>0TB)j0szo~!paO8A_y9Z z!eWC100#`@SP;QL!4MFNAm~8?!vG#i3PK1VK!8kv5Hv8jm?XdenF2guq`;vA01GFe zfOv8+NP{T`09<%Mpf3Rh1YOuLu>om;4+C~YxIr)l3>6|J445E NF%Hyd@>OO)06U&TKnDN- literal 0 HcmV?d00001 diff --git a/msd/css/mod/icons/close.gif b/msd/css/mod/icons/close.gif new file mode 100644 index 0000000000000000000000000000000000000000..7646f6b7fb31f2e2394a8bfe15a092e953e4154f GIT binary patch literal 131 zcmZ?wbhEHb6krfw*v!QcJ7rhZ;a|J%|E@dnH*3?o)HSb4_kAna@oB^5&yA=50|5gn zQ2fcl$iTqGpaT*HnZdwf5OC6S^^vzQjE=Kri7Ifo{n;d(Aiu-6+GYRt V)(7XCHTbf3C8wV}3&kGm+-MsnZ zoH@TYZ215GKLZ#j{$ycfVBlrY0r5a)FtFHdIO(~1uSGnA#{IU7rVbWWT?z%OimH4R u*v~{Wy2jlwf26#Jv%$ebrNi;S!wnB~rr8`wFqjl%zP>;0^xH5c25SJl%shVp literal 0 HcmV?d00001 diff --git a/msd/css/mod/icons/edit.gif b/msd/css/mod/icons/edit.gif new file mode 100644 index 0000000000000000000000000000000000000000..bfa895cf311613c09018c8063ae70fba2ecd276d GIT binary patch literal 985 zcmV;~119`ONk%w1VGsZi0QUd@?Y18M`Nj6@qS~AT^xuQwp#k*cjOeHW@XAo|%2Bo_ z8H#aAu9|I=dQ-S17Ot3T^lU@?_{7Y;jJHfoYH@g)gIk+~T87VL@WeyEqg z_u`M+%BYTXPUW~hdR{&Al6fYO7m{;9^wn#)sC((-$+e+z^3-dk9v-@%ZSm7*blOdE z%}ctQWRU({d|y7Ifkn~8lfNYVg~E=)AkqmI?A=I5V6c=20x>*_Fk&l)%Kr@Wn=7rbkx5C2*ot^y7{2 z#zm`-RC2pcjqjYF*Z+YiRsGeruj)>CK-^k6@o`+qe*_qV9c*nGc$C4I@Y(+1Y z9>}?jjk30|n{D~{^Y`oPif~J;fFf+uNsw+eQ%gee*V6z0|9^jffPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW004dfEC2ui01yBW000QK0R0FYNU)&6f@wBPI9RX`76=ZAFdVoh zgq0pCCi?MmL`@9^R3^5d@#2S>Ee1^lnRDR`5IrKk+$dN?LXtK+n6QxJLq-WL1j_^n zFh;|ZD_tTCHA3(N2_0yZILU#7gOm>q2-XRMf)52c2>=`zGQb1D2V9nb5kMeplmY|r z5M+a)i2=3<#KMI^(1MK>cM0SbQlJV5f;_}zaFb=q3@C8&>`_sW%o_$37Q`75!$pUI H0RaFz?te=g literal 0 HcmV?d00001 diff --git a/msd/css/mod/icons/gz.gif b/msd/css/mod/icons/gz.gif new file mode 100644 index 0000000000000000000000000000000000000000..f0adfe5aa5a13ba5136ac4f91db2b80d208234e8 GIT binary patch literal 1468 zcmV;t1w;BrNk%w1VITk?0Qdg@9gD~^o!D(=sIlDhHi*gO>-2o9%~XoRIG^9j;^TR< z+wbx9>i7Roqt`8z)rh&`q_(!a+SLF6i6WcYai+HmSEOad_A-CPEP}{kqO>MxyO_G> zw#nH|d$Uca;WxYT>F@QFy|hiU>Gt^fpQ*gX-QGf!%bc#Qg{;y{kd4LR-P`m0Wu4R1 z=jfxysov!5g|x7%!Q1Bh{`dL( zQ^M)-^!Zr3?!wgOWv0_;p0fY|g>$Q`RDQRVs*@{QYWUuM7x_;pX%t8kUB!uQ80!{QdoVu&&eL^2F)* zvDegpr`z`Y{coeuv*Y+;sOrt(?-ifm>ErIg+}~-G$4GOptIpDo#OSET#G$s(roGqK z=I@xYyxr{Uw$JG=cE5h9&-3~6%h&L}-rSbX^NgUst=8~&q|b4q*}K{9oUF5Xpwelg z(0!`gVy>ao*X7gP=8(zhOPbV&r^!r!!Ej)-rpx4H*Z;1}<(FNo3q|Nf-C+Jvpz(%b3L-s@J8%NZG$UysMB%Erav z^ZEbyX0G0V!Q}t`^@qaf+2HHn;_tD+h!zV)P}XZ zio3aRq^E*A^8LW00930EC2ui03ZM$000R80RIUbNU)&6g9sBUTzGInIB-6E81W&< zh!Gih7#=KQXhWJ{JETZ?V<8q4Sw->`S(zuJ!Y9mJjM0GM&mRX45ZwA;>zj-Mm{6Q@ z@r2-r7f6wcp+KQZm3dIBWXOmu73`S`FT(+<2OOC<+xHqhcK{Yi5IPV_jyFd^f*igX&$RMy;>bt|X zM?{6dJX!$3$uVUZ)JP4vtoK|PG|V9oIV@-r3Kl*Xct(EuNx-0g|2%=sArD|vgh3@- zs9@Q)BVI01+;V-TRgj{{xujyW^bDTWvk zl%OXPd?pdk2ty$Ah7)VlqQM_Fw4qQmfY5P=Abdo6#1WMY0mULaoU@A!N?>4$q6y(a zhaoKN!%71lh#*1`LEHjB7Y7uXVvQ26Op%&GdH92m1{V-;lLG-v0Kg=S;K0H(Sis3p z6h{IGCU>BHpZ!Mn4U(;bsF<*vn4^N+<&Z9D_td#y%Q&0E0L&RB(g6PkeImKk_uA Wh!COZV1S|==wpYyD5s2aKma=kKSqE6 literal 0 HcmV?d00001 diff --git a/msd/css/mod/icons/index.gif b/msd/css/mod/icons/index.gif new file mode 100644 index 0000000000000000000000000000000000000000..678cc1d5e5f258713de7145778b786c2e2afb2c6 GIT binary patch literal 1003 zcmZ?wbhEHb6krfw_|Cxa<$m+G*Nfh6lbgI~`@@BdA5H~+{r;zS(U}R$FR$Bw<=59k zHy(YPwf)tX$34&A{(7;T>BBMCFOPd~&0)SZoB6}3fJZNXuHSj~|^KdT)zMH+U=(wFQ@NV zQT_YF`VY4oKVHfF`{VG76-?Op^Zbe}CwCpb^6ci+OVc>-KKuUa{jXWuUVr=f z_rsSzeT&XqzW4RP&4b6cv~?AFO)7SrkZ0PS<2qsam8v+~zdw&|I&{O-Gnru&49XBt z{K>+|z)-=U19BZGPdIR#VED)(xO{z<1>;yX%=TpEEGM;Z)VOjD*U X44%+3TQTBBMCFOPd~&0)SZoB6}3fJZNXuHSj~|^KdT)zMH+U=(wFQ@NV zQT_YF`VY4oKVHfF`{VG76-?Op^Zbe}CwCpb^6ci+OVc>-KKuUa{jXWuUVr=f z_rsSzeT&XqzW4RP&4b6cv~?AFO)7SrkZ0PS<2qsam8v+~zdw&|I&{O-Gnru&49XBt z{K>+|z)-=U19BZGPdIQKX86b<Su1;1#Fnlag_-swF;_*P7v^6Vr=%9#1xk^k)de;o2F(T(*gyDOi3$N&mEGN Rp7zRE7%ZzfAS}RO4FIssN=yI% literal 0 HcmV?d00001 diff --git a/msd/css/mod/icons/key_nokey.gif b/msd/css/mod/icons/key_nokey.gif new file mode 100644 index 0000000000000000000000000000000000000000..e5cfa65fd00877391ef9a9ed7a82c8bde468167b GIT binary patch literal 547 zcmZ?wbhEHb6krfwc*ekR{rdF>4<0OAw(QcSOS^XM3JMC^y?ghB2@{SUJz7&!)7aR! za^=dcTep7t^l9S6iFI{#XU?2izI=I4PtUt|@2aY*X3Ut8nwr|t(b3h_)!EtU=jWH6 zo_^rKf$iJ3FI>1VA|hhu%$Y?+Mf>;fcXV_-e*F0K>C>BfByXY_wPS^ z`0&S%AMNe!uV24zX=#azi`%wsTX%Q&ix)3WoH%j%^yzu?=7oiYtzNzQ>({RzKYl!N zcRYhhrK6XF-)V&^t( zGASf88gT* p*|uoNu485sRG4kvB4%OjIPK|1HRVBBMCFOPd~&0)SZoB6}3fJZNXuHSj~|^KdT)zMH+U=(wFQ@NV zQT_YF`VY4oKVHfF`{VG76-?Op^Zbe}CwCpb^6ci+OVc>-KKuUa{jXWuUVr=f z_rsSzeT&XqzW4RP&4b6cv~?AFO)7SrkZ0PS<2qsam8v+~zdw&|I&{O-Gnru&49XBt z{K>+|z)-=U19BZGPcU%oXZXk=8*-`xHh@e*aj+DwrOO701bj-N5rNF(hNiZR8 zg+Nm}gS1An)fa){<$YIITg=+_=7rJ`E@hup3W^e}PTg#b3?c#_8V<1VvrKk4AfWj6 zmUy&EgF?iS)2+L*SThtA4zfG)1_do(b!gOWG%De^w_%YoyQG+D${PW}_I5{e=Usay OGP55OX=G$%um%8MDoY~( literal 0 HcmV?d00001 diff --git a/msd/css/mod/icons/key_unique.gif b/msd/css/mod/icons/key_unique.gif new file mode 100644 index 0000000000000000000000000000000000000000..5dfe8325fdadb133d55edeb0854899c37e4cb41e GIT binary patch literal 1001 zcmZ?wbhEHb6krfw_+G*A<$m+G*Nfh6lbgI~`@@BdA5H~+{r;zS(U}R$FR$Bw<=59k zHy(YPwf)tX$34&A{(7;T>BBMCFOPd~&0)SZoB6}3fJZNXuHSj~|^KdT)zMH+U=(wFQ@NV zQT_YF`VY4oKVHfF`{VG76-?Op^Zbe}CwCpb^6ci+OVc>-KKuUa{jXWuUVr=f z_rsSzeT&XqzW4RP&4b6cv~?AFO)7SrkZ0PS<2qsam8v+~zdw&|I&{O-Gnru&49XBt z{K>+|z)-=U19BZGPdIQKWBAA+5CsF?s_w!` z-n!=%TVJ*AeABn!LCpXEf0jN@`+tAI?ZqMc-_6|kqVCD=hX3!+zCJVW>7K^_zu*1; z|DS;}p!k!8k%2*rK?h_E$WIJxEe=xyI5foCPb?HHwNmSB@b>(&(_%8iVaJbwGCU2- zxdLX$1hsoA{$WYsVw=2hLSiyIuUE$}nsWTZu>w{o&b2yv)z2uZMTE@fn5lH+GlVBweBvPp2ufrEleTicEuKhl!q H$Y2csXB3RO literal 0 HcmV?d00001 diff --git a/msd/css/mod/icons/notok.gif b/msd/css/mod/icons/notok.gif new file mode 100644 index 0000000000000000000000000000000000000000..2f3a2162f9f2c69e9323a57ec989b8c4139bd83f GIT binary patch literal 74 zcmZ?wbhEHb6krfwn8?hqBPHeA^5y><8~^|RulSRNk%57kL5BedK=KSs(sTM(o_@=( c%)I3mOUpeO1%Z}FFKVx8E`4=s8!Lk~0Hm54!~g&Q literal 0 HcmV?d00001 diff --git a/msd/css/mod/icons/ok.gif b/msd/css/mod/icons/ok.gif new file mode 100644 index 0000000000000000000000000000000000000000..1ca168e4041e46a42fa36302e495e64ba4e17b8b GIT binary patch literal 138 zcmV;50CoRINk%w1VGsZi0J9GOoVaAG=w`C*ZN&V3rQ=}A?wH8`fz9!px$|?;^P#`^ zdDZr(+4-yB{j=Tsu>b%6A^8LW000jFEC2ui01yBW000Cx@X1N5y*O(Mz?Ohvcp5Ma s4T%AY;;2&xA_xP6g3#s54JLtLAmNK02!unCQz$GevX691l|%plJI1I!q5uE@ literal 0 HcmV?d00001 diff --git a/msd/css/mod/icons/openfile.gif b/msd/css/mod/icons/openfile.gif new file mode 100644 index 0000000000000000000000000000000000000000..bc910491a417488fbd577e57ac60cb5727de9149 GIT binary patch literal 154 zcmZ?wbhEHb6krfw*v!MQYSpT@Z{NOp_3HEI&p&?rc>n(W>({Seym;~b`}hC<|Nr{+ z>+j#cKn4R0DE?$&WMJT7&;bd6%wS+~pK#Jso7Ey&+ws?Y_JRXRvm69^L>KH?pT_8s zd;N{+n?LjX8=49<{*>~ZP`Px1fn(kUb>V^)QLBTehFPmt?7z1%?TqH^^F164)&Tx` BNqhhR literal 0 HcmV?d00001 diff --git a/msd/css/mod/icons/progressbar_dump.gif b/msd/css/mod/icons/progressbar_dump.gif new file mode 100644 index 0000000000000000000000000000000000000000..35292ff76175f27f8d1c4661cde1457c5d9ea73a GIT binary patch literal 780 zcmWkrYe-W87@eYJq+v)u!bqn^W)VT^oDW*Q%3k)cQWME3nb|`vd@FM^l`JWnT3WQ- zy1J|GwtVNF&a|>k_mI@UNTL^784-By!kQn4^W)Al*S)W-f=w%>c z8LJlb9Xx*ox&mr3sB+L%pzAQ!g)T8HO7xb3z5%KP#0luzp_8Gf89hztZp70{P(o1o zpe}>F0P-Bjvmlc|oCFaMA`Wj&u$rK4h4u+FkD-2y{)eEhfyx7w4eBDuD>rV=wF*^B`V7 zW2`C=hd}c&)CS@ph-8~ZZL?oa3_@`ieJvQFnMoZ?gD`Z1NCbHahCX!PgRuwXX&C$Q zqJyyqZ_mTD-MkD6FFCdUOi*>?aybthr4mlgi887zKfj|aBBGs=(eh3zBEMLvEKnD(DlQn- zYc(U<@{lpNvGH<~VZ!vr9AYdDnhAPW%9|;jdw*bV_WkUEcSVIo;R|64g^TlHKfV|J znV(;ncUWGIs#;q7)g~Ddxp)L;<$bzQUG44@S@>JZulGB2PcXf7rHbd3o2bwUV?=Ro zp2shrH|w3Nn>MAY1P;Ej)h#I*ul%n{zf?ThVH%a$KEQkx^7R9NqADd z&^eum-lz+zm~vXDuZrO{c3yWpzm}D#%y?b1-Y;vvcvl*6*we9a&4Um&`O}G$9FQPU z2x`){pXu~JCHYYE(KplQB|m1gw#g-XOU=f)j5-g$J>}lpj#oN}EUq_Yv?JMjdn(f4 m{;ht?!41Ayn!mi=nRU(F_^;tqlxiS1AYs}mX{Wb4E9^fpKFodq literal 0 HcmV?d00001 diff --git a/msd/css/mod/icons/progressbar_restore.gif b/msd/css/mod/icons/progressbar_restore.gif new file mode 100644 index 0000000000000000000000000000000000000000..b9def1801b1526cc0a7994a823272242da45c077 GIT binary patch literal 680 zcmZ?wbhEHblw%NJc*el+bMMrjXAb?`T=w(m+Mfqz{aly)^X#FY+p2%=oAUF_!JkXL zex5z}bN|erD?@)CUG;Oh|Ic-4KbQIcJhtxVk<~xfCjH!6^K*Ub&jT}mZYlq{F6rmd zbw9V&{9G0Gb94F6y;FWJ_xrib@8`a$KX-Nf+&}B*&W@jJlYbst`*Ta#&#l!zkFNf? z)br=M)SoNEeqOxrbCCnX$ODQ$S-_sw0g<3MVPOB?pxe~k(%RPE#Kz6Y&BoZr-OI_@ z&B@5f$u_Nj*39WrY75nvnHQ@G2`yTC~i}fQ)Jk?bI*PTjvd=I zq&3(@Pn^~`d*T%Pxie=^YN)baXT6~+a$WWQ9m)H*@7$KWp&+jy_=^9P{JWR@Z(b{W ze);m%OT9mT|NUoR=92OFFu$vrLv%&Jih@Mv76DnM6vvH8&1|BYNn1`dIlGBTN3Lnu zuqefoL&bO9h6S5cyeH}S&a+T_;KDx3xcJ(V&W9{*bFEmfh&VL0a4dFr)rg(-WyQrs z9t*`}9UES8tmcu3Q7w4*>Y8BG;h?8p3P)Q+GA>(L%s%9JYkMy1+Z^fq{`h z2Pg?NL4kpp<*LC6&((V^UY|APIPj@7hGR~O`l6NF`*hRh75H4vkGN6t8Zw4OUp zj)$d!zpACC)-S58*}cFv->xI7vB%#-J&mK)x2s$zIbpi@tlG&<^YdL6soTv?;qOkW zikZP~GQqjCVflQIwDfH|+E;OG+-nk&f8fx>1db`k>raO7pV=3Fbe_D2%Y?1_ug9Ek fQsh=x{bJF^XD{Ebe)H~QK;)-H!|y*N1sSXX1rL76 literal 0 HcmV?d00001 diff --git a/msd/css/mod/icons/search.gif b/msd/css/mod/icons/search.gif new file mode 100644 index 0000000000000000000000000000000000000000..918abed8a39106a809edaa276a7dc7a386ebc902 GIT binary patch literal 991 zcmV<510eiINk%w1VGsZi0QXn`dR{$~qPMP^Z=!)k{rSb^xIT(-ORRt*nuA;Q_xzQ8 zRpFrl`~3d0!r1ofqU!JZ$FzppoCEaVgYC8+*y8r^&}yxiYO9Y_l5;`y$Rb%)guD*SE#Dl=tF~hipaYrvmcSYqFnl@$0yrhh6d1Y4Ou%@ziVP z*_E)HZLphe@WeyMk`}nAd(p&{lX_E-ZZ+`6MUHe%n}k~Fr~>-+!r`F;x|?K*aZ1vb z3FGSc)WCR`rn~5<0`SUE+vNAInr*I_YTf4d@Y{js?fTowsK29J`S|m?pKZ^$gy7l1 zy{m%o#YXJLP>`q8uer^9Uq0j7sokn1|NsA4Sy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW003A3EC2ui01yBW06+-50I>)hNU$J51pp8Lz)+xqg%K4B&=45l z2ZIC;C^P_IkRXl^D>68cFl0iB1wJ?m1VP2akq!tt3KXE>0TB)j0szo~!paO8A_y9Z z!eWC100#`@SP;QL!4MFNAm~8?!vG#i3PK1VK!8kv5Hv8jm?XdenF2guq`;vA01GFe zfOv8+NP{T`09<%Mpf3Rh1YOuLu>om;4+C~YxIr)l3>6|J445E NF%Hyd@>OO)06U&TKnDN- literal 0 HcmV?d00001 diff --git a/msd/css/mod/icons/table_truncate.gif b/msd/css/mod/icons/table_truncate.gif new file mode 100644 index 0000000000000000000000000000000000000000..fbcb3287d460565d031097db93aba93c60f752ad GIT binary patch literal 979 zcmV;^11$VUNk%w1VGsZi0QUd@VvFSc`NbPfw)X3y^xuQ+wjSD?1L2_o^y7{2%24#< zjPS})m3>w5(`S=`t|YDY5MiT`S|lpecFz6PWtn;x6b?Y)oZMPBDc-= zkZv_KXwCii!N-yoDqh9r*_G(10@K~`V36r&mhQ;8jE8JR(v}H)Uq0~Lfv(5+KyuZh zfkms2RQ&kD+s~cHw1)HSx#zt^=%)hdVZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW003|6f&l;(1S}vh0Ko$wV+cwya3D#6tzCso`9d%SSrG*i za8)4TN)UoaP#mBUcW#3jLIQPYgG4nf6m|kK6%mhu#_^8Wuss;1O`(GDE?$&WMEKX&;dCLlqVcG zDi}ODWIQ%3I3TEC$Fak}p`}BRk*Pt#F@T|sQ$R{ZBH_b>j(!1Er9T2qiyRnb%yb+I z4mdC}$vSeaII!{Y!46^JX)KwW795^$)+{A5;UF_Br=X0%ikpEB?d)7Lq0! zCxtJU-K^IEM|rBZRZT%Atm@pwX^kX$Yo3WW-Vg3sqmrP4qk;B-2@Ua!yR zvsf%*v3PTHb9#DOAP__%5!nCOkHM(_Y{VCSX@W&Z(i`p~5#KlR`|iyY|Jpr=zyF5t zCq+&w7g`@qXs8w0X>TO}{h_FIl=wI6(FKaa9$^0^v43fgG;gbnO5_y0^9sEF}4>6dm;gM1H*!Toz^5Ya? zKbOa!5eR2RV#(b6!s3#2S@zxM6@^lzUe&B=b$Wx*1c2rwi`8a#I9+a!*XIueL*ey} zEfLt(?eR!z&tz51?&MrPwRZ}=D;3#+9DFxjA9tis$sfe<8WT>}#h>i$n`wssLCBr? z@Lt$<_@O`4YIYYxqBHig#+xMNL%Z%%K&k z1iw_uDEU)$`?46M&otB`;hK;^RxtNz1)W*U0o7=Q+pH2YDXSBeTX!U;v#4uRwYyWU z&$0$}yru(LUD?Afw6Oh1k$QHRW|H)ruHS!tgbps^zr|k{jSN9D>P=zS`B8>d!778) z*Ls=uRZi88A1<(2P948FB~Q#AF55bX$?Co^HsXckw4zn<*r?yGnaYd1IL;2bfkk4T zWPB{_cj$!O7xSt%!T~q5Dw&x0_bAbDO=VOpO7l48AYSui=P^K2wJQU@R=wv6YVB#< zZTyWb@#R2d4V_Q}*VZ0vMQNYGv3PAAf`RAMALhb!4M&$zy5}d1c-@OrK0w!)9-E+V zIul!2I`wHWgIfJ?73WEc%5^*-_V*{4jS6>YYrLPZ?zU+zeZvEjc zpz-Zp?jcjh{pA8vXN57y>3ZY?P2H8z$v{taY$4EFn@9lOHKb_(OjAZO*w=EU5PaW$ zn*d@v%FQ6|Ky|Vi-`84b?#E*ZW&(*}HWR7bWMUM3xey|=j09+a?K49ZPIQWe`YCpF za*&@$w0xL723crx87Wr!(v>3Xko;ATm7y$$tV~T!ijAdjEwT**SmLH_}=0+k$o&+B-+QYMDWzKRw?YUa)oQ}Iga&qGz zSe(2BG{VI{SeEaaf#XOn0fK392@mrS?%AWVVz=lUJ;^OT<+r#c>Dy90b7%IJc;=Bw z9KBI7C-%2sl>QmgsB>0v0 zWhH)9g^BD}Kk{4stCic*0-EZ*rGd5Dq=A6;9z_$-HD#s+^(|LRgNF7y13_cQ16$D4 fgH8(peQl*75RV%OnMq7r2%_@R!lvJ0u;2ax1LX2@ literal 0 HcmV?d00001 diff --git a/msd/css/mod/pics/bg-buttons.gif b/msd/css/mod/pics/bg-buttons.gif new file mode 100644 index 0000000000000000000000000000000000000000..1fb2865814990a6453937dc131de1a143d711a42 GIT binary patch literal 1298 zcmZ?wbhEHb+{d8K@STC-?c2BS-@pIx;lsy|A3uNo{N>A+uV24@|Ni~Qj~_pO{`~dp z*YDrI|NQy$_wV0-|Ni~||DRzLjE2B43IQFEUqE?*f#W{|BZrK~h6M+kIfS)hPHb3s zxLrWmYmUdpMMt|OjI-`&7&IL3S8(o<@!YiJM*De2T4&&|uu&bBCi zb?4;f<>%)+G;_&1Y*}$}vBzYs*i&0pUS1xs*lVuWR;|X>5u3B_p4z(l`uc>!U9#TW z*4*5j!M!5(^tQFPw--Dk^2y`?H_fM0Lc%hiPK`)g`E(+G z$;+qH6WTPN%}AM+`D|v!vX#$fC46E@(a9XE`4?7{c&&|EenZz=*>R=VoTLo zw;lIC?5OcufBXH9J$2{b|NhU|*woz8+ScCD+11_C+t)u~;w1JdQ>RUzF>}`JIdkXD zU$Ah|;w4KNmaka3YW146>(+1BxM}m2t=qQmVBWQR&)$9e4;(yn_{h;?$4{I*b(;I^ zx$_q;Ub=kc>b2`PZr-|m=k7h82M-@Te)9C$^A|5)y?*od-TMz8KYjl4_1pI!KOeCD z`TOrb12dP5$A$$5n>mEFVoq#W*wikd>@~+@FEZ^r{>&Tuib#-;b=B&G?wywUuKH+eetoODxH#cWoUKM+K+uGaP3pU!#@ZP@e?(T}uukN1S zzW)CH24-$KpB)<>9_|py)!#EKR>^~xm(V6*Or%; zR|Kz)JG*P^>+2hmPtWt+z3uJo9mTKjo!!0t{rv;Y-12^Vc6@w%VzPGpxjj2SKfkcp Nd%oY^s&@hm)&Rz)PJaLZ literal 0 HcmV?d00001 diff --git a/msd/css/mod/pics/blank.gif b/msd/css/mod/pics/blank.gif new file mode 100644 index 0000000000000000000000000000000000000000..076c7538e60cdfc58adf59c9f231302eaf5ab4a7 GIT binary patch literal 43 scmZ?wbhEHbWMp7uXkcLY|NlP&1B2pE7Dgb&paUX6G7d~kE{qJ;0Lp3yIRF3v literal 0 HcmV?d00001 diff --git a/msd/css/mod/pics/h1_logo.gif b/msd/css/mod/pics/h1_logo.gif new file mode 100644 index 0000000000000000000000000000000000000000..58639180c5bf529c47af05a5cc5f1b039b0449f4 GIT binary patch literal 3505 zcmchU`9IWc7stN_nMN{-lC3dXth3lcl&xs7M2r&I8QY8{hLW*Mwk#q0lCi`@maz|M z8l~(JvW?xK(1MyC_1w?>Cp_ne>$=W$&g*kt@6XY@bWugswiS>Ez5#%!sIZcf;>C*> z^!4?wT)Av+Zf0SDv9!GDvzzmCT?q7Cy5~hC2G550&qv%{jJh8l9{%uQOiD^hR#sMCUT#rQVP$1SZEa0+ zbMtIm#7a!aTvE((Y8)diaV{fiG5hg!ZpLzM`dnc)vmkS_s$#N-4mVd6WwnI2L~sH21iDQ$HvCy zJKL9NFBx4l=9||`J@2M_->>xb%nc4OhX$rchL=V@Fvdn##zz?p26L9Nva+(dx%unY zFW~PmI_{XV%jnSXKwtsB_JLR;5sn!s7 z-piThP;o84-_RXPNqj*^H2*Q{!FoOk9pX{mwY%Ud+s-KAvj~2ymt9wyfLy$yl)`k^ zW1brwHOEl7hFOR(i0I{-lDJY{As7_wHKwUzYa zNlc~f>BlD<{oKc%u~-wdh)h5C(Mm$ChgBwK-Ky=*?|t8z(mq>P1id`M+D*lM>d-wX zb6=KFWlKw6pEr=*M%%SK;tQt92q|D+I7Mt?kF`qu%Ahyl-ysnd~XN!s{Udtj-??!0GR^?|LsjYtln_-fG_7TpTb>h?`=E zE{N5X=NjdzKH%ka7@q9;-R#cW=U8tQ%uz5*y#MZ4K8z2dyl4}aFUpq@e#lT?H$uSL z#WwPg_K!Ti18RcP(L#15;(XOXc)JJu5SQr)nEVfTlyD??CiM6W!9E@yS!WZWW=NRw z;vJZvTEU&n&DR46 zUiA``*bWTIL3LYb9x~us=v#BF_?l2!2!$C}=9v>G*{^kHD8zld3wqn)6}UPMugiDn zM65K2j-q^6Z%LV^G>86`UV3GI=UZar$o*0BST}JFr=oTsca8qXH`=r^T!S@qsNoUvj3dH<5piS~7cL2Ax&#pW% zgP+UxC+oJ?u=DF)yTH%ie6!{tnMT#GD3M0*AB<;55ho?w7S4P~LupSiE5D0|yKCL5 z-kpjycB7t5#tQ)wW+=n&{Pdn!K;mTQDUd`O*iii|-Ffn!JJ}Rz$;F~4a{HUqw6&iL+Ff;KLq)58IDF_y$^XlR-aZ`&FBpeT$upG1v^Gkh}@d5mL>r z`#vU%l*QDAVz4+L;gXKS)_2%Mvz|jh*C)7zC7ZZ~P|EbeBm0i!Q>DzcV)i?TZ^Uc9 zlxaN3VZ%{4L05iR`^Z>XPjOm6F#in5Vf}6_rQ<+(Ep5_FCNfqKUWQN9IMEE*Z>Aa2 zQD(zA+wg{MwLJ8vwNl726dB^iZ*#2nC}`NRFD8|PdXmA?zaUr1_b|I=Mqt^DDlI~E znk(%%p?af3Nn1XSe05sbet-@0-s?fJgZYeW0H=l;F@e4*Q3goqb6usT#;Q2LD~RDb zJLOznDYIUZ*e)e=z6>+^tPDA$McR7CLn~M1xQ!!ZV;%urkDd3_cd@~ypO;RWK z(gN-q<TfB%=GrF3+&s73!M**-G=)HaY7)00($SFF;;A4?}0;aq4BU2ke;SVJI%@k4AbT%)NnA9#nHiMpr6= zzv$Fg&6_x(p2_V^$Vc&Ii=$;t`~X%%1{f3?9eT0_a9e#a zNIer8A=XZ=LTkIP$6zv+Ye~;qI&OzRO&Cy2j(IxJ57Yce7zMBg@mlKHRDIzQ&4v=! zN_SnD5zjt<{bDn*i^D1>23r*=3uYd80gI|kr$a4VF_!-OjV-6eoa?KWJk+c3oGk0< z`g^ZZf}>U@^}We$U|Q(mojD{6ESpco=!f)*G){`bJh;FUYxl{<6Tc~y)RPhzy=!l8 zbC%UoPtB8q4SwEyA_^q2-kp4BQLg?8{-dq=D=wreZ-#?C{q^r_U+}nCPEhAQ4mI;C z0rx43cP1gPHE7zrNz1?iCalYX2s-f7kzKMxkb7+Ci-4|;LwXV~!0|)=h%QjQ52_U) zZRiy`*E};s3#iqVMEd79&xi7+$$JLNwFTnT>mR?Q3m>knld0vtut> zl_~kn?ve?X0}IOjFUUNrCZx=s^;YFtHsV^Kv*oxt&A)LqdiLJ#-SO|VI_h-L5^>k^ z6Biey;Y-}-U0k3E^*clPYraKiS{AS7HV!u&{6?}5T5=%;_9VRb%jO+tibn+wy=D1P{H>U` zFiqoymA;Afo}#Os3A^LlV&7^8f>xr314jkg2s9ZMGr|Aogv9xP3XIi8dQ|fqx@Wsd zp>-*nw`Eb!Xs17uwN+x&vW(gDw2Afgc|r@**=TokjP;Gu)3WB@vpccN`cCC-{TyZV zb4K|054ut7MjCQk69B}2b6Xe~!*p$ZZTD{V^8~!Dy_=&i%+Apn>!|@>Z)$_Xunrn< zjFJJ$0I%cdg4GZ7b~Ft%ylx-pX0Pf1KYtFQPEaFw;5{6DZD9ltcTXQRf(G2y0cYo^ zw)g(?jfBJgw)nbfz_tDqV1H)n;Jh7SN(f~+do&siJEMv~E2${RE6Kv-QD`M3N)?Gl z$)V6{Xq1{f3ieOH&p}}7-VRP`Cb}2@8Mc>cz+HTO@oGqHR+eeAulcwa2e6ZS{b)(&^aR|5{;tHb_B;(`BH z+|vi~mkMEz^FR`8@klfRh5XyRN3W*i?P%+Z^S+M5x&IH+*ahc{^Krr9VLCd0p0(R0sw?>&#%ezSi4+}+v!@%`J^t<8-upV!w` znJddniwpB}vy7SPsmX~?<71;AM}|KP4G#48_4d4f*ZuZQ7yUKuRp-l&c4}K|OLJ3W z1Es#Mw&q23)pK%X#k2CV(h^ef)1tzH{Jh+p?5xZu8R=<{Q&WEZ6?igj^za&)k_v$eTxZFS4?<_!ysx!HA7 zlWWFCh6Y!!T)w2QckzO*j<%NO`EwfTXVp|y&L}G>D#)WzNQ9iMjI@;G=~E|9NWjHm n$HhcNgoTa?9z7z!fA|m|l=mRd0dB~CFxNg#4t5Y50G#~~?CW07 literal 0 HcmV?d00001 diff --git a/msd/css/mod/pics/loveyourdata.gif b/msd/css/mod/pics/loveyourdata.gif new file mode 100644 index 0000000000000000000000000000000000000000..ddcc647c48663b5e8b7be0ee7599df489028ba5f GIT binary patch literal 6340 zcmW-ki$7D3~~Ms9Pz zvr%qQqmo-$$fd-TB4OXp@Av!@UXRD~@pwIrO;73QI^F<6ffW(Z-1EviyZAy_LRQO* zXinYTymIpZc=B!Ir=(*)mX}%Cg$~iF%fEla6%E(o*#QOR2k$&O8xVE+QS|I+*F>E5 zxu94hzwq6zUL!B^9zEj}abL_%je2Gjj1_tu^$PyIx~g|Ku=>sWnQz}t`A6PN&D-bx zz$>fBDK4XZ;GJD$^2PAPk@sD~pFb|a=|he2A;ndrp9QHkO`m6`*Ecpkr=0Z5D~;z= z#aA_b`1Dc7^YPl>zrMMpj|(fNzkZ#p@}=JNW2Wa%wLF=anF%kg`8f9OxKG%9_VYVW zpV1zKx<4(vlb)Yi&*cvdG2nE+e2#ZUesED0GcEsmVpe(sZ(&L3o}BZzpdzuRC8MUH zckumWr0V{geqR^9*L3%dO-v-$whootFKlf);Tt|RH}~bavs+SjRzu7EoYGIZm!0D? zH9UOBr>3qvNf{dd|7j zzpo68D6Q=t9J-hJJm`7ltHHsypQrq?3MXG?e5#N5m~P>nTQ=8UaXYKTIVtxeH~C8J z)APY`@5V+QW730*s}9^|xg@Y(kBsiW;p>r__b{*QdfKzt%0{P{)Io06rwZSHLlb;* zN^)8|?q?RzyhAMlqOC*YQmX2!I=d}H;sf)Ghq^0#b4vzlVf)A=tB7RP>)xB2n|s|J z{8?L@^#Rm9d_O%W;`G?XPa8`6-Zl` z(M;0($L4I$REWE{yn_0F51dp6oYduN0`S$&N{QX-m#n4_4t6+E)lUSyZbPuADz!U2 z-88yRW4FfgBm=)1*Ax#|NJz@4gx!5cwpRvwuf$CjfAl}OFr*r0hAk@5^YQ%>;;cSw zswBVq=*P3AOoi(NnrhmKV;B6R1m*{|b)?c1ogKHO47Tz1o+UOFsv?T!N!soySa81o z!oO`2L}>MGVePGPWB|#Peu^;sfj2a*mHbURam?wPj+ZcRElF*t%yaZp$$&iBwHy|C zya=c<($NHEg1K3>!WG{4uFQWjxmAN33zi=b`LN3!)&VZshGYFR;NIBX|Jnmy*-x+^ ziyOJ3_O70Up70dYJ~&5T;z%XbbSt&uMteWTfn7`%c6AunKS6^3%}l@V&OrzT6#>V2ImD{fI*m zLE;c;i-Fi)rx=f(&-wDcFS9p2gXF~C62Rm{t9 z{-p)=PcrDTFtyMFx*w^9yvWfIAjo19C38`T?FnzH7VvUI{u9o}&+8@J1 z>(7u+(KzAY)6!S_E0=yy%eMVG%3SPNv6?9FJoJucjQe|yH)VY2%CSLW^$v4Z+w2zo zUho@xU+waOdNNkIzwk#V*E6F=P)6O8AJL&Nm7X3o>sc zFUv1?p2|$TH|o*)y-ouDyHocdUx$BM;==Wg81njyfj9bA%YDDbSr>h8HygZj4|-YD zX{XmL3q{=o!`NdEc!qn{ji`DT7XyuzK$JdS5M>PUVe&dDN-p%SE9|8S~JzUZpMh0q- z*=H6n4Vo!*%F+gMaI)Xej!Gi3NV;5EYee zvBq7v4(~U&Z`#=nL|Q#Q?V%pjhqvudT|LqR-U=r5W)ZzjLKHVS1XrUR$-z|>=;(J! z`oA_A$C;u^9@t|m&ARa#ZE{H;8642ovtoou1ta4Y!MpQ<$6jn z|6G)NuamNNx9RVgm#Q|JT_(Heh&>|2I_HolcK=8W^R!cv zR_#`}v?VTeN_me0t@mK=XR$qGmU?d++P{iNsitP5=qdUaDk>82_7tS^>}l8aezA0p z%KlF8;8S-1?0c^9_E(f3KkR?WMCxv>nXq2n-YisX`76@ul8Eq_ZsrWtO)G8nIx0It z4v39s9BKmtm3-UN1Xa%1^<(|vR`F7y%ScQ#Impy1i;xyMqdDh|@OkYh5y1>pYT{vW zT}b2mb0O#Acabj(l^Du&in#kMEin9CfRnnCf(bn;cn2>HV1=RP(i`-&cvr7bDz zbtL!F%IW?VDFK16bNtXzS)7wC5*NoP_E@~aJ0+3~RNeqiIX?^=?rk}+<)t}NQ-Ra* zI+li$r}DNNnT-q_O+cQe9NU&ljoRhmxUD|53ldtD=%G z`J9HJFu7Qear3`3K4@ZK4q+d_BfX;|<}WcCfrb6YnED7u^oOS9s;?Kydl(5BTrftp zo&H3296`;g-()XJoTogIr(J1X6#m1iRTDfC3?h^@3IpUK-|w!g!)b(;AHFsguv_N} z@w)XbpYaG2f@lJcr_v-000igkt{1}!OCDzGNJoS4oA{^Bxx4-cMtzX|sY<8T~ zB_|Qj36^&H`I=k+P7sW=wPHm@PQzM%M2dCJFCwmvA=ED0yz=U}ga6$-BdN~lh}Jo# zJme#$v|LwjxSyq?OhJ1~{=pba-AM~ zzA_W_8DV(s940>Q$`_Mc^#f{*sC@nAxowO(U|s&U-jMyLklwmV=f0mAad|0k)UT+2 zg_rXVY|ObHW{rF(z2|P^w{`A!N3R5Ut5})~MBb}PRr&tQe_kaX<&8G{%D+?f?CJMV zmqszEe?Vm2#@JhH+6#%Ln>XRX?IGp?;Oq2jCM!vUM?|`32?O!^2ahZjbm~uDe6#ja zzXidxI0g;*1)ezTStg^wntr4?W&AT~@yNyVLx&G8zo^|@<(jnoNXlIAuzM_#7V5k8 zECV+hr@dkdbFsnmpEvydM~+yW6?v;}p)lkxBYG`F*ugiOzw8)d(@Ct+tF6GPq4u@9 zH%-4|=phTD9qT(>Zq%qqoQc8AJ@ftk!C#8QB6P7Fe)%uW+(XboDS)tY5vc979Qxp~ zKJzg1!+k?oH*HDP1ZFAmS*k-9_SFSkp^KfQpcfA#qV_obprE;YL`apT&lgJ+jE($_ z+gNer2p?TCwrbxap`=MX$!Do|1?*o9G$99_4;A}I=J5_bL=k`|sfg#4%jV9uznRx) zLw+Yh9WJ>XTc`ai(T{xcgLr7@g2imWzd9l3RY~_;Lo$W9lkCeC0BBPe-I}(^GPLiOiFUZpjTAg5LEu2R6h}sDHCqO03(?u(lr-f4*9GkMvfcbNNy*w zRwPQvTag2J3M;A(i&#y5LZ;%17*ZcsBm7mvq$psI)w!^I{HU?hTtOs&Wkhz7?&fUc z$n4#q;$ZlR%rL|_sdazp$#^m{*7dO|aI1zUs7u7lZ*G?+M${cD_ zbNNhE5piQG?t(>vC^j}Wlkr$u=WK{4aU>*KqZ^JxE7dWz>_F*$Yj=aWI2ef6b`|$D zj-TNpj0<7Y`smYA8m}C8Ea`ZjAC7CaPPj{s=d$sV#__|ov0bYV#rN9v$L^EeMF6%? zZ)hi6zn(BCOkAOft8|NpSpk12Ns6bEl&;;_7*H0`G}alrfkZXeKVfcva8k@i!vcbS zsL69ybOi2y%@~=AOhJ_IOPEVitWQL5bHdW@XQefn`qC>L5=|3mHh$Ye9Jk*y8DXc_)<^z5wWaY-dZFmL z(#WBw4|O9@^%oy8O~SZOBfG7s6VVgfC71xEl4>^{mZA#>%@sZhi6>njj9uJo)t?>w z)0F(6o3x|Ndb$wJ3k%x8czW14a%A`+S(wmkDs|tRy>%x+(=qOfb4n2%@xM7Kh2pCp z>{Y$*Q}}*%?D+frs_Ows^0E@8fHb@%JJANg;h=c+nPoP>Fbwr2_{?yh#sSdt*{2)i zI2&5dA-5bS1i*Kcp3=(Zuu`n8F-q4Rje0C!!{Q^u+0ytcW+SlXaQwM&t(M^!f17MF zCv%WxGE7F4n?n68lTenJ+=`LSl2_+MlA`7NKSwVz^WP3eOAHt4@&Lut zF;2JhT9We&DG@)zJ3B*blG@I<>?~b+d1I*yo-9z-2^!; zYXz&WQQgmDUg_aVpNDV$y4;t%bKrEGvV_W5L6Jdfi6J>120(5q^0!-=U?divey$=Y zKD{e*?zCL!gUs*yo|gzqGlz;`Y8jc&A#*ru4P}e`vLbrv()J9nXzQ30 z8;z4ItDsj%G?qOzDc}6HYhBBKyZeqdKpLQ+r><~RL_#7(ts+j4v$Te^qv9q~EB2+S zY5qz6$1JgyhdyLiek3JM2ZDAMq0d9rI%#oJ4HbcUiN;WhJ8cV|UYR`-SHk4z+SFM2 zRDaorzQ8NAgHkL4PzpXJ=K8gscIAhPYF~H#^*aDZOI+y;)YlrF&trdo&G6miB?rud-9~ZG3AsMxX3Dc6V zJZmkts^@Zf39m{u-6Sn!8wYxVPps5me_mVHm@fxq-#=c^^0(1F71A<`NwaP0Xw0uU zr%^G|WH(x^va^05t@NLBTR${5kBv5e`r9lJ$+b-Aw@lf!O#8IVx;GWWPy_Wrc@eS! zhGdp2^q7Do7iu*_ZDS!8^vT6kDCOV3!zRG8@bjBK>FtHlKilfj$85&L`-yaH6R*+5=2^%)PUtl*;$Jd)2Lk9dle3&hJ(DL9sOTU%R)>XI7C;ASL^UD40LBfl zQGr5G#73_1Nva%l91AfGBflD;Jy^I)WYB^mJ}MNG5TGm={N3q*Hy3@C4Y8;%aFp)v zWKco?WmKZ&%n1%`K$lNwpk!E%p{>VmHL+fn*kjd1EP@f0CkelmB#W*0;_b=!JSu63 zO!`6wsp9=NxO}2`{}u(1l|&%)LB$m033Y@wfFLUjn9W1g0BI1w*T6{ezJaZguf3T3 zAQrlZgYe>DZgU39Si~iY*bx{wKq0rmeI5Xj$v~ri-)_c^3z`TYGUS)$D95jymzYBnk;_`c7;5;D(Y0hKYV`XSPTLoKeSs08EY+|8p zS>P|Sn3pc{Dx>TO8`;4CJp`|~=BRlgGMmnqfP2|&0x|{y_@n`Oye=D5<&%Cgh$k8E z{P?773QikFUK4SMhJ31;!&+DOgdXv!)=Q~9jcC3*BG7n3}6%>4l#(k=$!!! z|A-D9FH-$() z3hrLi^u*rwYHs(8FiL45JR3-(3}ktoh27@Au%qZ zm`MPNw^dWYqk@VsGeQ}S1odu7E}aw8M2d=u^#bS-zf~~{`Adl3=%{ZI;!~Kf40`dU zTtp8?T#t6{cm}~z2xB!80mN|h}p zOC?UiM4S+Ock_E04csq4bn!t;D)<=21+jo0pa)AM-J*gs`^Bf&s0wv5RvyZsi?wPX z?w1hS?Rz`T33D*A)d9Z&0Hai4@y$0(5f|md!n~lNj0A`vA%2P3V?=J#qllH!dW?ji z%K~&l9m!^6dZroty#4A$ z@3Dk{Z=ARbA#z{6r>V>N!Pu*Fg5&k#)7TqkoRrRuGw?BrV0D|bg!CAqs{Zq=GnggdZE~+ S`sSCB_unpZ?2rgR|Nj8-YN{3h literal 0 HcmV?d00001 diff --git a/msd/css/mod/pics/navi_bg.jpg b/msd/css/mod/pics/navi_bg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0554ea1ddc01bcd19533bcb889db1f79b3753745 GIT binary patch literal 16812 zcmeIZby!tf*FL=H?nc^8No=|#1x3131&K|wiA^hlgmk!R>F#b2kP<1CZV;4IK%}I< zjYmD6^Bm9ne!uJe^O;<%IoFIa?|F|g$C_&mxR|_H#AUnbVQm2bDk{J=001xnTqFQM zL6neyR>TGAd)W|CM*dd5Y)eBz|6Y%bs3-njMhrwE`(8%$LZSxHzR3ZA9Hc8h%Oyx` zzl`Mt5E1|;#BU7}-?uU;qC6gC4WND>`w1Zlgur$AgMI1?0IY;Rav5_6QwW2)m4k}| z%*w%$K~|Q5kB3)~flbdEYVP0;V+RnoLSj68VmzV@ydq*eB4Pr(2(bWaGyq^+<~)3S zycyrucyR#$8OT59ZOuUWSw^h>TS7-d`Hmg^*YzPe|G7RSuV2@PjPyeWk{|L<+1P2{ z<=sZ)2-bja`NibLCV=~GO~$e+0P@8afdB0dF!#OecQFG50JxZ#m{^#&SXj73IM_Hu zWCXal1Z0#XBxEEcltj4S()a9-o4;R?@NjVO@bL)o@d-)s@$pG7FZiT?3?cg82)K9w z5Mct!Kn4mD6M#&FghGULF^5Fe3?)XR)dO$j)94Vjf3>}MkD|k zyJtP8gObDPUh4uE5L=f-5L0fZ&>N1)b_5myN3AAPW`yS3YDESYLgZk)>xJ zuOzReSO|f)%aT5>wT5M6XU34BV5(hX(aq}B_p5Y z^FcR;J^K1)+@{&%Y9u19MBz<|7A1>%gERSRpHrsr zS-~i9-pAo0*L6n`DO-1j4WXwRJod`6yim3UH4&J|_1?rIPhRr>S?7Bb33 ztPR5zK4#4OCNb>+jh3fnSUec!-ZCyYM|8@)WE{c2R} z-gb(e9TK;1I=hh^6Mh=e!C6feS(eP1zA9x=`KImoMCyRZa@nD0Bk2x}0fb5zUBAO{ zkz7i*v2~?%K4kw{%AJ(cPZpvFJ%!0i)H6|OvW8MBXKp?-Zk!7)imuV)&95Frk96)C z#e1lVOBFqiUfc_3-8%9%ZWDm+x@1VY+V2yKCgYu zE&!xE-aZE+UBy>ff*N02L9Z8En?1Vz3 zR<>GSre|&N74B1>EqtbHn%qd&FL=>jG#hR!@i4xjn&?F4;Dt}gL9&gR#HR~D;Q}Df z3UEra42czg zkbMDE(rtp^dNgr}9nOx};fg*NLw_~fO6)ed&BZ zSzG0j>w{0vCBAw-xc5kVaK9ydW@GBsq1XPM@?Ee`R#oD1`CGSU?t_*YN+&S>L+CVP za%#281u&-m;uRgtnL&qwcYhdpg>E>$V13 z0In^72RPLT8ZHM!ygH4yqJ{Ky@AB+U@%l0ndQS)QMdhIo;GOu4zfWmcX0ZmrYgP-t zyLzpQ;lJ8JySLXmRp|!)MoGz3@bP3n-JA;4V%xP8VGb+JM{iD?oIkld#6D1U;ea2v z@6$COLR=iDp?t=n-nGwnkHLH7KJTP<6!RY4g+pi^JT3qX_r(P=-vCF;J;%qC=4aIO z;Kq}-1y6KQyE7v$(Rz&6Z>r24xFTjulIx%cTl$c?5!hBr}^O$^~5TFDRsNQMY6@K(3yHoQE@o6fZ)uSSF+x4}*7MudJ_hWJPj~gayz3k1T zXII6K17|kRolg!v9b5qKY~_h24P;X9SLk_n4(E5smWK%;SM4@>Ej1&EeUUJ_1sYJXv>ZtHjXV z=$O5Q^BY=EkEkX>Y-K4KYY`3(O>I5>0>JY*M{jESXzyB87i>#S4SYr>g&XQu$Ww_6 z%K=x1d7~VTRm5ipb}>W7{LCTa&I#K-Zj!+WWolNi|J3NnUo?t%n1fc9u-=66n;5Uk8M+Tn+cHp%PM)>r6vhIwQi2)jDCuO0A=Z!Y9s0KN!cj&^TJ zvCJtTrkk5;XcT9CY-Abox+ft4SLDc<;I*8GPia)x1t8}n=5VuejACDESmGe8h0k39 zZ*S&f^X}2Py}eV0DqK9{1*5(i*~6E{uj#jUyq-Pm?X&c}`Dl%v^{eeL7d`|FE(kA)d3V|K7JZx7(i_0;^HU< z20J*wxGqoU+~yD(WEX%y#Zyg9i{Xx(tGy${`5wc?B9X-Ro=_OK$>oWj+swfp>|x>v z=H=!A1L9I1jwWWd5Elkhh@~}Df@!!3v87}3Tm^!$+NH8&7PV_T<$7%1VqVjX9f273T z9{f)uVJt4-NfQbAQzSRiQs@?{5b1N7r9=^AAbiRR2r;|Fp}^&3(2*(4_GVCUeh;$RMuM6mEr)xS{uXY&uoBZKg7m%ak_51ijHvHWZ8 z<-Y%JiI)!19N`d!1%ymY_|1j6EJRENxCDg-Ot?h(_)NIW`OSHGL`)&VrWT_Arkuad z@#_Nq4Wam-3(@-aNRa%`hxV_szIXb+Rv&_de^%U&L%?5DhUhIOduiYhImGX!a}q>& zCN5rnZ5~lEK>;yA9*)Z|ehBCEx1PT!@UjJ=YikS7A2NRD;QrS0?=lc433C${lmC^J zU;F+mg?}g-ari)-W57QTGXG&iKc5HxmxVtU_;2oyfW%Dnoy{YE9( z5c%ScCO-wplu}lJ*$KJCdDST_`i+@aZwYd&k=LoyV`(63l zx{zz4VhW0S`UZv;F0KKAmTvA=Pl96Oa&l{GyShimJ}mF;$0y|F*VVu58J}2LJvg+s z@$mEt4hc<6dR9=Us<&@)YI<#b_oGtM``S<-)=OqO$B|5hlHr880$8|Om|lV2esMzY=zSR8~(R(JB;ik34lOS7JjECC2xy zDrE|wp%`*b9d}s2UjLxVR>N_#wMMSEnGr%O=G%i5!X6v>Ohmjl)Q@9!->c<~Uh%(W z7jOSJl<4>rsokN=6XQ+eIjxU93Kq zs;}tGn^uUdBT3+7dp~x?5q71>Y)Er>J^yS+Ox#UhL4s_cYLQG(B-B3qq)lumt8jjP z^9Cqw{g7Lm%4o(D3{#nPUfO0e_N3nt>$l;UI%7gLb<|g^kfe=4U%6gx7Uw|CWzX+I z-hKgu64D@D@yWtUL1(+BdbPWbLpDRga9~pE&dw<-+ao&ducW2dugU}rj}DvZ3%%c_ zs>HR|jU$REDPkX)R847*>=z#X^2X~P^bIW9PwjI~?)WjJN8Syo1Hfa=c-G%K;?6Yu z-rdmoqnDDOP@#-U;}Bk*D_kUIo})lxZr^%CzT}LkL(|TGh&sjXiHB)H(FPsE!u2QW z*V(}|HIpzZ8P$37$dE@fjnV0vXHDA{>xMS{d~srMByjP|X#zs^sj;iaike#21$D&_ z7^k?mw@E%2t+3lmM^n?06=wu;TK8l!Dz1m>cp2>Dj2boH_a}UPkFxC5=#cD6cZ%$% z4ks*$8Eg+*Y;6O7V(bt?eZRwzAbOR#CI|E*!;fu!6gJTy37+BC^rm|FW+FtTB2;%5 zW8cJDx-3_HW%DtU>4JNszH8VMv=|!k(hL|#A0YcI;JZN#*(>b&Wj|= zC#q$t<8%DS|}pv<6aXFcMKiXx`_G}giPHX%+kJzyo zFT%dGX1+SD(3r7DwjEA(WY)NzKImOjl{9k-Jm$QoD~z`hk#Z(`aD!;Prv33vz@Ozn zC&A%fGc`<}I7dE!p2YX_Xy)T9ZX4HmuW*o@XYZE127?vroe~IF%87#>2jW`F+E71E z41SxKj+KN49@RqxHp%TQixRaPLv> zLHZa1I(Z)W;O1QGDE$E3zKpr?^PwVMj!$?Tq1sapT3Rxp1A`XtkeLtt6hhr#`C!RF z@#=G?X!z5W=qVa6o7c|xI2+DUSz{i?>in&G-C}Dye2WI@cpV!TKukyX)#+;{k%}<> zCkyToO!xHmylAKJ;QHG)_-+b2pImE;!tTI-T)hS4Ys>^=T@e?d>XKL<5SAHyrxdTd zozI<4j6E_>+h)yZcx!-E*X?U1k)a2xsubBYUHzbeR=R0H<}=MPgCN3gE7so4+vXfD z9WRI(u1XWfL3SFp2Sm(Z&9)96{UOb0pru?cmIbvF-J!KfJ!9(rhn)|ulfm3?XxZKH zZD+(L*ofr*Bw0qf?^sOrQI}{VopAjTiR{qqP<+K!4m0=cLh#toXh$4>a5Vk0kHEf0 zyW4_^h|k<%eAh1Pb&Y=aozpddhiaRonsSNsv$bRaW>Ow{1cWpCrBnL{D}pJd zp4s4QBYK}+sPlj>09Rjl$<--EiL{0q;QO<^noXBeq z5%94ZJQ+XrgYmxd@F)7F02rNQs=H5}t;*_l=T;q-}j4qt)WEEw7e;%#1IFf4etzb6QJV zZV74b>ug|T&bhsceUx=fA^WU&=Q%x#Wl?CNQ}}ZyCwx|pXu71&#eI@m(c8(*YZd74828MUgxvA>W!+n>lI%! z3FV}~EyyQUUmlm<&bVxnN$V0PmH#gR9Nn1W?{bBXW0N9KGo&;wNMGS`ZGFaRSoHP79Q5v^;-i|H*88|d5jjT_&f4s<9#K<# zcM=`T^EP)?cMgMFku19x4ukHx=DEgnNY0~a7*sp$-)sOw>jEKN86H01IaX=jcBi?= zFCU$YS(L%VT=I*OlC!j1L^_U-f|t)TI+3-OB<^;8o@TS<=;QFC!-i4%ib9&DH{Lr; zkCi7jziFfzJJv`66`#hgiBVC zdtHb`ESrWL+iA)-R5v*{)qPJYY3J_l=(eV|1jVbEH(b`Q1$5x=t5=?-wufss;P`Vv z6q=I6zm5^o4l>uSir4#8q@K^=E<4bS3F&@_qj04_$azI#WB% z{EUl}FV1Mz7sE~S3T|Ya)InWDlM8XK9I7fQ$??#sytj<2yx^WxW(!T1&9UCod_>o=nHawR)1E=fIAG5gveQyy@RqUP0p~yc)fVg zfKB^BHR&ZPR%G~1XV1Brfr*5aLFMuE9^r^88ESM??oiao0O4k97pn(7gLgaY*$2Hx zk3zO|U{mqNBC!-&pFEwmm_jTx2PVrOOy> zp@4lgu`ZlJEE;>{t)epXeJ|QJr!sH1ggrHQRmatjWi2WxWWM@}3Jh)rdvhC>WfCeKUb%1a zEBYrqc8Vr$ejF5HHTGYkWY)gTVpZv}gF)uP&m=L%#Fx!ZrzP>?gZOpcd71@Ym6dxg zP{p{j8O`V%`t1)JGPEe_S2tO5?k?0u49(4EdOm{P=Ts!Q07yugyBhF`hi<=JQlN~p zW;k0Egc=nz4Q)sbN#&%J8iel)`4`ig4d^dRs!+WrYhTQd^ozeMlvntS&6Lpw55wQ-ki)K*EI=X(iofCJ-@46Q zEI6$%Gz4o_3W#IlV(&^(Co6!*rZ5qEjPb7G#*9g6w5ByVq#p;S#wTMJ3?{u7+2<&o z(<_U!TFX8XCm_hn3vz_pgka{z%G{JEX%xE#VCm!K?c#o9HeFdc6MhJVuTaIO>X0*mymURAfc<&D2V4uOxNS1D4FZbi7U*a-Gb0H3mi9H%!%Jc&0M_ z53>=E_Vr z!wscK%xz#S?;$$_$`w4asP_C3aDjMwvRL;<&?q`pu!bqES*KOSr`iz9zNEYXkt_wh z-7&J5Tv_p6nzXxjaJ%@Y-`Q6((?*z6h1s}$*#uEY`;|Am2PO4nLl>?kNj^WhHy8Ht z3f@9*ykD|lL5TohO_<7l<8}a?Su0O^Qu!6dX75U4{%;OTqrZ1XY4nkibPe=nxO9!C zUlRB}Jj5Y~-?92~GuTv{;y6A@o)@RymH~F(%+^|RpCHa%@3;x7(qjo_vTSN{k__Z3 zUdqOvMuR0@QvxTzkscS-kH+|0&ll6J)g8teO z)_zlgw{h{2tto!g*nI~6of;&_G71_&odKSzJ1YiUZ&o-{xyr-su4XETu(`4kjcK?c z{^(gRZN{BqAc&Y3!xZk56Nss)GY-e26|au*X4wy7U-i+XO3#Q{E*TsSO`#wdoa0(g z<919*dS<(eGgd07q?MRc(3utXA$>4E`>9UkTr>1E0eW4tkLDE=mR!<6u^Bs!P1u4b zmakY-5PVHq)xVIPRU?=kwF~Xp>Ya~I7w&w}JxX*+u4z!;eKq0OXE;>Is7bu8TbT<5 zJq;gVDT(?#{gKXUgT-efVRxO4?}<#W2pT7qQhw46%+KM~zQeu7pfAt7Zoj(8Y7G_$ z%#nKj!O?TidwSgZd1LpV(~O`l8Ma2y z*OwW)8qPV!J>T$?rjB=#Kw=Mw#LB*xRMDoK*1Drm6+1a*@vuJ?`K2{)iVY36e11Ex z;`D}LuvyT`Q+MrKe!{c&U8$oy3MZJkY387B)3h*M)#}aPT^sSY)%5eFZVSfb&xzv0 zyWy!ae|TW=su3?Y$YDlPt3WAOQ5&wB8p0xHgAoaYma27K02!5LXR7N$XixfsT+A|P z`TF)IA0{Oe2d;lMR7{a2FXq!&6!E4`;wTZE4-gD;XZEv97B$ z$q*#ML{rkvtqIp{0Ae#pDheJ)KoW*AtXXE*T0CDg54>Lf;)24>#*7u%v{Yf=2tsR+ zo+c!9WIB{Svs*oHIiBymhxAH&6|{$uqunsa-vvd(xov@lBQE%J3Swqyc4-KZw|fVX%1+fzJzCb~rB|^xxm5y6$QN*4I^LjmS6qt+RV85o)@sXRnXNdq1 zO-)5Dn=F>u^OlWsT2N~(SckP*#9V`T@T#UxEG5RFU6~l~|Im6Ll9%HvZZp#7qII>1 zE5rgNOe+CWJo|jWc=pNz$Nfr8KgDsg)&NF6B*Vy#tXN#dVB|`Ix|8G?`?cONt5>c| zb8l~78-6O$6QbyML#qo!M(ayW$u#g4YQJ{@jHjjT>E**9$}D%ySnem@W1-$%GRWua zH{fP$Fm)AJOY?Au$BiA5dwQ#DQ&YhL$NbKWLlQI+VgjFvH{7<5j>d9c{F1eOsFQ;S z%aXNHRFA130r}1Lrd7^AY&{fe!Y>ha%q78uL`N?i3cy`n7I0{WjtWIHAO*3lN@hHw zN<~kj>5a3l#IJBYsCsD0bze~PC^nIdg+v9H&VuPJ&n`2Uj;kL_ZFyrmnrC!#e)>x4 zQ|Q~9Gwi~L8`o}NMNg{}Gj|9T>G%;p9K|OE-Zfbv@>$36XugvDGOgG z!Rof5Q@BdaedgG?+jFn!>3Xu{evp_T7ad1}a}0Ignwel?9b=do$(XpvEh<$;1%mab zw4)$JQU~vG-SIC{-f1gTkoo(2%=Y{tl%+}rAfZWLH{aU!-;V4cBW*4*Y)*j8mLj)@(X?ztAc{AYE_Sy1Fj;k z8JTa&YLmrhFxiJFJ*d1@8B%agSR`CZ+8Mx`UHPt5gTlIGFobbB|`+&j2t zs&pE%OV?3*S-FqS!JZ*MELmS=c890yJd!8pDzz97*trMC*c-t*s7qOky>s&{#)B zc``Ff0EWcthajuLT%$m6|MHR*y{op~( zX0(O7?op!i9)yf}FSgz>JyZmx6OV;v+|Wc-pG)x)e5+<~)OpR7fN;Q4%KSWX%(PK@ z`4foGByO_MOEE;bRtVjL@LHIho9Nk>jKWuU7n~0DYV6v>d)iJ1Ti_`UijTTQ*;sT= zt&t@cu-kIv{AoViIe9+w`2JZa^=b2Ai0rem(BWWp!t_Oro~<3$V}^Fr@}fs^{Pt>v zk@;SguLYczjlzzdL;XcuPS|9Y<+*v$6<@4xtgo#tg^Rn?-15?-3jG=#QV0ogkEwdF z+v47M)+cM=?W;I_3n<~4R@C$bF}5ho(A>uAdMdT$V0XYacST~RR>PfVKrz|16C?zs z(bTSuL4#WBB6}d`-bbSJ!8_fZkZjO%EiO;Bf!7vR+a=yIS|8)@OvHiQJ^c|(V8B&25X%9XkXX>>tr*BxmG2fP+6rU;UUPDD zbLsMtkTwYy2I+cqK6YFNsG%$on^6S7#{xoY(jaIigTIa1Tul6S*JxP?`rV(3!Rm6v z?GDggACr};mHV_)y+4PI`P>r7(>~*7PZT4%CoySrFBvt>te`F{ zUboGM>rFE~??>+w+Ub4Yk8BqJ>S;BE;vMC|6%9W#O6_5FQ+-u1ZtR$LEfKGLsD2*y+J+38J~9Ds8(+0Lh>b9_ zhPHO>#x8{&^NKEcAaDaNgGw7f^cW*YJt$yemT|5u1$l+MhODKo*lUm0f7oKn|G1!I zho<{x?C0RhN6DrlQ>b7 z6?;VrW4CTAF+%+7(apV&{-%}PQ7fmc+!o4)Rhf)kyLg-)EQ~q=iM}D@Y49hk*t-0T za(9wIX0TV5EMbG)KHPH_Mo2R+lbvkT@g<}Ci!@XHu2{z=B0MoW@m;4|Dl+BGM=Bc@ zyAn=Scj^t_Ml<8FHB3uGZ`IQ<$!Mdoy|{6!FWq1we04oNlZrJ{g0#oFS`xY;yW|-| zzJab6>J^X&FD6PrJK|*Z;!xs&M+6O^UUN7`ydZ&+ji&f%U(Q_-_h!%L-Mx0jcf57HQ3 z{OZl=B0$QUON)m`&8gmrt4Ksd1nB6f1Asn07NrRu10%r9P6)gJga8pFnTdrv#hT+; zxomI+K85QF;tESPYP5O34G^~d&(T30*zw}2pcm~&dF3fe7Ta838Q*;~q{qs` ziKCp3OOEW}x;Fr9VVUYm#Y_C5K^-bV_Ve ziT%AhJlVdfxwU3<#gRa+aTol4#BGV2UK$@0a%etJag=!8D=57D2eo3+5v<*7JmNN_ zQp;=`p==-=ija<130Gq88spYaIG6L~*GL9qmqE1NmEGjnTe`}Mt}=cSu+ z-kU`z5i#x7F>9IZddT;OzMM+2@o=`k_0g$GO6Kd1t-$1sOwwIyn0?!;#}k2WNvFdl zpv$i2IudMh=frk{3oM7|(aC`Rs|q`N*Y8Oo|GA^zdGgao%6 zmSxfqj-ESTPKbDkL48?GSEBONj+l0xP$xpIDRc{kQl6o=yuKcGORZ@<%(@=zr!7ca zPZ)3cyvgx~%Z3=-mpC9Do4D3jRoJ32qE6#%5CIwJ!a3u@{2-=wMkQZ}Ys$~t563AJ1- zYSLlENT#CdOJa6W_^FnNfdIZ-n z$Y|MbkIT>b$zV+qC;&qxD@xkz|M+tKOXbDy{4q1k$ zMgSZY%22K~S{W=()m$TX%?~-1RZXed{vDqt*y~#FR~;k2^`LWdj(Q{~ zP#4`>L)AgM01|sX^tnYN;pg}chNKpraim-T?7F&xub^}NPf|1(;&L%@uoXSXXk`_6 zfFz7a$ToPqXg3aWn4p`!To*CRu+B3R>fSh9APMSma|Nk_teNf`f)$(Oqnp5S)?b3u zDg@d;zj~Ecl^K=mj;@W z?ikw%(9Qq=oh|zdDQ`T$Dq|l5ZAFkO+3AOFzmr(C2K8ogz?o5P|1Dzsz3pFuL%%5E Pw@>BY{hfu+#nk@?jE9Ub literal 0 HcmV?d00001 diff --git a/msd/css/mod/style.css b/msd/css/mod/style.css new file mode 100644 index 0000000..b0ffb9f --- /dev/null +++ b/msd/css/mod/style.css @@ -0,0 +1,664 @@ +/* ---------------------------------------------------------------------- + + MyOOS [Dumper] + http://www.oos-shop.de/ + + Copyright (c) 2013 - 2022 by the MyOOS Development Team. + ---------------------------------------------------------------------- + Based on: + + MySqlDumper + http://www.mysqldumper.de + + Copyright (C)2004-2011 Daniel Schlichtholz (admin@mysqldumper.de) + ---------------------------------------------------------------------- + Released under the GNU General Public License + ---------------------------------------------------------------------- */ + +/* + @MySQLDumper STYLESHEET + @name mod + @copyright MySQLDumper - Daniel Schlichtholz + @author Ingo Wagener , Daniel Schlichtholz, Christian Gresshoener + @date 2008-02-28 13:39:07 + @lastmodified 2009-09-15 00:19:30 + @media screen and projection +*/ +*,.nomargin { + margin: 0; + padding: 0; +} + +label { + cursor: pointer; +} + +img { + border: 0; + padding: 0; + margin: 0; +} + +body { + font: 12px/1.5 Verdana, Helvetica, sans-serif; /* Important IE Bugfix: No spaces in font-attribute between px and slash */ + border-top: 5px solid #256777; + background: #fff url(pics/bg-body.gif) repeat-x 0 5px; +} + +body.content { + min-width: 540px; + padding: 0 18px; +} + +.menu-frame { + background-color: #FFF; + color: #000; +} + +/* LINKS */ +a { + color: #256777; +} + +a:hover { + color: #E87B00; + text-decoration: none; +} + +a.ul { + text-decoration: underline; +} + +/* general */ +#pagetitle { + color: #256777; + font-size: 1.8em; + padding-top: 2px; + margin-bottom: 20px; + border-bottom: 1px solid #c7c7c7; +} + +#server0 { + position: absolute; + bottom: 4px; + text-align: center; + left: 10px; + color: #000; +} + +#server1 { + position: absolute; + right: 16px; + text-align: center; + padding: 10px; +} + + +.small { + font-size: 11px; +} + +.ssmall { + font-size: 10px; +} + +.success { + color: green; + font-weight: bold; +} + +.error { + color: #E87B00; + background-color: yellow; + font-weight: bold; +} + +.explain { + text-decoration: none; + border-bottom: 1px dotted; +} + +.explain:hover { + cursor: help; +} + +.active_db { + font-weight: bold; + border-bottom: 1px dotted; + color: #9AA2B1; +} + +table { + color: #000; +} + +table.bdr,.bdr { + border: 1px solid #ddd !important; + border-collapse: collapse !important; +} + +table td { + text-align: left; + vertical-align: top; + padding: 0 6px; + font-size: 12px; +} + +table th { + padding: 0 6px; +} + +/* MENU */ +#menu { + text-align: left; + position:absolute; + margin: 0px; + top: 154px; + left:0; + width:190px; + height:160px; +} + +#menu ul { + margin: 0px 0 0 0; + list-style: none; + background-color: transparent; +} + +#menu ul li { + border-bottom: 1px solid #ddd; +} + +#menu ul a { + padding: 0 10px; + display: block; + line-height: 2; + text-decoration: none; + outline: none; +} + +#menu ul a:hover { + background: #eee; + text-decoration: none; +} + +#menu ul li.active { + border-bottom: 1px solid #256777; +} + +#menu ul li.active a,#menu ul li.active a:hover { + color: #E87B00; + font-weight: bold; + text-decoration: none; + background: transparent; +} + +#menu ul li.icon-holder { + margin-top: 3em; + border-bottom: 0; + text-align: center; +} + +#menu ul li strong { + text-align: left; + color: #256777; + font: normal normal .8em/1.4em verdana, sans-serif; + display: block; +} + +fieldset { + margin: 10px; + padding: 5px; + border: 1px solid #ddd; + color: #256777; +} + +body.content legend { + font-weight: bold; +} + +body.menu fieldset p { + margin: 5px 0 0; + text-align: center; +} + +/* MAIN */ +#topnavi { + list-style: none; + margin: 10px 0 20px; +} + +#topnavi li { + float: left; + margin-right: 6px; +} + +#topnavi li a { + float: left; + font: 1.1em verdana, arial, sans-serif; + border: 1px solid #ddd; + background: url(pics/bg-buttons.gif) repeat-x; + color: #E87B00; + padding: 3px 6px; + vertical-align: bottom; + cursor: pointer; + text-decoration: none; + white-space: nowrap; +} + +#topnavi li a span { + color: #256777; +} + +#topnavi li a:hover { + color: #256777; +} + +#topnavi li a:hover span { + color: #E87B00; +} + +#content p { + margin: 10px 0; +} + +/*Tabellen */ +table tr.dbrow { + background: #FCFDFD; +} + +table tr.dbrow1 { + background: #F8FAFB; +} + +table tr.dbrowsel { + background: #F9F3ED; + color: #256777; +} + +table tr.dbrowsel a:hover { + color: #E87B00; +} + +table td.treffer { + background: #000; +} + +/* Treffer bei der MySQL-Suche */ +table tr.dbrow .treffer,table tr.dbrow1 .treffer { + color: yellow; + background-color: #E87B00; +} + +table.border { + border: 1px solid #738C88; +} + +table td.sum { + background-color: red; + font-weight: bold; + text-align: right; +} + +table tr.thead th,table tr.thead td { + background: url(pics/bg-buttons.gif) repeat-x; + border: 1px solid #ddd; + color: #256777; +} + +#configright table a { + font-size: 11px; + color: #E87B00; +} + +#configright table tr.dbrowsel a { + color: #E87B00; +} + +.tdcompact { + width: 100px; + height: 16px; + overflow: hidden; + font-size: 11px; +} + +.tdnormal { + white-space: nowrap; + padding: 1px; +} + +.sqlheadmenu a { + +} + +/* FORM-Elements */ +#content .Formbutton,#content .ConfigButton,#content .ConfigButtonSelected,#content .Formtext { + margin: 0px 6px 0px 0px; + font: 1.1em verdana, arial, sans-serif; + border: 1px solid #ddd; + background: url(pics/bg-buttons.gif) repeat-x; + color: #E87B00; + line-height: 14px; + padding: 2px; + white-space: nowrap; + overflow: visible; + text-decoration: none; +} + +#content a.Formbutton { + padding: 3px 6px; + vertical-align: bottom; +} + +.Formbutton { cursor: pointer; } +.Formbutton:disabled { cursor: default; } + +#content .Formtext { + overflow: hidden; + cursor: text; +} + +#content .ConfigButtonSelected { + color: #256777; +} + +#content .ConfigButton,#content .ConfigButtonSelected { + width: 160px; + margin-bottom: 4px; + cursor: pointer; +} + +#content .SQLbutton { + font-size: 11px; + background: #E4E9E8; + cursor: pointer; +} + +/* htaccess edit area */ +#content textarea #thta { + font-size: 11px; + color: blue; + width: 400px; + height: 300px; + overflow: scroll; +} + +#content textarea { + width: 100%; + background: #FFF; +} + +input.radio,input.checkbox { + background-color: transparent; +} + +/* Colors for Formelements */ +input.text,input.small { + border: 1px solid #256777; + background: #fff; + color: #000; +} + +select { + background: #FFF; + color: #000; +} + +textarea { + background: #B3C2C0; + color: #4E5665; +} + +/* disabled textarea when editign rows in SQLBrowser */ +.off { + background-color: #ccc !important; +} + +/* for Geckos */ +input[disabled] { + color: #888 !important; + border: 1px solid #CAC8BB !important; +} + +/*horizontal menu */ +#hormenu ul { + border-bottom: thin solid #D5DDDC; + margin-bottom: 8px; + width: 80%; +} + +#hormenu ul li { + display: inline; +} + +#hormenu ul li a { + font: 11px/20px verdana, sans-serif; + text-decoration: none; + color: #4E5665; + padding: 0 14px; + border: 0; +} + +#hormenu ul li a:hover { + color: #E87B00; +} + +#hormenu ul li#active a { + font: bold 11px/ 20px verdana, sans-serif; + text-decoration: none; + color: #E87B00; + float: left; + padding: 0 14px; +} + +#hormenu ul li#active a:hover { + color: #E87B00; +} + +/* special elements */ +.MySQLbox { + font-size: 10pt; + padding: 0px; + background: #000; + color: #fff; + border: thin solid #999999; + height: 200px; + width: 100%; + text-align: left; + overflow: auto; +} + +#content #sqlheaderbox,.sqlbox-warning { + white-space: nowrap; + vertical-align: top; + padding: 8px; + background: url(pics/bg-buttons.gif) repeat-x; + border: 1px solid #ddd; + color: #256777; + line-height: 22px; +} + +#sqlheaderbox .Formbutton { + line-height: 14px; + padding: 2px; + margin: 0px; +} + +#sqltextarea { + margin-right: 30px !important; + width: 100% !important; + overflow: auto; +} + +#content #sqleditbox { + border: 1px solid #738C88; + background: #EEEEEE; + margin-bottom: 10px; +} + +#content #sqleditbox form { + margin: 10px; +} + +#content #sqleditbox p { + background: #A5B6B4; + font-weight: bold; + text-align: center; +} + +#content #sqlnewbox { + border: 1px solid #738C88; + background: #E4E9E8; +} + +#content #sqlnewbox p { + background: #A5B6B4; + font-weight: bold; + text-align: center; +} + +#content #sqloutbox { + font-size: 11px; + width: 700px; + padding: 6px; + background: #D5DDDC; + border: 1px solid #738C88; + overflow: auto; +} + +#content p.autodel { + font-size: 11px; + border-bottom: 1px dashed #fff; + margin-bottom: 12px; +} + +#content .Logbox { + font: 12px/1.2 "Courier New", Courier, monospace; + padding: 6px; + border: 1px solid #ddd; + height: 320px; + width: 90%; + text-align: left; + overflow: auto; +} + +#content .Logbox span { + color: #738C88; +} + +#content .backupmsg { + padding-left: 20px; + font-size: 11px; +} + +#content .backupmsg .success,#content .backupmsg a { + color: #999; + font-size: 11px; +} + +#content .backupmsg .error { + color: red; +} + +/* TEXT */ +h1 { + font-size: 16px; +} + +h5 { + font-size: 1.7em; + font-weight: normal; + color: #256777; + margin: 20px 0 10px; +} + +h6 { + font-size: 1.5em; + font-weight: normal; + color: #256777; + margin: 10px 0; + padding-left: 4px; + background: url(pics/bg-headings.gif) repeat-x; +} + +/* Config */ +#configleft { + float: left; + width: 180px; +} + +#configleft .Formbutton { + width: 160px; + margin: 4px 0; +} + +#configright { + margin-left: 180px; +} + +#footer { + margin-top: 36px; + padding: 10px 0; + border-top: 1px solid #eee; + font-size: 10px; + text-align: right; +} + +#version { + position:absolute; + top:60px; + left:0; + margin:0; + padding:0; + height:90px; + color: #4E5665; + z-index:10; +} +.version-line +{ + position:absolute; + top:-20px; + left:0; + width:190px; + text-align:center; + font: 11px/20px verdana, sans-serif; +} + +#version a:link, +#version a:active, +#version a:hover, +#version a:visited +{ color: #4E5665; } + + +#selectConfig +{ + position:absolute; + top:360px; + left:0; + width:190px; + height: 196px; + z-index:5; +} + +#footer a { + text-decoration: none; +} + +#footer a:hover { + text-decoration: underline; +} + +#ilog { + border: 1px solid #ddd !important; + padding: 12px; + background-color: #fcfcfc; +} + +.right { + text-align: right; +} + +.center { + text-align: center; +} \ No newline at end of file diff --git a/msd/css/mod_green/icons/arrow_down.gif b/msd/css/mod_green/icons/arrow_down.gif new file mode 100644 index 0000000000000000000000000000000000000000..825c9e80204868c4813fdbbb9a7f3e3eed3fb2b5 GIT binary patch literal 859 zcmV-h1El;%Nk%v~VGIBb0QUg^|Ns90005Mfl!SzY|NopqLPE~YoSb6-&YS@M&H(>X zQvUz|oB#lvoMX;00LI3~|NqYaoX-FM{}U4v6ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW00000EC2ui01N;P00#&-009UbNU$IP0}Kop9Eeci!h;DPLUg#W lp+N%*En*zVfWbrn9T_HUXi;Rthy@XLq?mG}#DxI?06Vnhv#J09 literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/arrow_up.gif b/msd/css/mod_green/icons/arrow_up.gif new file mode 100644 index 0000000000000000000000000000000000000000..701b71411077d650d4a0823bf7101b6730623e00 GIT binary patch literal 857 zcmV-f1E%~(Nk%v~VGIBb0QUg^|Ns90005Mfl!SzY|NopqLPE~YoSb6-&YS@M&H(>X zQvUz|oB#lvoMX;00LI3~|NqYaoX-FM{}U4v6ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW00000EC2ui01N;P00#&*009UbNU$IP0|*TYG{B&t0fP)9GI%&J jqQ!{=8y>`{apA*=2PqN+sc@r6h!{(fRJgLF!GHieR0Fh; literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/arrowdown.gif b/msd/css/mod_green/icons/arrowdown.gif new file mode 100644 index 0000000000000000000000000000000000000000..32d7ceb398e28330cc2c87cdf1ec8e0c73585366 GIT binary patch literal 65 zcmZ?wbhEHb6krfwXkY+=|Ns9h{$ycfU|?j>0r5dH3``O|{VPwuS2@!(; literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/arrowleft.gif b/msd/css/mod_green/icons/arrowleft.gif new file mode 100644 index 0000000000000000000000000000000000000000..560043d1693cad7535eb07b168bada343b23b9df GIT binary patch literal 878 zcmZvbKWGzi6vw~y4`h&9q=E@8DDn#^4(0|GYziW@znse6ZpbfDllClTM&Vj0g0) z^xBk)qCwORY6+?#Dhk3uk$TK{NMFD0H%q=MQB4=aPqKNgQ+5^AQVtVIC)sd$?~v>nn5)|C4^8YI(b+`V9-q9 zhOiYZCl8A-21$Z2gi>(Qo^i~fPwlZU*~{EIVVLG9=D=syV_Q-QHgLi)O&v4#>3j4f zrJ%@mH*^}{Wd*C*`TN&$HJI;$XL_w)9X=2hlU-+N|!B%>?jr{lgYus!T$dK z-rnBs?(X*Xb`(XOPA3S0_4V~;v$?vu>bmaA%8G5cxnW^ju zZe)_(#B$*QrV4ZN^X1mU+Y8qp^=_WO_v+kyY2o|E`){vjFFu|t#P0o<^VPB`Os+0% mJb&2v`Dx9S@2=Iq%>I71HF$IDY4qaK+OMx4&YnJnyZ-^^rL;Bx literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/arrowup.gif b/msd/css/mod_green/icons/arrowup.gif new file mode 100644 index 0000000000000000000000000000000000000000..e9eba1fbab6eeb15b5fd53003be4c2b8002aaa5e GIT binary patch literal 64 zcmZ?wbhEHb6krfwXkY+=|Ns9h{$ycfU|?j>0r5dH3{2uZ{VPwu<(KYYjL6u0dd|o7 OHA=42o>hu4SOWm~%o3Xb literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/blank.gif b/msd/css/mod_green/icons/blank.gif new file mode 100644 index 0000000000000000000000000000000000000000..076c7538e60cdfc58adf59c9f231302eaf5ab4a7 GIT binary patch literal 43 scmZ?wbhEHbWMp7uXkcLY|NlP&1B2pE7Dgb&paUX6G7d~kE{qJ;0Lp3yIRF3v literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/browse.gif b/msd/css/mod_green/icons/browse.gif new file mode 100644 index 0000000000000000000000000000000000000000..e52271a20506f29f1a5152f7f0caae34d9e13bd8 GIT binary patch literal 995 zcmV<9104KENk%w1VGsZi0QUd@HA!Q#!q~3A)cf$SlcKkfr_}W0jQ#n=jiSt&x9arc zjhw5&=%)fUN@VuyqNB3M@zZDRwjP(JyNYp2%)N}8gj)Oh^_qiQ&$xutzx|?L`@A~ZU`{vn|^wn#>qg>P9_3`xl@bvtUr_`F<+!7Cp>eL5YIVZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW003zKEC2ui01yBW000QU0MiH@NU&g<009IbL@2=fu~D~GLWF-h!z_nNC03FghrR500?k^;&Mk301A8%F+-4mhYw;> zY&e6KOb;(@0qETez`z(QXb65eQG!7PCsQ04fbgUO!7MF|mX zQvUz|oB#lvoMX;00LI3~|NqYaoX-FM{}U4v6ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW00000EC2ui01N;P00#(703Qe(NN^y4g9sA}1R$W{!G;0=EEG^6 zBEyOjA26gCAV9^43l9cJkRSlUkN`%OJgHG6Ns|XBa`fnMBF2j#H9~w?vt>es1%o~a G2mm{uV7bKr literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/delete.gif b/msd/css/mod_green/icons/delete.gif new file mode 100644 index 0000000000000000000000000000000000000000..131f69e7bb0e0c964c80f133af8561618267591f GIT binary patch literal 944 zcmV;h15f-%Nk%w1VGsZi0QUd@t~)!`JUp;FJMoT;y*xa$JUqobJnn^pDUN$h=l;$dL2J3HoVYuQLhzC1kFLPGJ8kJmvz*hfdT zJUr@ncgQVZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW0027xEC2ui01yBW000P$0Cxx+NU)&6g9jm4C^%q-K?pT&j2OsZ z1BU|^uxOF)2oS;N&$Lhl Sb^tpNtwFT{@ghVR5CA(M-UEjK literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/edit.gif b/msd/css/mod_green/icons/edit.gif new file mode 100644 index 0000000000000000000000000000000000000000..bfa895cf311613c09018c8063ae70fba2ecd276d GIT binary patch literal 985 zcmV;~119`ONk%w1VGsZi0QUd@?Y18M`Nj6@qS~AT^xuQwp#k*cjOeHW@XAo|%2Bo_ z8H#aAu9|I=dQ-S17Ot3T^lU@?_{7Y;jJHfoYH@g)gIk+~T87VL@WeyEqg z_u`M+%BYTXPUW~hdR{&Al6fYO7m{;9^wn#)sC((-$+e+z^3-dk9v-@%ZSm7*blOdE z%}ctQWRU({d|y7Ifkn~8lfNYVg~E=)AkqmI?A=I5V6c=20x>*_Fk&l)%Kr@Wn=7rbkx5C2*ot^y7{2 z#zm`-RC2pcjqjYF*Z+YiRsGeruj)>CK-^k6@o`+qe*_qV9c*nGc$C4I@Y(+1Y z9>}?jjk30|n{D~{^Y`oPif~J;fFf+uNsw+eQ%gee*V6z0|9^jffPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW004dfEC2ui01yBW000QK0R0FYNU)&6f@wBPI9RX`76=ZAFdVoh zgq0pCCi?MmL`@9^R3^5d@#2S>Ee1^lnRDR`5IrKk+$dN?LXtK+n6QxJLq-WL1j_^n zFh;|ZD_tTCHA3(N2_0yZILU#7gOm>q2-XRMf)52c2>=`zGQb1D2V9nb5kMeplmY|r z5M+a)i2=3<#KMI^(1MK>cM0SbQlJV5f;_}zaFb=q3@C8&>`_sW%o_$37Q`75!$pUI H0RaFz?te=g literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/gz.gif b/msd/css/mod_green/icons/gz.gif new file mode 100644 index 0000000000000000000000000000000000000000..f0adfe5aa5a13ba5136ac4f91db2b80d208234e8 GIT binary patch literal 1468 zcmV;t1w;BrNk%w1VITk?0Qdg@9gD~^o!D(=sIlDhHi*gO>-2o9%~XoRIG^9j;^TR< z+wbx9>i7Roqt`8z)rh&`q_(!a+SLF6i6WcYai+HmSEOad_A-CPEP}{kqO>MxyO_G> zw#nH|d$Uca;WxYT>F@QFy|hiU>Gt^fpQ*gX-QGf!%bc#Qg{;y{kd4LR-P`m0Wu4R1 z=jfxysov!5g|x7%!Q1Bh{`dL( zQ^M)-^!Zr3?!wgOWv0_;p0fY|g>$Q`RDQRVs*@{QYWUuM7x_;pX%t8kUB!uQ80!{QdoVu&&eL^2F)* zvDegpr`z`Y{coeuv*Y+;sOrt(?-ifm>ErIg+}~-G$4GOptIpDo#OSET#G$s(roGqK z=I@xYyxr{Uw$JG=cE5h9&-3~6%h&L}-rSbX^NgUst=8~&q|b4q*}K{9oUF5Xpwelg z(0!`gVy>ao*X7gP=8(zhOPbV&r^!r!!Ej)-rpx4H*Z;1}<(FNo3q|Nf-C+Jvpz(%b3L-s@J8%NZG$UysMB%Erav z^ZEbyX0G0V!Q}t`^@qaf+2HHn;_tD+h!zV)P}XZ zio3aRq^E*A^8LW00930EC2ui03ZM$000R80RIUbNU)&6g9sBUTzGInIB-6E81W&< zh!Gih7#=KQXhWJ{JETZ?V<8q4Sw->`S(zuJ!Y9mJjM0GM&mRX45ZwA;>zj-Mm{6Q@ z@r2-r7f6wcp+KQZm3dIBWXOmu73`S`FT(+<2OOC<+xHqhcK{Yi5IPV_jyFd^f*igX&$RMy;>bt|X zM?{6dJX!$3$uVUZ)JP4vtoK|PG|V9oIV@-r3Kl*Xct(EuNx-0g|2%=sArD|vgh3@- zs9@Q)BVI01+;V-TRgj{{xujyW^bDTWvk zl%OXPd?pdk2ty$Ah7)VlqQM_Fw4qQmfY5P=Abdo6#1WMY0mULaoU@A!N?>4$q6y(a zhaoKN!%71lh#*1`LEHjB7Y7uXVvQ26Op%&GdH92m1{V-;lLG-v0Kg=S;K0H(Sis3p z6h{IGCU>BHpZ!Mn4U(;bsF<*vn4^N+<&Z9D_td#y%Q&0E0L&RB(g6PkeImKk_uA Wh!COZV1S|==wpYyD5s2aKma=kKSqE6 literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/index.gif b/msd/css/mod_green/icons/index.gif new file mode 100644 index 0000000000000000000000000000000000000000..678cc1d5e5f258713de7145778b786c2e2afb2c6 GIT binary patch literal 1003 zcmZ?wbhEHb6krfw_|Cxa<$m+G*Nfh6lbgI~`@@BdA5H~+{r;zS(U}R$FR$Bw<=59k zHy(YPwf)tX$34&A{(7;T>BBMCFOPd~&0)SZoB6}3fJZNXuHSj~|^KdT)zMH+U=(wFQ@NV zQT_YF`VY4oKVHfF`{VG76-?Op^Zbe}CwCpb^6ci+OVc>-KKuUa{jXWuUVr=f z_rsSzeT&XqzW4RP&4b6cv~?AFO)7SrkZ0PS<2qsam8v+~zdw&|I&{O-Gnru&49XBt z{K>+|z)-=U19BZGPdIR#VED)(xO{z<1>;yX%=TpEEGM;Z)VOjD*U X44%+3TQTBBMCFOPd~&0)SZoB6}3fJZNXuHSj~|^KdT)zMH+U=(wFQ@NV zQT_YF`VY4oKVHfF`{VG76-?Op^Zbe}CwCpb^6ci+OVc>-KKuUa{jXWuUVr=f z_rsSzeT&XqzW4RP&4b6cv~?AFO)7SrkZ0PS<2qsam8v+~zdw&|I&{O-Gnru&49XBt z{K>+|z)-=U19BZGPdIQKX86b<Su1;1#Fnlag_-swF;_*P7v^6Vr=%9#1xk^k)de;o2F(T(*gyDOi3$N&mEGN Rp7zRE7%ZzfAS}RO4FIssN=yI% literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/key_nokey.gif b/msd/css/mod_green/icons/key_nokey.gif new file mode 100644 index 0000000000000000000000000000000000000000..e5cfa65fd00877391ef9a9ed7a82c8bde468167b GIT binary patch literal 547 zcmZ?wbhEHb6krfwc*ekR{rdF>4<0OAw(QcSOS^XM3JMC^y?ghB2@{SUJz7&!)7aR! za^=dcTep7t^l9S6iFI{#XU?2izI=I4PtUt|@2aY*X3Ut8nwr|t(b3h_)!EtU=jWH6 zo_^rKf$iJ3FI>1VA|hhu%$Y?+Mf>;fcXV_-e*F0K>C>BfByXY_wPS^ z`0&S%AMNe!uV24zX=#azi`%wsTX%Q&ix)3WoH%j%^yzu?=7oiYtzNzQ>({RzKYl!N zcRYhhrK6XF-)V&^t( zGASf88gT* p*|uoNu485sRG4kvB4%OjIPK|1HRVBBMCFOPd~&0)SZoB6}3fJZNXuHSj~|^KdT)zMH+U=(wFQ@NV zQT_YF`VY4oKVHfF`{VG76-?Op^Zbe}CwCpb^6ci+OVc>-KKuUa{jXWuUVr=f z_rsSzeT&XqzW4RP&4b6cv~?AFO)7SrkZ0PS<2qsam8v+~zdw&|I&{O-Gnru&49XBt z{K>+|z)-=U19BZGPcU%oXZXk=8*-`xHh@e*aj+DwrOO701bj-N5rNF(hNiZR8 zg+Nm}gS1An)fa){<$YIITg=+_=7rJ`E@hup3W^e}PTg#b3?c#_8V<1VvrKk4AfWj6 zmUy&EgF?iS)2+L*SThtA4zfG)1_do(b!gOWG%De^w_%YoyQG+D${PW}_I5{e=Usay OGP55OX=G$%um%8MDoY~( literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/key_unique.gif b/msd/css/mod_green/icons/key_unique.gif new file mode 100644 index 0000000000000000000000000000000000000000..5dfe8325fdadb133d55edeb0854899c37e4cb41e GIT binary patch literal 1001 zcmZ?wbhEHb6krfw_+G*A<$m+G*Nfh6lbgI~`@@BdA5H~+{r;zS(U}R$FR$Bw<=59k zHy(YPwf)tX$34&A{(7;T>BBMCFOPd~&0)SZoB6}3fJZNXuHSj~|^KdT)zMH+U=(wFQ@NV zQT_YF`VY4oKVHfF`{VG76-?Op^Zbe}CwCpb^6ci+OVc>-KKuUa{jXWuUVr=f z_rsSzeT&XqzW4RP&4b6cv~?AFO)7SrkZ0PS<2qsam8v+~zdw&|I&{O-Gnru&49XBt z{K>+|z)-=U19BZGPdIQKWBAA+5CsF?s_w!` z-n!=%TVJ*AeABn!LCpXEf0jN@`+tAI?ZqMc-_6|kqVCD=hX3!+zCJVW>7K^_zu*1; z|DS;}p!k!8k%2*rK?h_E$WIJxEe=xyI5foCPb?HHwNmSB@b>(&(_%8iVaJbwGCU2- zxdLX$1hsoA{$WYsVw=2hLSiyIuUE$}nsWTZu>w{o&b2yv)z2uZMTE@fn5lH+GlVBweBvPp2ufrEleTicEuKhl!q H$Y2csXB3RO literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/nokey.gif b/msd/css/mod_green/icons/nokey.gif new file mode 100644 index 0000000000000000000000000000000000000000..0a3caa67bf23998c087d03ca603c93af3fa9c96d GIT binary patch literal 1299 zcmZ?wbh9u|lx0w4_|DJp@87@IXXj3uwe|e+q`#l%Zm2h&G;wlfN^C=Q*|KFzeqYGH zaN+#(XHRzR+Vy>@!~g&Pe^@ayjDpb+7%m~8_>+Z^fq|bv2jn48o^a>*&!Ff~;qZa6 znUzD_K;y)QM8`&61_p~84hk%tj4~Dh4Gs;B%zQdh5hperY+~b;kny>Y(7?nh#=WN` zz(Ki*l~1xOW)x4{!Uq{Q+lU))^JvKMWp9|?b*0N(zh$fIYI~QgNe8bmTSyvxNceYs zbwaaFs1*PF$)A%~>j(yIopmXpVZm+oYpFF0_W%FqFDbwf$S05{>!{(#>KOWw*Mor} zne#&o<8R)U1b-fego(`zCd(NZWgA5GB|+=}g#xfWvE-U8rH=;fQ8r z7ZG5{F_9GuiAa=e5Mnl4b~Mb#<8~^|RulSRNk%57kL5BedK=KSs(sTM(o_@=( c%)I3mOUpeO1%Z}FFKVx8E`4=s8!Lk~0Hm54!~g&Q literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/ok.gif b/msd/css/mod_green/icons/ok.gif new file mode 100644 index 0000000000000000000000000000000000000000..1ca168e4041e46a42fa36302e495e64ba4e17b8b GIT binary patch literal 138 zcmV;50CoRINk%w1VGsZi0J9GOoVaAG=w`C*ZN&V3rQ=}A?wH8`fz9!px$|?;^P#`^ zdDZr(+4-yB{j=Tsu>b%6A^8LW000jFEC2ui01yBW000Cx@X1N5y*O(Mz?Ohvcp5Ma s4T%AY;;2&xA_xP6g3#s54JLtLAmNK02!unCQz$GevX691l|%plJI1I!q5uE@ literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/openfile.gif b/msd/css/mod_green/icons/openfile.gif new file mode 100644 index 0000000000000000000000000000000000000000..898764012b6c2d30d5877c96799bda200d6eb5bc GIT binary patch literal 941 zcmZ?wbh9u|6krfw_|C`x1poj42a|LD|6lt1=d|x1I{$y4^5IG4|Ia=DKhHV0C;I>E zmjBONuJ5pabkP6*p{P(+@mKO2+KOXh}ddvUc-$AyHg3%Bd1R126PetE)tF5<6ec>cvM?*WxWnmqh`m#@dD0b!kQWCTm?!aBcrMHgp2{O8{SOWmJ+Ir&v literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/progressbar_dump.gif b/msd/css/mod_green/icons/progressbar_dump.gif new file mode 100644 index 0000000000000000000000000000000000000000..c57f75e1a139ecb86135bef9dcf933aaf0c450fb GIT binary patch literal 502 zcmZ?wbhEHb)L;-`xXQr5uxJLuqM3p#7iex?opk!B>;4^8S1-8j-_d>NdeznQNvDqp ztXxoi^+Mw5qgtC+yYAnvxn*_IsUy`_&v)Osk#y>4;^`xro7Z&Rx$d@qd)2k`f-4pz zo;s?zWsTszqQ3R zwI%GV>>aFK{9WyBZLAVKto-bg`#aes+9wOLaEZFh0F3FyV*51%LDATD>==svD&WWaG1(v@`Tmo35(fP zHrrL4W>;BlR&v^`K6|jo&^E8(g zvobNU^z^Vy6cn5w)yE>)-#cqcACut3UKVZ%Hg0BKZf-W_HLID|uG+xM#=CLF2DUY9 z5?j`CiEtg_}3p59l|dTG_o_uGTbRhI49T4^F|`boc|X373pOo1J>4MLtWk)C!5RREtB48! literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/progressbar_speed.gif b/msd/css/mod_green/icons/progressbar_speed.gif new file mode 100644 index 0000000000000000000000000000000000000000..c8cd9dc771639b5836cccad7c301c815b60a30be GIT binary patch literal 484 zcmZ?wbhEHb)L;-`xXQqAhJoQ}TH5Wvzz@~c|7XrTV{H7hxA$3E+9iAYOAhvz>>Ymg z_I#+S{@K&}p}Oj4PtWbZpxZ%#cLD=%2L-*auD;~paM{8Bvc3I}p57n5y+3+--d9!s z=av5w8Do?8)m3+b0vTun6o0aS4b}mXAU`p%{dZ_6@X(R!Ke43fq=?wO z3oA;z<~A+iVp%k4t%_c!i`c?|mV>vtt|_^doDp}a*6DCz5pGoJ-_QN8UO-8xtX@z^ zsI;O%pn-{T0y{h7hk5n#rOb+aa*G%7tX{HX z1wR`bFE{VrJ=|=&xn&L?IJoo^>o;!Ry2dNP&3*64NhvnT{ktz7 zmfC;*@G)-5qq|pa-o`Aw_}e1s?OXmXSsr2;ag{50xi`Cc?W-*Xlb!r#NI7OKNuQ|9 z*em4w;c^H6!fpvgkA@Eidz$#7PN^ttcJr3d)zt4P+qRbOxRi#sPbr%z;5V6X-N DfN`z* literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/rename.gif b/msd/css/mod_green/icons/rename.gif new file mode 100644 index 0000000000000000000000000000000000000000..c55d78ea2b89edc9f733cf32e552503f251186c4 GIT binary patch literal 296 zcmZ?wbh9u|RA5kG*v!uW1Px%^0Hzv%yao_BaNxjyp!k1=|6mG816e?z_>+Z^fq{`h z2Pg?NL4kpp<*LC6&((V^UY|APIPj@7hGR~O`l6NF`*hRh75H4vkGN6t8Zw4OUp zj)$d!zpACC)-S58*}cFv->xI7vB%#-J&mK)x2s$zIbpi@tlG&<^YdL6soTv?;qOkW zikZP~GQqjCVflQIwDfH|+E;OG+-nk&f8fx>1db`k>raO7pV=3Fbe_D2%Y?1_ug9Ek fQsh=x{bJF^XD{Ebe)H~QK;)-H!|y*N1sSXX1rL76 literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/search.gif b/msd/css/mod_green/icons/search.gif new file mode 100644 index 0000000000000000000000000000000000000000..26e0cd408b16178aa0b289e9039e2a9b8d873f4c GIT binary patch literal 540 zcmZ?wbhEHb6krfwcoxF&4+#DP!9N*IS6MB$oT7%X*zA3WPy0m{h9{Lzn7*{QvhCmB ze_y|R{qyI~xr^7`{Nrj{r*%$P@axyF?Yj=_-gjin&O__B?B2Qe_@^(QzJB{wQq{I? z*OB6imiO;JEL^(g?c2A%e*Sd!Pd|J9>Zebi{{HzpVd^}fmDA_0lGb#-e)FERx?N~Y z&cta;vx^!VI%c~1#x7X4$;2W0%a<<}4xz_RUOaT{oV141@87>$d*}TB_piBQ@~4j< z?>=~voLwcQX5Y|0&Dt&D{G}ToK79QD_uu>X@0YCFx_aZTxRj!oFJCTRzVYGX=N~?N z`2OSPkMG|@6U%_qj~_paYx{tHW*7uO@h1x-149Ud4#+K_IALI~YzS#;ZfR{13(^;o zXlrqB_hk%Z=jsjd61NBrW?(RN>(!GA;A7;OtJ}+JBk8AZ#T0DV>!IMvD;&(EYS}9- z;-g}$=)x~J%|KMn(^=V3V_FECfUJ&*wvpMz7I}Lmr`yeH<{VlNo2)hcZJ&p5Gjn>s NwPTTa+tSEj4FIDy(VhSR literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/icons/table_truncate.gif b/msd/css/mod_green/icons/table_truncate.gif new file mode 100644 index 0000000000000000000000000000000000000000..fbcb3287d460565d031097db93aba93c60f752ad GIT binary patch literal 979 zcmV;^11$VUNk%w1VGsZi0QUd@VvFSc`NbPfw)X3y^xuQ+wjSD?1L2_o^y7{2%24#< zjPS})m3>w5(`S=`t|YDY5MiT`S|lpecFz6PWtn;x6b?Y)oZMPBDc-= zkZv_KXwCii!N-yoDqh9r*_G(10@K~`V36r&mhQ;8jE8JR(v}H)Uq0~Lfv(5+KyuZh zfkms2RQ&kD+s~cHw1)HSx#zt^=%)hdVZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW003|6f&l;(1S}vh0Ko$wV+cwya3D#6tzCso`9d%SSrG*i za8)4TN)UoaP#mBUcW#3jLIQPYgG4nf6m|kK6%mhu#_^8Wuss;1O`(GDE?$&WMEKX&;dCLlqVcG zDi}ODWIQ%3I3TEC$Fak}p`}BRk*Pt#F@T|sQ$R{ZBH_b>j(!1Er9T2qiyRnb%yb+I z4mdC}$vSeaII!{Y!46^JX)KwW795^$)+{A5;UF_Br=X0%ikpEB?d)7Lq0f#JE&t*MEg6nqGgJtUw^LBo;_ zfiS|plMqOd5q821R1~4DX>Ao(dHTG6#(O@TZ|B1~*Y!J|M-DqW1@-_Yz`krxo*}fnlhBNHR1b9iD9Di)j9|iRqc%zUNWVcPD3NA@TF|jaBjBNL_R9 z?K`(4E{FFIO?C;StL3!TF0oQv+ASQMR?JSx<&b!&Z*X*Rd1-WFiq|e`>+T;Il2zAM z3=B>5KOg0F^bL-T4UY_n2gh4F#e<{M{m*5B{!#hd?98mfJ0^8@VZNbN5Pa!UPv7v@ zm!sr!^NGm`eUhQ!vFVo?yK0(x?Cp1rPsyef@@}EDty>C-NBLr@Y(kz^!oz;!ql?`3h+^*`zp895}p;cJlBB?ByCJcx zj@K*b|2ICd76kyu>B50=;QymcKOp|EmfipSe@p^kLkwXBHOh5W8Iaor7tZ#+O5}aj5Re{Oc8? zPbhbAU!3-kPLPH6aem2QJoxpRxnbOVi-f^_lOI8_>!TD`w> zI?R;9TC=egR6CYKGS{3mix)|?nN+`BXr74*KSXoea($75{t47=QZv(T=K+k6|p^S{-OHS?@&D$YB9?ZawLV#JEX>VnBPB}sZL(yK&^ ztj*;j)uGDJ(BudQ@24>zu_QBVhn0XV93#_WJI>ea;DZBuuDO-X3gnoyv~%RDvPTXp z_e;%uOPHA1)wLh1N>aE*7&8p@h~-Yo7AV!aNe86p<4nO?rO%#a3)D@!Kl-#={{xj` zvi3P(4F^{u-OQa1+6HHtk!f`m)h7z7KzA#tq_pH{!E1|z6CZ!fdT^=W7mKVb>-Oe) zW@c+$F$eOJy5C*I>@s)3DTB3lMl<4}&j$!r=t;Sh1C#mvHZRp9MlO@_c2 z=!LGUk1(kIACoWU;sdtH3`pyY^K*;-Z_=$+ zpD@eX@4-l}Xq*=Lhw#;on#S**eYvT4U?IeqPY1>9H>CMrjsIi!20c#ya9);^o@n3r zeVHdz5EuxL4s%nxCJ^lOSk??6vy52ewy%3+MV^dYL4J*Kd%aJF)Qbl3x)2NZOo{r^NJaXRk!nl)#BY5#;4gFYn(m=- zyVe@Ps7i{F9|0tK#H#OM5D~8!*uA#`Gt*t~Tkn&*fq9U&sa&LvbwmMtw;RtW!A9Pm z{We|7wEAF6y!}kSI+wo9ZjH6N;tMml2)n1d>4Cg-r`^BfYC?gwNRQp_E-}Cdrt>fA z8H}a;dlUtEQ~<1J!ps1QTA2#*l|&Z1TgF6%I>HSGsVHX#OFPsx2qOZLOO6Yb>CX=A= zpPZ%sqA%5#*(V7Szm?=q*XAGBYCZ__5r;oABs%Cu3$@8)(XRfZ(@l#P)lGM6e@Zym{2uKTQeS76RHah_McTxoMcB*Lucq zaTbw#P`ftENI00EMn7Fwi{Wl);XQi}V~Rr&!LPg@r|K61=v51MJs(@e&aFa<&J7Zr z@R8!LQNq+xXB@JH7-wrzAYBW`E7t;GC`6fAyl*87Jvi&rRA1Z?GzHfPV+QP(w5U09 zZ2fE-Q{XCH4YXIo-HA^)n>1w?`rnmNpCC!L;c+!Xb-yDx~$iQs))}nI7^h&P67lC~9?w zPm&X?sdQY(-^>hY2LM+V*GA^K-5w>3pCy0LJCWdEd>Gm||K3oK_8%q8Cy;5zP!_)v zzp+^#a>F&S;5F3i%v~D_CszP%ocGrAhi_=3^(B47zcaUM8Q(VpI^??<%%W~(0?OPJ zd&CQ#@QT5bT#8kHSpS#$=`p}4#t?=HV^$zdRH#A_1Y`$IsKe0HbCQo4Vc`*W- z`ONz^*WKVH_xklYzp)x(bJ4b|QWIS0LW--7W?TKd(_ih+IDv zlXAum1W1u^(ZD*x$D0G*Tm?*oJxt!xtn^Ryo@h@XguVM+chsRbwkg#2eZm#kp0&$A zTGw3ndOO``OoW!5*qiR(n#kCE##Zp&WAUyV?dpCE=Ete0@!J^(wreQwBl}e7y=7*) zdFSt~pUh-^uUgtVxg9_PQ9pCxA?4^ps-R*zGZ+a!Lq$Y#fshcC7CGS3``~!-51PSX zC=s_qbu}U6wzTyJO-tMv2n+PncJ75A;=&Fx18+^Eg6TjZBXS!%@;WgvcQtZ&U!>J7 zP>UW?%mCy2Y0s#b%vB(lin{;+Fg6GSn5X5yd(ZgKIk=Oa;Q$18pup6)U@#OLO28G< z!AtrH%n&ewf(qhO^S^?ztb#-LA{`khCjfL;VblOH`ri+OfM^J<#=x@Vi9{}F?1j`~ zUf9hJw%P)a?hxRvOibj15uAkF$mCFNLIi}e<$^kYCp)p>oXun^7YrdJgwhj?9lx*D z$K@wpc9!3F5TmG!!~{n~S!7B%2Fa8FcYTv$BtRw`*snqcG0>-cUNzo9yfPkIz$}Au}$faZ&7+{wx z6V*7_1yb@(r=)0RQZX5R1qid^g6<4#1{JQuVe3#bGs&68*oSYo!eE~^QSZDmupA^w zhP};6qv|scOJly|f=3LP1SW%{Lc*!Iaw6P;4g5@pVX4{WYyeBnKKlVBP70VTM`w@` znG{%n6ENe8JCzTdlIFlUIX^4n_i|E`SJ6&{2Z6+F8V4ESlm5;oVAHe_^jQzN?>M@Lnzs<#1s?bHU+^34%h&zQO8JvPGfD=#-D%2e) zJzJivBL*-Z9)^EFRwfrz-zk_+1~s>s1^=;z>rvsk3@niicPAqXm~1^V;L1U2D+?;I z1^$YhtA4n9ih>z_&P-*FtE`~13Nhfs`N{g}kRML=I{u%?#7oKS2t~oN6O4k*9qoM)U`C z3xK1|V(kH((_qn#*|HV_43J@|AJN*BXC!(>upx}*45Q&Hij<`-jEWW&G6ulCbprHc z@tMqr0b2$rlB}3dD7Po0F0F%<_0k+1kl#^7?ZCuvF$4)Ddt{Kw7Ak_yA>N*jDC zp$f?^KUGk^&#=axT-b;MvLdiNPKl)5zSl@EDZkkr(n^NoNT)5x)$$l9;%P$YkLBQ#y z)Q{D>3(AA&HN1|7*UnXbG=NXRr?RUNoz>NMxyUH)!`W)kh^sbN9RzV3?v|RQfaT1m z`-u&=4Phtxsfkk#C~IeY8xMX&H&O7GofxSXo=?OBTR-vkM)9V+ z(w*jb`!>KyO!HI{&!dLt*HJIRJ%R(c80XTxsodIi?&&%1nNEy7os0G7HW9e~QPt-@ zwVXF<4RC3tN0m1XHkug1j;hL549c@8t$V$BUmLaEh-&im$6J!0ggaMc0lb^GZOl(? zESI)8BYwPpn|~@knD%U?y)MfcMfYyKXw?45r9I~{KQ9%(x28S6v;E0jd;VNo7Pb7q zVB?Q8d^58=v9lv~qiv5ZHsz<%V8f~yWyhYMI$=?r9gjP^QagKW+k;)IvWPWJhTMDj zj>5-XPiwj)on22JcfdZO$6UH){@vq0bx%fh&){)ffBb?imX=)5J?7t;;>~*z)${Uk z&#He9?I`}YPdzR8-rtRS|8nVlXWP5!-@BaJ{kf)lWv+K?qZdfS4;+PB=n&={go}po zg^=23NFxnjYAZmy3iQqi^p6Q*$Q?!zrN%>o;Jfu^#zG6bPAga8qW3e=W1-#WZp5#` Y#ZN*9W0Cqf5g|gPnI;-ABmge|1z|&1I{*Lx literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/pics/loveyourdata.gif b/msd/css/mod_green/pics/loveyourdata.gif new file mode 100644 index 0000000000000000000000000000000000000000..076c7538e60cdfc58adf59c9f231302eaf5ab4a7 GIT binary patch literal 43 scmZ?wbhEHbWMp7uXkcLY|NlP&1B2pE7Dgb&paUX6G7d~kE{qJ;0Lp3yIRF3v literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/pics/mainnavi.gif b/msd/css/mod_green/pics/mainnavi.gif new file mode 100644 index 0000000000000000000000000000000000000000..23547e33a5731b8f3d806845e917609e69e769a4 GIT binary patch literal 1748 zcmV;_1}phTNk%w1VUPh(0K^*r!NknJ!pgh9$ji>uy1vKJ(%8w%(!RmU#K+Ia$41ejE#v7^V2AVZ2ANiw9BE+|v~s$9u3i-ia0PICJXU$+M@= zpFo2O9ZIyQ(W6L{4z(bmsne%Wqe`7hwW`&tShH%~%C)Q4uVBN99ZR;XS+Eu+s9npp zt=qS7(BTJr4xw7TUm@{kM z%(=7Y&!9D1pg_8`>C>oFt6t5zwd>cgW6PdRySDAyxO3~?&Aaz)3k-q_A5Ofu@#Dyo zD__pMx%21Hqf4Joy}I@5*t2V&ZefFk@8H9WA5Xr#`Sa-0t6$H)z5Dm@5f($n3;DZoGDB*+@R%juF7F?*|h8%Y2;fElG zDB_4AW=P_RD5j|5iY&J1qJbv9DC3MY)@b96Ct3i&jy(40NG7S| zl1w(~0+o_zM{=bwNED(Iku7Ha6Bh$gD&qKr1`D54e!AnBx(R%+>`m}aW!rkr-_ z>8GHED(a}DmTKy$sHUpws-;>$z^kyvD(kGY)@tjmxaO+suDtf@>#x8DE9|hu7HjOW z$O>!00?an+?6c5DEA6z@R%`A5wb*8>?Y7)@>+QGThAZy4+ZYo z#w+i;I#y}#z4+#<@4o!@>+iq(#)#v<1Q%@Z!8BTcz`_hS?C`@7M=bHg6jyBV#TaL- z@x~l??D5AShb;2QBztT@0w|}f^2#i??DESn$1L;AG}mnN%{b?*^Ugf??DNk+2fece z1{7`d(MTt)^wLZ>?ex=7M=kZ#R99{F)mUe(_10W>?KRdG3?TN{WS4FB*=VP&_S$T> z?e^Pn$1V5Vbk}Y7-FWA%_uh3|P{7}S2QK*Fgcol3;fN=$_~MK=?)c-7M=tr~lvi%~ z<(NxufdZU&?)m4Shc5d6=%kl!`st{ruKMb%x9WM@YgFn(%}uyhnIa$if!7@P#mpAx6NXz8c!_ zhB(Zj4#oGK@A>eDKy=WHd}@*2u;-y77&0jH4XqNXIyi#y5D(qaOFj$3FV;kAMuM zAAz%-LK^arh)kp+7s<#*I#Q9i8=fR5Ny$oD@{*X$4cT|UVjA<9$V{d(m&wdR zWP*UujHWcFNzH0n^O~cuMFh9W&2D=0o8SzmIDs(@51R9w=uD?N*U8RycB3IOjHf*3 zNzZz|5T5qTr#|<|&wl!Ym%c(vq6=q$o|PN>?fuHP-Q^Fpa59XG+tW+VnAZ6r@gf%F~|u q^rt`#Dr*S&$f6qcs7Ot!QkTk9rfor%KhT4sSmp002Ano%a|3 literal 0 HcmV?d00001 diff --git a/msd/css/mod_green/pics/navi_bg.jpg b/msd/css/mod_green/pics/navi_bg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5b956ed9db516d145892a3653c700d60167e51ca GIT binary patch literal 21264 zcmbTd19WDww>bLM#*|arwmY@WueLp%y3=-QcWT?VZQGvOwqNI`d)LGN-u3p$NwSr* zb5^pGoaB5gef$9+ONmK}0YE?j0OQXK@No+GBI0CZu>a2c z2mO-|gA4#bf`I?EiF@%iK)xxB#=mPrL-P6jkvzIwF zJw_rJjwpW>!Jr{vKVLG`Uu!>6z!xGW6mUUBgJ03as1V4P4%y6%)!oxVN`^5cXoOq7 zIRf_IduCXaXUX)hYK%zHAyGj-t@(UqpY{5LzlHoQLeSt+(E<5aG+}miH{?z4d(T_Qm@A8Pk?8(yH||PbKgFhZQrpekR3i~A9LeW*avAdcRC9zG?jtKMCEqC( z@@SAxOooFx^#CKadnLgP3N{jzy=)@OZzD%f_z6bEn1)4k6vE3f{o7e$FR!24 zkCr^FfE=(q0BbhulUVpQS?o~`9Pwt&3U>a&*v7hC??%MXiz!GPx8+xyPHc({kAx3E zG2TYU>AC$5Z=AR)jL$I^6rQ8nS?`#;Q46yUqQndJ$-t|;S*llRH`PiU z`1+rSaRY8NeD$_%s1tX0?>b$a6R4A4ymWf0R(IXzZ)*+{HW(OE^o01m&Tv|0Hc?q{ z+jPU8@qpAv#+p9glS`eUe~egjb`?BjFA% z)xpwvQ^qaP-)1wCcYg&z{^&w)a7cOTjX`*p-;1W*jwa2bG?BApb$)iPJ%iG+II0Ah z(npn-7A=xOqnRse`kgT_9qKQHg&j08Ib@fN5(}Q6qEZ zl5UYRlw?baOfq8JP$T@?%>m$Z#r9cc=&~YxrYNX?$BMrIp{$gpkS!qa59Gfhs82rw z{nyb#fbJH3SK*?@jYpIUA`_0u9#;6D2|I|4|6{nX9jwpbE4w-~i?ZVN(AZg-#9wgz zUlYAgkJ+#z#Bvpj=BzHK%vrJB<}Ck3SUkug^%Wo=T;R{L@e^Qv`sCZ-Kz3K5>Zih( zll@KrFRmgiu(g1x7Upt6MjKKVScUYlfYd`FjPq}RiN1}>LIq*!z0T?tS=#_3c2>7z zrL%I7`VoBfnqY!se}k>@3w+F<%BMgm6;|RKDVz}Lsg#>dAk7&Cgg^w4t12dBx!w`f zS4w?Ds9A zs-ZEajav3|{W%UgE)0(ViK25#`q0*AtGwiBsC}`O8z;G;bO^Oph?+%IQks5|**RUBVYKPrPOtp}kw%0K(xIIrSt7--ApEUk zz$Qi#YBnU?YIG%I6jX8=X0Z1RfoO+!PY`tZmKD64TEq!8CIRtlDT!n=Jd4e}%OdK& z-A{s8fhh`JRIQLYJdSkJk?rUQrCRet@(s)tg5$h_F98%pF?$GJL=K$lhQBR^rC*SD zh6N0y?45bvCg#OI0HC>&EDLhYIBcG@){0T3FJw6|2#}Cg9(1cI8Kd>VG>)F`f6~Ix zR(FLwPpdU85PFL%eC&3cZp}37gQ%d@*S;1ih9ES|Hr~9Y_rqE2@*2zD8Ozxk9xI`V zeAZ8BMk#yFm`YLXN?MwM>)7d zF3wR8Iuv4{RX5q<1a|tw!RV)fUrKi~gm>bUkDHU<4jS$xe7TY-zUko}9%9C|rxTdG zwq(Nzyp#jAN6kGauvh*i4e6a-H20}~)n*InU0n3pZ{I`y;cYT_A#CKSKz~-Ey1*G637h{UouS7R#%$<&->RVv8!oix-y?Hu1y#Wd z@3Eofhiy(lisp!ECB?U#7LueF%aE1tYlT@-3iaMI3Xe0F=t$*< z&kh3UtQg}`f*uL6BgD6Piw?%NZN*$Bw;l&-sE6+? zzNqY0k&ecvX*S%p8amj?zbMa{wbe=Qq2O}Dy4H(ZO{JvX2&%YX{I<$oWJNCbat{+* zB(Arq3EGUb6#lJ%i9UyzTN#2K6&|4?X``+a8=(@W+t)C(mNba>I5qcNhGoj+G>1^` zYWFUBpjg>cLyOZ219Xoe7f6Su3RDT4+cATxx^{1<_#U=;S1|kyB=K_din~xlbr2|-hb}<~wk2_Q2n=Ah=((HZ zGG9t4p5K5jHLoaNd1kHNabd*a@$s!>cV=d&P(02na0XmUuA`fWly^oup~hj7k`C^6 ztR4s6hB3~Y_8+Yk>~Cv*|7^Fq-&z4MGdLWO<@-@?^@%^UD=M(#H<8Z6~r~Ho2Hs0XB+D8nSgB2OfGD z%;ja-Lt#D(K=~17Yl5%Q(X##NT^Ce~s@8MGar5w-_w%UKxy}glMyDyOBB0D>KI$E3}($tYf!4Izc)nP%+c zQ#r5bayfh{q&)qWhEBGYzNB^4>kADi%%sb$_4&mMMJImF&hT-#FHhLCyD0OxE?q*t1ZsjvU%#U=nHEc zv?zjpY#bgu%UZ7L&z{FC!uMQP=9}SI{X7DlMe7`fDar?<2H9%L2_~ zmP^0(^nCjdjC#r^jak<$T<=nI>a0F(BzSo9w}N5bBPuiLf_NNZtAJiv@KS@7YU*oI00&# zZ^(QAlogu-^APk#zx~s1fS!#X$j2-|+Lx{X#kisF zlGnf6NIKb9l9@1G8@80c!DlT|t3zxUF@!tE$oSd}>ojb9bBHaIP2|Os;~gm@E4e~) z;-CMs{U@XL7AeDq-H(%aq+>|fC!=8p{^JMLhTa#$bFTQtyiiMpbsocNsibY~-=VIC zw!<*9$Biu<$ZC~gKdi6Za&AIfqFj2uakljW09df9nrOwtmM!OUrajWTWmWtyAq!z4wuAyW9w-_wVbH}X&n zw-mTKVi18iGE^8?3_n%sW;Ei<2DnmYWG#!ymVP9Yk_fBIQ)+IKQ&=3c1loCk zu426CUa5QwDg^$7+z@84)Q@LyqJ;9ggA?m-WASJB*iNc?%gcLQzE|b>eVAXIokOXW z*h4`KzIY_?i+7!0&Skf;ZA3jlqTxIQKC_1w-htqX&C$Eq_}qZ|BA{hz6O=F^{4(l! zIM4Y2{6HPMg41%mM3_UGqAFXIfzs^h(BiWFn!tz=X-ETtQO%7SD3t44hPZ@x>2QZ_lZ`@9d zleTq*$CX1aZc}fIb}T|5{zNCNUGhRlftL?T|&uG%DZm4M1jdw9k(qNgshuLzqo_FNI1zE*j85|k|&8a zV@#J`)!EuQ_+3p+z}=?yQERw?-Y~I~TYmAxSIS>kHnV^11JIxO&oTI)o^KR!BtLD2 z;fBH_o9RBu;u6k6AtO18^Bqx!wfx8HOBPKM*AG*?pNi}$u)2=AEeDuNq)9C)C6Gi1 zXd6^iEzp1hW%8U^yEfu`TngmHE|(5v`jhFNzHHgnWC6dbCz|lQVmF`UPnSVf_d2pf zRV*bVLlM01v}owb9q-)$F#w7|YY*@d?*o8nLZeP{Zh?ap{J47%J#{}1@2z|=G`GD; zp450qYFUDjiwsG6P~LnJiJk53ZwSH}%R2hO46dx?Kuc zfJmUXW<-Z;Oi6Qe#t>(uVO_)8^;2CssC`Zp#16xt2c|>)VNL^_^VbhRuhPQCce2G6 zFtYhL>8|s~gpfI;BL$yG>z)+#wus%=LRZUn%w~_X)-1d;3!YwJsK_`;P@iH5!>b^s zW@^}@)ux%J9Ti=!*mc7a``2|zt&FRbsJ;EL%4y{S-B!cPWL?Jv9XP9apLfn!HV>F| zo6)5dGnS~PK~A*;`%?@AT>(Rd+&-;%NtRjOOU5R&)NI2n9eJJK6C^2@9m@!o=l4~CZX)FX}r98(xi7K^!PJAtH z6S7v4l*I0%Kpn$(dMHboQn4PaOFg&*^oEs=zU zYp8|$RXOoXBfbR6X5Uh#g9!BK{>We5SRr%J?C0a!S!dFqVoK@->Y@hkJ*w@-#}$!T zeV5XZihxFuvWO?A=Tb1BZ6KGM1sT+Qks-Rz$e!DbGMAVIOA2WDoPG-A47Wsc;}psH z(OoySCy;|MYODSpq?fN@DAhrIEALQFj50A5+sN?08tK&eoo0}XRxyArG?6{(ctbdHzeU^254u{~p3H^>;f zAw!{(tl~-l7-~=R{e_j5_i--pJLiMl*3Vm;3yVgE0Q)djFx(>Pu5Qyoa|7W_Fa_`SvZ- ztKbJfjzO59&*PKs{pEc717P0BMW!arxYzxtQJU+wpe|6Q5?_4xNv%a-Oq{*tOv z7c@6FAJvLW}^h2qYmj{^4tl zxa4}UJ(@xfc;hu?#j8^%N$0gLDitHr2jfNzN?ur@+?D2lAj zsol7>sOxZdaiYI1Tb5^oCeY}@4|J|SX*Fzu2=d)8dz-=hjt`f1w4!B7hAm|jn9LBT zg*5IA9l?jEYPQjY!k*1UXlXjxYv9s47-f=Vys~p14jtT#>G_kuw<5>0w6}Djwxg>z zI@7>>u3OT>dn{m$c^@snad#kp@({h>j^42G1JIY*rtJki zNr$xJG{}rYKanpV(s5U)Y)KeQNemh`Q|YrBvC78_wCZvqg-L<6ze4--bxs^Bl1rKq zco-SFmB&Wty6!q{9K|&QA#{Z0V7nMb;8Yxduw#LKG?9ET`((#4rZ?K)QHb5&pEhdN zG^|};p5Nr?`hD9V7M@?LZLLuna6-_xezrX;Q_&}i-7>Z>`(vm^$g2Wn7aljg7hhIJjz5d+;@dVS2dqdru8B?Xh1d=4BMUdwNjovI zMJFB>tlHvRilH%p6E1__k*)?}QlS1$>}A1koB&}fw1SQT_QInGAeq29yk$5>$fQ|b zF~8vZDO9Ftni-$E?5H7s9wF<+A5DE)#_=WzSKj!Bx=H~Md1jgSmWOIWx0xB}Ap~&V zR^ZK6X%J%yZe+pe7rvr}q7-Aie0AFe%Nay7SaqVLZsB~AZ?)kHCJTcq5t$m#M6utE z%*;^4s(UbHuA!&I%5#SdOLzEx3MK8X^i@;H@|y8GHlJ}_yc@21Hg?2q*m{h3IRPdd=eDb~E2kzvzOC_1cwAzOc=URn#Ld(e_3YHO^PZMBH}q0y|+S61D~tS}}( zMF4xZJ^S-%WXHr8PMB4#6))|}*vVCO-S<*CksvuKi31tLjBuUv1qU4IR+=Xa2g^CJ zI`)NdoUL&~U73o(8jP%rGV9ea9iql42cNnTJW>h2NH^DkmP*33Uu;_uYL0XVi~FFt z=Tx*j>uFdQ^d!~wxW*Zx_5O%2$dh`A_?rfS5AJKk5eJLbod{May{VEVsup=u?r~sK z4t9<MQ5(I7*m$6$qM4IxI)*Gd+=fJmjq z(-`i{vA$X++A&A6NaNTA`Du05{P!q~F-oxvM6QUD9-E&x}O z518t?TzOu&Mqk;S8jxh#UI*%BG;E*9TE?0K zJQdLxS-2A-^pU1WtLB}CM)pJt(wd>_0X{uONLm3l^J_c`oczF54(1-oh`k8KZMcc- z=0MipG|)RuwiDVo*W~Rc3p+CQ;+l@+v1I7Spk*F@=*{VSriNl}tnX0$VDx3$ zOOijY5*8UH!QkfhWlD*U$ZR3V816&9Ha;2H5Pw;nq-nczqexL}&Ddh4J_2?fSX>Vm zih=R!LBPD_&nq6?>Ek!FU*@((h3?BbxcX5gT}L}PYl%?0D?Eu-A<2Vn4yL&WJ!FKl zG`b@GnsG2_pt`qQj72KD3q^u0>D(Tuq9Gm6=N~tR+OSp=vo*kHB$3Ljl^LMb{{ZmQ z4J>Hl*5#)?5V$(h1bIkH5WC_#3)s%d@O|(p46ppT7 zF_QBA3q@z7Nqiq>^yU8QIBvBU=P10h2eZ9C0vKZ3a}`4G5x%ioCY(LNYG5SUOqk$e zMzoUEthU~UTG367@CRTEsU)Z!IEDYzpOOLX^DrcT#c8>%HR9fiBfXlgg3tN^Xdyhu z&BeFk=$sdfio+c}-AKy=4#oq_RB7V+%t-_^ zmL;V|#P=gG1Q!>qDEkd~7Zeg$UiRR^Eh2kI_1xObM&aHYob|o9F!b^I8d;ngS|9T! z|0c@g5hroQt**wWOw*9k4?wG7JyE3S3KaL;Ua~qxb70exk&CZqJ&vT_0^uQiM8Q5m zM-%b&jEdrm0$cy)LNn}xMS$UV$#Xats{Bqhh&kGjZ=5n@{!(RRV*AIur*~x$gQ&Jd zgstl?SXqrBg=i6OjGPu5U){9FnX+(2fd|1jPVFh@WvfjX&5=8I_y4qP;BXbn2RuL4^?w>Zf1cfZh(`UFKB zxw;z3AP*uRO=Y3prw-U5O{4eEN*c5Xq!l&ds0v+RenD81qjE$+RUOx`iq3b)+1!_w z4yKl`T-|SU+Ug=3pGn!~Ia@ijo$cxV(|~+k7Cm5jUCPovtlWi1vw*a3TGJjtPiJey zBoFReOw57K+3*y#`(m+OU+)(9WLc}Na~P^rZs&>N5*JK&XcRjRp*3Mm>fV99tD2QT zg-@*G8BbCdsn*VkY%7U>VaYt}O2x;YzXB@lqAg*+aQ{H?dNv0dfW@v5h+n8QVwCZefG13>(o)g(@o7YPHMgdg13pAoYmYM&QpmehdGZ(GWnC ze)nU3g0S<6$AADp`vLH3s7}=GM5~cJA*oGJCV>o+$Rph6Gz2<-01`Pgpc4~jq%H5A zubQ%`$mS1-H#l^EYvvMA$hL?hXDGL6>b&yY{o8%H3 zi(X>rypIRTh1C4xS&OnK(x3l8RQ&2`17^gAxt^mYdV3Apw=3%?)|KQyW7knnt(F|) zA!QXTQyhkXFXE~+JZ`!j8tL)5ty3w~hiTp7}B?u$gFgV82<7`BX%#LKFA z3|Ub&=DRjwpmmAk}A<^PDPzh`~`{(Zg6cqTnD zT^sEV4oAfjkaPceQoPAI5FH7980A1%@X>KMV?B%hp^u#*COZIMji0Px>ASETdx&GbiBm>NzYYHExd zTN4(9x|1;>As?k&fM4|YBmR(Occ|#7sx)jpG<@^u8*#cV2FbEa_oL}6xAMT&Zo%B3 zCJ1wBnRmOJky)Lxjv|jF2FMgJ_iJd^(?%L|Fv2^VAt&S)huN~SbHyZ?UZxTS1Mjb> zmV^b47*bh3CqHH~sK#BEB4ARb9Jr;6zcjdjdo$glK6D~;iCtn@be$cXk(&DD)?aRfB~CIq_0CBUb&}zyMQCttwW=-* zy#X~VquKd0ep#~iCO-(5&LbYj$6bO;#N&O3*STm=b*b9OE5(-MoY3*6sO!stgaDXr zZn~MWA(-&GF7zt%u`N@J;?!i+L;I`b`462Lu#CJKQKfW@v4TvsR{Js)&t`Ndf^yuY2gt`D-M`;&2E$K2DCL}7B zFr|GUOi?o~2Q zb%I$EhuMvrsONYe{)(LoG_6bNe~K0dTKmw*B-QiP^W@EnTIz)Wq2>}dCGRcuC|Y$N ze@64TOciA-%+>6Spl#cD_l7U5j6gc&iHawJnWh(9xOHgsRGcHVE!=y2vxanZo_%2q1RxF&&fpP-2AQX{Q24AED1tBT*|@Gi zS*U=(NuN&~iSYDXgnpDU8v z1!l38vLMqh3?pFCs8z&NvoX19{0>OlK}^i{ z9-#|1Wrb>sN&}RotrFMt;)Cq*``-|hjm5sNC5e^FgI9wn()1DYnw5t`(_4xBnP2jO zA=SjGH?eeZpumb5^7~Q(M0L|hyGQ~_(pT`;mJ_*ESglz#NR{E$CZ%EIHfs+woHxjk z(f;LGwxaK^3lUXlZiZ!u-sD#b>>EY?Ds4Hx8dogpXic8oZ~(9LoR@w2L-#5aVW+q{ z=Q0j1wSx2cI7J!CFN0MXz-5B###O^%%8B?u(n#t%1me+;5#O0 z997OyMgReUM}0!kb8!*ZWv48PbkSwodJiL4qarK#>Yz7^W|Tdekh zW8-RAN!E~LD-?VH7pF&(6*l#F^CgabY|OcPRh^eFE#QO}A_A|?TSJH`ge5x033cEp|qdC z>g6+7pAG}NF?kPe3X5vt;n>pD<@ z+`GO}3a{J)QyV$L2=7wZ81jAC_)~Cj{`;pAGR6^R9=lo1c_p+-|6=9W!X=S8Ij}!n z*TiQutqGis@ozYz^L_Se5KHjPC1(4b81ci=)Xzj7d5H`sS7k~hMSi0~e50*v0fW~P z_d1Q1r;WC5kO$A$C+2Jp%`m{};Em^}!DNpMzh`_AY1Q-;PooW}GtQ%{aYax8f}5d!&Re{ztl21-5e{v}QUZ2g`M}P(-zeE-K6h z0}d4MC($bjUYDYl>+Po6MQ&}Nm+a4T92Mv8C#HK6)kmaQR3)=oJ+I9qhPup-_cl%B zX0rmrD!$;uHZT}HcvgE>DRe~N%_AOz5!jdAEI?v)r58ETT>y4NLilrA$+4lUB?GIj z%ftsj8q2IZ_MF=@R;*WO>GADJJm=i06C&FA56CIxY8;Cqj{VzURSrZ6IoPtr%?H3l z$+e=h(id2(XG;i1(~&q}x5A2}u5u<$GgT*$9VU^xRpKzwSW!=vUI&p|YxtrQ@v?RW z`1)kyQH7qX5gyQwocpu8a~RsityH~rbi_(1_$RGF;Y@&WPI~2zgzpOQ&ki4LKAUUM z7p1x?n(|O-lt0Be0kj)B1HBUKO~U=^^EzP&W8rP8{MSL8vKZ}GU9;&VLhY3S#^;|~ zvpFhMD=ml2N{0F4zeYBWDk8m6B`9)gGD%l(OX@3~ZpUHVoVic6sGk(h@vLdRAXEH_ zZmUs$(dmTPJSh$MwJ^YBD`DYCm8rMt#a1{)@c!|p5$xZoTRP(0=q_D{At}nN!MU_y zkv|mU0u@iJ71BL}dk9fz*vI#Sg0v%_n6tX2-zZGeONb&y@={$qCBVi3M;k7isS-Xk zBqM@#`RY+Nj-3!Cw!N_Pd5A5l2yyeCsRGU}=q35ju}#@*Wl?dwaF#XXS)o7UIZS*O zn5u>uU#>SbYP-l0PghA7JBhoyb*tmu!SI{K8Q7QenzzA;v>rIA$nvO5Lq^^p-;9~h zInV9%od*BXy*pDxAuFRoWc4C(ei72;5w47dme-y=o7{Qz;Y0HA5HUF|T84i9piW`E z-478MS_;_WL4A+8S@52?b0Z4UgDIr?tS3*I#)g3Qv-*?O3xW@T*;qMF7W~E(9_1T^ zKO)$oI~?7!pC<--wXnEJV~9W57;TlcV;23b`&?ZXNVN@4L!+t>rc?x3TNILr%D(=l({*u@CnFsOBm|PMR+2l5igw`_c}*Z^jkR|(=)9OuYZ5b$d`(zQW3N7@F93CQ7Kl+ z_kNQ9hwlUXm+#a3e+R7(!0=oV1!V1@F5zC=BAZg2*~Fs`YBWB%y-j&Dxp{wmW#&Pp zlutQ!uj={MgDP-Wr1a;w_j<;;BH+4nHUJ=k4IKFdjD%<|K?g%JBkHPd`L))Lf z+;Sk#*X{ZMAcC$FI{!q=09{gtcn2@BO+*zmy<=!n<@6TPNO#6pLVzUy0svucB42l& z4AGgFQ}4A<5IF4YhLJ1Ohw483oH{)`=hVW_JL-TlR5=*#Hy+1bhXvB1wiY2J#_r_G7j(LPzG`xuwF$_rK`m~rUQF8t9Y!gx5-HMhl`cNuWE(x=jwT3nw=3h#4t z1$H#v?Vo3T60M^)BTSYv&hZ>5lfTx3Z0f|0ih+Vn3v4Z;=qrVpfL914{ZNP&nS3-l z^w2Y%3YgDTfjeIq5*T;G2LLj0DjY{(In+ibrVpFC!kl$9y-vH9zHA=Pr!9;%tcssn zyH*K1+vk@^omplu97u?ls@!AQQ>BAb2Sj>7hv-+nsWt--fnx@oM|hn|8zfFaTHB&h zo+f((P5B+u8Yw$-L)^T|2dqdXKcPln%AQuA zXL$7WpRs~n;&p{T!e#~@mUz!I3fFgt+bs_!xGcX)HQ3bD%E+qAviksVC{4TVmkfSoD>rPl zBXk~V|j(kW7-o&h?QFs|-G{a4*ENEVit0i#eWvja(P?rwdNm<-t z#D+M4b5Y)mF|Ntj9c`|rvIo@EMIR+^?2;wFJYL(zXhNGaw|qXl>)ay*b{Bn~=R!&2 zf8%0Usrs4kOv~|WFH1On*CE|2Ec2CvUYl~Q=DV!l2Y_xg@dL2;cb4~GKi(m)+BEz# z{5xI!L{TIlKVwPK7)jciVbYk-luy6&_rsi1^;~yCtT#)#w_i9 zTy}>3?FNgZ)4;6%rVS?b@|P42eC^lGvNN~%aa;i?>mI_`gx9fjlmkc7{VVc5%Da^G zt?7&Povj=7fM`QgM-!%(H#WmS=~=^q(w!x8|2|X)1xu!PiFqy@gEeM1DPGUvRP&$N z-*RxZ$XD$s4UvSO{~FvDmjMs?Ps=6gYX4J(&&)skImo9!N8aXkOB0u82|n6)z~h}D zDNOGBal;vzVfJNq?EfF@uzA|?^NZ6&SUM&*{SWQ`BdT}V?tXW&SgD&re7K>uz>CK( ze{-mf@q41Uh5en+tWx(M>~?kT3McL3=6T1G1J#Mr^nanBf3ziMd;q-f&1(0OFCHS; z%>Em5-my@X+e!KV5kVe0UBPXheE3@N;56>j*S&fL7i?xOjj(sLi7r)e zQv~Uu{&!v8jrN1o#kL2h3-E^r?^WN2xA%RIPJ+`S#E`eX)4RRUi$zTI<&zESs=JH4 zAM=ZCE-t!PVCtJM!MZvgUAmZ+Oye2t+nE<{5$y8bXHjxVm0lgdTCI}}w|0-wTWQ-D zZ~Jnlvv0d%HutZwa4z6nUPKf3Z~co$Z8~1jPS2w1M<@Y=7{w}fR@Kz=ajTGiYSK4G z@X#yyb81>@n%HC*htLLd^`NXZTEAffJ7=&z021hfD_IN#<}`z=4`ooVaaNBBQ_IOr z3A!Y+@KUZeD<1%+l+m3BD$A37mrWR^PTjg-r8(wepYsaJxSR|!p+-wEIstUwRe>lh zpdA_bfI4lhd4;-Ny7PW08NUcD0I91~{R@GEQf02!_f_}`Na}f@|AnuDa_FI@am&}Y zoJkA;(@WDeLQX@>%swkO@9Of;P5R7b=ITW^+4LONg){!KP1+)j-LV~L4HX|ul))w8 zJ|h{;TGxb9STGyWTzv2~4d(F+JIX{^)wK20YuT$4nmo+BLge2aKA2V%ZE7c1M;iN!8+_Q8ApoOwT(DpWGo_b4`#`KnG*EoVX<9}7}655MRnXuV3Ix$p` zUaE?+fa(M*vH)&Tv(zQpb_Z4r%nXST{aanE$-Vf=nS(7*mc{Zz9X9QfrAB7QW}e;) zD$S00cp0%~R9;n$lZpIm6YXk70YE1Qpu2FQa-7;1MSDc^PR|+$TJ)2mB4xWNV39uA zilS+CJZyNHf@yuAVZ5kw+}1$wybCdCLL7T!fSJi%AXI1;SaC01kCEzKg@Cq}p+ zWgHrJ)rmxKvHx-@va7HUNu$3{&mslm)OLl=YN>fkfXoojmUDHL2vO#F4&1ImqO7z4 zAzU>GrM}XY`(Wba9fSI};_{Pnqv3s5#I+;PEMtHOTJRQ%vRu;%=_v7_i8MUuQtZBB z7ZJB^g#5?|0M#1pU}@YcE)>8Nr^awbL?E5W-$-QPCWs|;MG4Qf;(|m z61sX59QrDc09RI62tXDWjHQV|Yl|st7$y|q|wwFgg`^17zOxgQsglR?krG#Pv zvh;F=K_PgGf;u;JsM(HQ}R=FSyVN& zRs}O5M3WZ5bAw&4L+%EHd(lIzlnEh0{5lOUR@euaXwOMn-SM^_=wx_d;kTN@R;njR z_hnWYz&r&G#t;lScXF3`{z|JUem-tm3-VLVBGmcmHm@X8F+hE;(mbJTz%JI;SGiGY z1+dZ-+(0N?lTk3G7PHZpNh3sidF{ujh8gwHZzazF@pbgJiIvt}Ip!mLD{o)%PvQXu z)Tl~U`mn_JWgEC2IJ0NcrbN`vFA>%GttFft zN68uPj({&mA2Ju;*d(cxVD98(!FIc$q$VA-(V|!1l?VY*Ll(9R+wmjrsITg9r>_`b z4`7|5Oy;84$lh#RA*lvevh9?e5Muj1rZ4jgNJwewhMj<)u-{HtO{d|IR1%O~C?8NX z5q;i3EyX^fN+-277igaT1y^`Ut;YAqeOp}257LpkpFi<0YOn;1WJvYtAQ)|{O|SG( z0t&c^da`9Ti}PA!t_qTc{Yk3)kBhh}q-Ywg6$OT-sX@1MYWhn`hQ$vBh1N~ZCCUYH zK#CF$x$YyYX*H62FtQgx&$ZrJiLJ(VUqYKJ*Y9R--b6E*>33-9{gKszm&?SOd{b{F zdk|uLz@|R{x>ae402wm$yqf$jlyhI}?SKR+)>)@vtsEGU`l1%j_0^`7-+*mrYkzwa zK?EikGSGgMWQVf_MMr8p@?cB_(8#VLUSAQxObj4xPPKX4!lN%Sma1S^Vz?n#Phqi} z=p0d1DviAshiTXfsR=e4W1ao6#4SpOuTrD2k&p&r&E3Pv8Cwv3N&C7N8KO?UM9P)6 z#DHRz51T5oF~?EJ89X$Q()KoiMg`QbmevJesi01)Voc3REIOlykm!fbD_aJD180K_ zR*Cl1e}qchXb!?BT&Pdb-$)Qmp&;{VW>cLrsfXwz=A2T3#x2h=_C2WPDR3-8p=P2J z)W=auc$Y=CKthF9WN#G4ND-+^YRSM>pxGzHArZdLzI+)+pMr_4(R6%C$f>_8#y`-r zaHKp;?>DC!1wacz!u_l=vDuA)>obMgBR0t4RKXxKk~{X_zQHxSk3>{$e9PT;kA1_u_qC=FR|3p*kTEapPyJu0P2#cciFElP=FFKOAg} zzyGXI)oBTfFLPXX7_sfo*MCyqCeY0estKs%HWcD-l2W>SE<5pfaB-Tu+oFEj_G@`wX?r?U>fgk z|7@A~y89d8x_G!6zjNv%|CeUU{Tndg;C`F3Y?Jx_I9jto|Bid=S6WZGQn~*K**n<% z%ngl6&+5N!yd2WY|L?1v^2R^+|7bms%XDio&G1}UqngWSyd2_vtEQ$DXJT@v2Zfe= z#wT%FNxCeqCnR*1rLG0l7U|lNLugM*MIkAKsa8$)FC!%E%F|3bJ%JyCEvpC~)(3hJb%I$h&Y$OvL-g zXO>O#(&m(Fii|A;Y7~BcyM6uFJZxC23QX-P zLTjV8BdDS6!KCs=+xV};3>)Ipc9M>DPo?n)^F~j#N`hP8?(PB;o~Q-yrc~7SDh@Q4hDd7sjn#}>7i|c*j1g~>-)pQoyuWeQ zx#O+i*cAA$8(ik8IP}ohHn#| zH|$mDB`M_uG*rmX>q*SGlPQ1nH(3s3KFM4$J^_I6B1ADV#yI}mk+-;!j2gNIV|m0{ z?+rTnaQ#SPdzn6xJLfuSd~5E{6?LlO8^D2;{^eTco_OWB*V;{VBR}3Im!otmr_N>} zvU7-~tQUHOaM3n=%XL@DCf4X}A^5;lw97ktIPcra^wq7Q=B#fz(w*cE+sU>qBzM9q zH?^7++0qz!G(IfYB3tV@%0#%Wm$yjXNgIqG4D>c)=1j{Jp|MM%P=z__j0&i^>=hJ8 zB{-`Vg*&Q0WnH^T5i0;Ggx9LX_o@MZr#HQ9V(sn__*>VzY_Daz`E!A0Chw(%6$k{9 zz>4Bz_tj|Wk974Y`G_;3Y71RmOg3imJTyrwUF!PN_ZXSz3$2SdP25985lVnL3)jdu zm!U;TYoqJ&7SVEw$rfr-j!v9z7_MSI>1mFUg#`-W+~kxc=M}60Nf|$4Jr>&EPzZ6= zRaV@VBlY&s<7vl=V|mJOI-O5#WAGY)wewHVvJhQiv9H-(nWytylnbPYc#)XbZ>E1$g8NIn^9zx7fB-GBdDp0DK_1&(6CU-|ToP z=c<#+NJ^L|NBMb4{cpVGt_iPzV&SPVZu|fOovr-X7z_{0#yW%-aIe*1IYP9=VM3QN zY-`68m`%pLDSvg~Ht;*V(YfqgVJ1Q@G}NV+X@b#9@0qtGd>ZJWrdf!XONR1oJSame zj$~Cw2a?6szGXgu-;*=RP2I4(eV4h?DWcox4x1$YAZ+#~dCRfM+`+zi_d_EnJFX4^ zt^}%jdQ`dEoAqAsjZQ~Bm1#5u}t>Qu} zoB}>Iy&Mtr(7WeD=V5|u85{*Blrj>fN;*wWhxtriCE)=6D@5Hhf9uj^8E5Qc_fxlY z17ulqa83wxP+f1v-sC1H0AvulV!4saOyE}$frNTH&?<5S&a(=4!c^nd%wWuZ*f=dNSygJTX5n$Ps&f@%5_?QRjzUU-!H4J)LM;+_wL=s~`?wAX5zr4N@y8eo>s z3zlU1d|K-G4T$hvxtN%fB;GIJkq}EUvaj-h!;KRmsC5C4qD*%k3=pMTZ9OE&Ofw9X z&>n$^s?*{?XQ=FdS>565<}+=){v}8f!86`UcfW-;_GeqqRSs?>#Ft~hB>oy$?&D(P z>Z2S|Ki#;!3%~GyM07lxDR-rO+=VtCDg#o#OEr_5WWEFJC5ko#Z1LP z{*phYuD8VpvieLDE}<{qjlCy ziOvJE$YqgaEdVk$Kr_7WKjAq?akoXf&tCy!Vrs3t*p7>}Wc!lnqT9&)HffW`h?QEQ z;#%HJY;`_?gVhY!IPU~jYYpz1nPZ*4Ts}uVz$ccRKdHcd_rBKUsW2h+zLz@;p!eAA zET}K#`^$EYX){S4LIv5P%A+wb*a5pVJR81qWOTW0{kB6YraAzNPl;@rx#IbF-9@+c($f z+HGrfST_Dx8D}w|@}9?rkaL5%N=h2f-WA;nAVZpa?qGA$m-6zOogxbX8nuBspGhT zxgS}=)^T#-kkqq0X3$Tni$85{Dpww@g~7OhpN>3|PoLoLntPlQ97ql6Xc@ z1z(U0ryQ%_i{$-#b2=srHbz4C0ICQA{VQYypeX38*@{fI6?{!vEP%?q%6ajGB5Ii7 z0C>90^5v$HRjVke0{`XxLR^J3QZZ7}7F1(;iQi7yQ%ws7QLcuM>L=A*N<2Pk}a&WeutkcqEk0ub|omn%g@1@$ob*Q6q%~?(`at=ugq&^T)jx%NEXLIE&)lVpl$Z$eVQSgt2R8GQVmykkX< z#=op8D_37M<4pj`LVfR)g?hacGq`lSdA;&HQv#?1I`x!PL9EDShEe7b~l zUAh0T|BxSu>jzSZKuQ}>U=wLxSuPK)!(d*JwCnTSD?xUck9cQht6^=yhDnD^mT%xx z7#<~9;vw+_3y4^#EFs4ZQ=22#L5qHD?PwqJjba5;yPzU5ev|4O56UurVN@a(Y0Ufa zEcfI47?p#ieq#Yr@y81ioq@I|YCd04S>~3Qb ztZqu0Mb%99(ls+>2YqV6{y7>+ck%+bJ0on>iyIfFA8K9(?!K2 zru)pAS}P152_=qWMSY!|DO^OGWv>KUmQKVB+f06(Dl7d9KX>zimQ9c7rm>ioX2Y_E z*Tv9rcU{K~0R_^RANQg}6(3yy-451 zvSRj5s&eh(;l@nGvgKqSPK7~a$I)T}5naMuNl=B!Sy0-(JQN)PVQPwGP;Fe>%)o0jQ3E|M{J(!3(QZ@2g!K-i3V^$ z*EzXXAqyZ(;g)i$t0cb<16DQC%<UwDm+*$oeVTmN5K|J6?~0PhXz}p&(ukKdvqP z^7@andN0Bry~&?)4@iz&0r+djBV(ejpRh=USVZCt`GU18U`FOY#ES%ynNGl*ELBdB zKvnN7T-tLj)`Ch8GZspLumzLaNYsw};PIn&WM(53KRk?c5Tc9Tg+F#Skp&^InC=;% zp;RHo*6K!`7#D;6ZXZxEH^QQ88auG0A(0O_VIIg|Oe1OYo-2cs$>ikv;-`LZiF=s&6HelKBI-gi(f#X{qw zp(-bHoO51q99eMwNQGF}V$PwN2ebh)_qVRIik<66lq<+jm)hWDfIIqkBpM%hz8rmHSHZWHV8M{ 0)) ? $config['minspeed'] : 50); + $dump['dump_encoding'] = (isset($_POST['dump_encoding'])) ? urldecode($_POST['dump_encoding']) : ''; + + if (isset($_GET['sel_dump_encoding'])) { + // First call -> evaluate encoding + include_once './inc/functions_sql.php'; + get_sql_encodings(); + $encodingline = $config['mysql_possible_character_sets'][$_GET['sel_dump_encoding']]; + $encoding = explode(' ', $encodingline); + $dump['dump_encoding'] = isset($encoding[0]) ? $encoding[0] : $encodingline; + } + include './inc/define_icons.php'; + $dump['tabellen_gesamt'] = 0; +} + +$mp2 = [ +'Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', ]; + +FillMultiDBarrays(); +if ('' != $databases['db_actual_tableselected'] && 0 == $config['multi_dump']) { + $dump['tblArray'] = explode('|', $databases['db_actual_tableselected']); + $tbl_sel = true; + $msgTbl = sprintf($lang['L_NR_TABLES_SELECTED'], count($dump['tblArray'])); +} +// Correction -> Multi-DB-array is filled (so that the info is not lost in the config), but multidump is not activated) +if (isset($config['multi_dump']) && (0 == $config['multi_dump'])) { + unset($databases['multi']); + $databases['multi'] = []; + $databases['multi'][0] = $databases['db_actual']; +} else { + // if multidump is activated, but no DB is selected -> take over current DB + if (!isset($databases['multi'][0])) { + $databases['multi'][0] = $databases['db_actual']; + } + // find correct dbindex -> take dbname from $databases['multi'] and get the correct index + // from $databases['Name'] -> needed to set $dump['dbindex'] for first run of command_before_dump + $dump['dbindex'] = $flipped[$databases['multi'][0]]; +} + +// Activate time counter +$dump['max_zeit'] = intval($config['max_execution_time'] * $config['time_buffer']); +$dump['startzeit'] = time(); +$xtime = (isset($_POST['xtime'])) ? $_POST['xtime'] : time(); +$dump['countdata'] = (!empty($_POST['countdata'])) ? $_POST['countdata'] : 0; +$dump['aufruf'] = (!empty($_POST['aufruf'])) ? $_POST['aufruf'] : 0; +mod_mysqli_connect($dump['dump_encoding']); + +if (-1 == $dump['table_offset']) { + ExecuteCommand('b'); +} + +// only read tableinfos the first time and save it to session to speed up backing up process +if (!isset($_SESSION['dump'])) { + getDBInfos(); +} + +$num_tables = count($dump['tables']); + +if ((isset($config['optimize_tables_beforedump']) && (1 == $config['optimize_tables_beforedump'])) && -1 == $dump['table_offset']) { + $out .= sprintf($lang['L_NR_TABLES_OPTIMIZED'], $num_tables).'
'; +} +$dump['data'] = ''; +$dump['dbindex'] = (isset($_POST['dbindex'])) ? $_POST['dbindex'] : $flipped[$databases['multi'][0]]; + +// Build output header +$aus_header[] = headline('Backup: '.((isset($config['multi_dump']) && (1 == $config['multi_dump'])) ? 'Multidump ('.count($databases['multi']).' '.$lang['L_DBS'].')' : $lang['L_DB'].': '.$databases['Name'][$dump['dbindex']].(('' != $databases['praefix'][$dump['dbindex']]) ? ' ('.$lang['L_WITHPRAEFIX'].' '.$databases['praefix'][$dump['dbindex']].')' : ''))); +if (isset($aus_error) && count($aus_error) > 0) { + $aus_header = array_merge($aus_header, $aus_error); +} + +if (0 == $num_tables) { + // no tables found + $aus[] = '

'.$lang['L_ERROR'].': '.sprintf($lang['L_DUMP_NOTABLES'], $databases['Name'][$dump['dbindex']]).'

'; + if (1 == !$config['multi_dump']) { + echo $pageheader; + echo get_page_parameter($dump); + echo implode("\n", $aus); + echo ''; + exit(); + } +} else { + if (-1 == $dump['table_offset']) { + // Create file, since first call + new_file(); + $dump['table_offset'] = 0; // now it can start + flush(); + } else { + // Determine SQL commands + $dump['restzeilen'] = $dump['anzahl_zeilen']; + while (($dump['table_offset'] < $num_tables) && ($dump['restzeilen'] > 0)) { + $table = substr($dump['tables'][$dump['table_offset']], strpos($dump['tables'][$dump['table_offset']], '|') + 1); + $adbname = substr($dump['tables'][$dump['table_offset']], 0, strpos($dump['tables'][$dump['table_offset']], '|')); + if ($databases['Name'][$dump['dbindex']] != $adbname) { + //neue Datenbank + $dump['data'] .= "\nSET FOREIGN_KEY_CHECKS=1;"; + $dump['data'] .= "\n".$mysql_commentstring.' EOB'."\n\n"; + WriteToDumpFile(); + WriteLog('Dump \''.$dump['backupdatei'].'\' finished.'); + ExecuteCommand('a'); + if (1 == $config['multi_part']) { + $out .= $lang['L_FINISHED'].'
'; + $dateistamm = substr($dump['backupdatei'], 0, strrpos($dump['backupdatei'], 'part_')).'part_'; + $dateiendung = (1 == $config['compression']) ? '.sql.gz' : '.sql'; + for ($i = 1; $i < ($dump['part'] - $dump['part_offset']); ++$i) { + $mpdatei = $dateistamm.$i.$dateiendung; + clearstatcache(); + $sz = byte_output(@filesize($config['paths']['backup'].$mpdatei)); + $out .= $lang['L_FILE'].' '.$mpdatei.' ('.$sz.') '.$lang['L_DUMP_SUCCESSFUL'].'
'; + } + } else { + clearstatcache(); + $out .= $lang['L_FINISHED'].'
'.$dump['backupdatei'].' ('.byte_output(filesize($config['paths']['backup'].$dump['backupdatei'])).')
'; + } + if (1 == $config['send_mail']) { + DoEmail(); + } + + for ($i = 0; $i < 3; ++$i) { + if (isset($config['ftp_transfer'][$i]) && (1 == $config['ftp_transfer'][$i])) { + DoFTP($i); + } + if (isset($config['sftp_transfer'][$i]) && (1 == $config['sftp_transfer'][$i])) { + DoSFTP($i); + } + } + if (isset($flipped[$adbname])) { + $dump['dbindex'] = $flipped[$adbname]; + } + $dump['part_offset'] = $dump['part'] - 1; + $out .= '

'; + ExecuteCommand('b'); + new_file(); + } + + $aktuelle_tabelle = $dump['table_offset']; + if (0 == $dump['zeilen_offset']) { + if (isset($config['minspeed']) && ($config['minspeed'] > 0)) { + $dump['anzahl_zeilen'] = $config['minspeed']; + $dump['restzeilen'] = $config['minspeed']; + } + + $create_statement = ''; + $create_statement = get_def($adbname, $table); + + if (!(false === $create_statement)) { + $dump['data'] .= $create_statement; + } else { + WriteToDumpFile(); // save data we have up to now + // error reading table definition + $read_create_error = sprintf($lang['L_FATAL_ERROR_DUMP'], $table, $adbname).': '.mysqli_error($config['dbconnection']); + Errorlog('DUMP', $databases['db_actual'], '', $read_create_error, 0); + WriteLog($read_create_error); + if ($config['stop_with_error'] > 0) { + exit($read_create_error); + } + ++$dump['errors']; + } + } + WriteToDumpFile(); + if (!in_array($adbname.'|'.$table, $dump['skip_data']) && 'VIEW' != $dump['table_types'][getDBIndex($adbname, $table)]) { + get_content($adbname, $table); + --$dump['restzeilen']; + } else { + // skip data + if ('VIEW' != $dump['table_types'][getDBIndex($adbname, $table)]) { + $dump['data'] .= '/*!40000 ALTER TABLE `'.$table.'` ENABLE KEYS */;'."\n"; + } + WriteToDumpFile(); + ++$dump['table_offset']; + } + if ((isset($config['memory_limit']) && $config['memory_limit'] > 0) && strlen($dump['data']) > $config['memory_limit']) { + WriteToDumpFile(); + } + } + } + + /* + * Display - Progress + */ + if (isset($config['multi_dump']) && (1 == $config['multi_dump'])) { + $mudbs = ''; + $count_dbs = count($databases['multi']); + for ($i = 0; $i < $count_dbs; ++$i) { + if ($databases['Name'][$dump['dbindex']] == $databases['multi'][$i]) { + $mudbs .= ''.$databases['multi'][$i].'   '; + } else { + $mudbs .= ''.$databases['multi'][$i].'   '; + } + } + } + if (isset($config['multi_part']) && (1 == $config['multi_part'])) { + $aus[] = '
Multipart-Backup: '.$config['multipartgroesse1'].' '.$mp2[$config['multipartgroesse2']].'
'; + } + + $aus[] = '

'.$lang['L_DUMP_HEADLINE'].'

'; + + if ($dump['kommentar'] > '') { + $aus[] = $lang['L_COMMENT'].': '.$dump['kommentar'].'
'; + } + $aus[] = ((isset($config['multi_dump']) && 1 == $config['multi_dump'])) ? $lang['L_DB'].': '.$mudbs : $lang['L_DB'].': '.$databases['Name'][$dump['dbindex']].''; + $aus[] = (('' != $databases['praefix'][$dump['dbindex']]) ? ' ('.$lang['L_WITHPRAEFIX'].' '.$databases['praefix'][$dump['dbindex']].')' : '').'
'; + if (isset($tbl_sel)) { + $aus[] = $msgTbl.'

'; + } + + if (isset($config['multi_part']) && (1 == $config['multi_part'])) { + $aus[] = 'Multipart-Backup File '.($dump['part'] - $dump['part_offset'] - 1).'
'; + $aus2 = ', '.($dump['part'] - 1).' files'; + } + $aus[] = $lang['L_DUMP_FILENAME'].''.$dump['backupdatei'].'
'.$lang['L_CHARSET'].': '.$dump['dump_encoding'].''. + + '
'.$lang['L_FILESIZE'].': '.byte_output($dump['filesize']).'

'.$lang['L_GZIP_COMPRESSION'].' '; + $aus[] = (isset($config['compression']) && (1 == $config['compression'])) ? $lang['L_ACTIVATED'] : $lang['L_NOT_ACTIVATED']; + $aus[] = '.
'; + if ($out > '') { + $aus[] = '
'.$out.''; + } + + if (isset($dump['tables'][$dump['table_offset']])) { + $table = substr($dump['tables'][$dump['table_offset']], strpos($dump['tables'][$dump['table_offset']], '|') + 1); + $adbname = substr($dump['tables'][$dump['table_offset']], 0, strpos($dump['tables'][$dump['table_offset']], '|')); + + // get nr of recorsd from dump-array + $record_string = $dump['records'][$dump['table_offset']]; + $record_string = explode('|', $record_string); + $dump['zeilen_total'] = $record_string[1]; + + if ($dump['zeilen_total'] > 0) { + $fortschritt = intval((100 * $dump['zeilen_offset']) / $dump['zeilen_total']); + } else { + $fortschritt = 100; + } + + $aus[] = $lang['L_SAVING_TABLE'].''.($dump['table_offset'] + 1).' '.$lang['L_OF'].' '.sizeof($dump['tables']).'
'.$lang['L_ACTUAL_TABLE'].': '.$table.'

'.$lang['L_PROGRESS_TABLE'].':
'; + + $aus[] = ''.''.''.''; + + if ($dump['anzahl_zeilen'] + $dump['zeilen_offset'] >= $dump['zeilen_total']) { + $eintrag = $dump['zeilen_offset'] + 1; + $zeilen_gesamt = $dump['zeilen_total']; + if (0 == $zeilen_gesamt) { + $eintrag = 0; + } + } else { + $zeilen_gesamt = $dump['zeilen_offset'] + $dump['anzahl_zeilen']; + $eintrag = $dump['zeilen_offset'] + 1; + } + + $aus[] = ''.'
 '.($fortschritt).'%
'.$lang['L_ENTRY'].' '.number_format($eintrag, 0, ',', '.').' '.$lang['L_UPTO'].' '.number_format(($zeilen_gesamt), 0, ',', '.').' '.$lang['L_OF'].' '.number_format($dump['zeilen_total'], 0, ',', '.').'
'; + + $dump['tabellen_gesamt'] = (isset($dump['tables'])) ? count($dump['tables']) : 0; + + $noch_zu_speichern = $dump['totalrecords'] - $dump['countdata']; + $prozent = ($dump['totalrecords'] > 0) ? round(((100 * $noch_zu_speichern) / $dump['totalrecords']), 0) : 100; + if (0 == $noch_zu_speichern || $prozent > 100) { + $prozent = 100; + } + + $aus[] = "\n".'
'.$lang['L_PROGRESS_OVER_ALL'].':'."\n".''.''.''.'
'.(100 - $prozent).'%
'; + + //Speed-Anzeige + $config['maxspeed'] = isset($config['maxspeed']) ? $config['maxspeed'] : '1'; + $config['minspeed'] = isset($config['minspeed']) ? $config['minspeed'] : '1'; + $fw = ($config['maxspeed'] == $config['minspeed']) ? 300 : round(($dump['anzahl_zeilen'] - $config['minspeed']) / ($config['maxspeed'] - $config['minspeed']) * 300, 0); + if ($fw > 300) { + $fw = 300; + } + $aus[] = '
'.'
'.'Speed
'.$dump['anzahl_zeilen'].'
'.''.'
'.''.'
'.''.''.'
'.$config['minspeed'].''.$config['maxspeed'].'
'."\n".'
'. + + //Status-Text + '

'.zeit_format(time() - $xtime).', '.$dump['aufruf'].' '.$lang['L_PAGE_REFRESHS'].$aus2; + $aus[] = ($dump['errors'] > 0) ? ', '.$dump['errors'].' errors' : ''; + $aus[] = '

'; + } else { + ++$dump['table_offset']; + } + // End display + WriteToDumpFile(); + if (!isset($summe_eintraege)) { + $summe_eintraege = 0; + } + + if ($dump['table_offset'] <= $dump['tabellen_gesamt']) { + $dauer = time() - ($xtime + $dump['verbraucht']); + $dump['verbraucht'] += $dauer; + $summe_eintraege += $dump['anzahl_zeilen']; + + // Time adjustment + if ($dauer < $dump['max_zeit']) { + $dump['anzahl_zeilen'] = $dump['anzahl_zeilen'] * $config['tuning_add']; + if ($dauer < $dump['max_zeit'] / 2) { + $dump['anzahl_zeilen'] *= 1.8; + } + if ($dump['anzahl_zeilen'] > $config['maxspeed']) { + $dump['anzahl_zeilen'] = $config['maxspeed']; + } + } else { + $dump['anzahl_zeilen'] = $dump['anzahl_zeilen'] * $config['tuning_sub']; + if ($dump['anzahl_zeilen'] < $config['minspeed']) { + $dump['anzahl_zeilen'] = $config['minspeed']; + } + } + $dump['anzahl_zeilen'] = intval($dump['anzahl_zeilen']); + ++$dump['aufruf']; + } else { + // Backup ready + $dump['data'] = "\nSET FOREIGN_KEY_CHECKS=1;"; + $dump['data'] .= "\n".$mysql_commentstring.' EOB'."\n\n"; + WriteToDumpFile(); + ExecuteCommand('a'); + chmod($config['paths']['backup'].$dump['backupdatei'], 0777); + if (isset($config['multi_part']) && (1 == $config['multi_part'])) { + $out .= "\n".'
'; + $dateistamm = substr($dump['backupdatei'], 0, strrpos($dump['backupdatei'], 'part_')).'part_'; + $dateiendung = (1 == $config['compression']) ? '.sql.gz' : '.sql'; + clearstatcache(); + for ($i = 1; $i < ($dump['part'] - $dump['part_offset']); ++$i) { + $mpdatei = $dateistamm.$i.$dateiendung; + $sz = byte_output(@filesize($config['paths']['backup'].$mpdatei)); + $out .= "\n".$lang['L_FILE'].' '.$mpdatei.' ('.$sz.') '.$lang['L_DUMP_SUCCESSFUL'].'
'; + } + } else { + $out .= "\n".'
'.$lang['L_FILE'].' '.$dump['backupdatei'].' ('.byte_output(filesize($config['paths']['backup'].$dump['backupdatei'])).')'.''.$lang['L_DUMP_SUCCESSFUL'].'
'; + } + + $xtime = time() - $xtime; + $aus = []; + $aus[] = '
'."\n"; + if (isset($config['multi_dump']) && (1 == $config['multi_dump'])) { + WriteLog('Dump \''.$dump['backupdatei'].'\' finished.'); + WriteLog('Multidump: '.count($databases['multi']).' Databases in '.zeit_format($xtime).'.'); + } else { + WriteLog('Dump \''.$dump['backupdatei'].'\' finished in '.zeit_format($xtime).'.'); + } + + if (isset($config['send_mail']) && (1 == $config['send_mail'])) { + DoEmail(); + } + for ($i = 0; $i < 3; ++$i) { + if (isset($config['ftp_transfer'][$i]) && (1 == $config['ftp_transfer'][$i])) { + DoFTP($i); + } + if (isset($config['sftp_transfer'][$i]) && (1 == $config['sftp_transfer'][$i])) { + DoSFTP($i); + } + } + + $aus[] = ''.$lang['L_DONE'].'
'; + + if (isset($config['multi_dump']) && (1 == $config['multi_dump'])) { + $aus[] = sprintf($lang['L_MULTIDUMP'], count($databases['multi'])).': '; + $aus[] = ''.implode(', ', $databases['multi']).''; + $aus2 = ''; + $out = ''; + } else { + $aus[] = '
'.sprintf($lang['L_DUMP_ENDERGEBNIS'], $num_tables, number_format($dump['countdata'], 0, ',', '.')); + } + if ($dump['errors'] > 0) { + $aus[] = sprintf($lang['L_DUMP_ERRORS'], $dump['errors']); + } + + $aus[] = '
'.$out.'
'.'

'.zeit_format($xtime).', '.$dump['aufruf'].' '.$lang['L_PAGE_REFRESHS'].$aus2.'

'."\n"; + $aus[] = "\n".'
'; + $aus[] = '   '; + $aus[] = '   

'; + $aus[] = '
'; + + $DumpFertig = 1; + } +} + +//===================================================================== +//================= Display =========================================== +//===================================================================== + +// Craft page +$aus = array_merge($aus_header, $aus); + +$dump['xtime'] = $xtime; +if (1 != $DumpFertig) { + // save actual values to session + $_SESSION['dump'] = $dump; + $page_parameter = get_page_parameter($dump); + $pagefooter = ''; + $selbstaufruf = $page_parameter.'
'; +} else { + $dump = []; + $_SESSION['dump'] = $dump; + $pagefooter = MODFooter('', 1); + $selbstaufruf = ''; +} +$complete_page = $pageheader.implode("\n", $aus)."\n".$selbstaufruf."\n".$pagefooter; +echo $complete_page; +ob_end_flush(); diff --git a/msd/filemanagement.php b/msd/filemanagement.php new file mode 100644 index 0000000..8a8bfb9 --- /dev/null +++ b/msd/filemanagement.php @@ -0,0 +1,550 @@ +'; + if (is_array($databases['multi'])) { + for ($i = 0; $i < sizeof($databases['multi']); ++$i) { + if ($i > 0) { + $toolboxstring .= ', '; + } + $toolboxstring .= $databases['multi'][$i]; + if ($multi_praefixe[$i] > '') { + $toolboxstring .= ' (\''.$multi_praefixe[$i].'\')'; + } + } + } +} + +//*** Abfrage ob Dump nach Tabellenaufruf *** +if (isset($_POST['dump_tbl'])) { + $check_dirs = TestWorkDir(); + if (true === !$check_dirs) { + exit($check_dirs); + } + $databases['db_actual_tableselected'] = substr($_POST['tbl_array'], 0, strlen($_POST['tbl_array']) - 1); + WriteParams(); + $dump['fileoperations'] = 0; + echo ''; + exit(); +} + +//*** Abfrage ob Dump *** +if (isset($_POST['dump'])) { + if (isset($_POST['tblfrage']) && 1 == $_POST['tblfrage']) { + //Tabellenabfrage + $tblfrage_refer = 'dump'; + include 'inc/table_query.php'; + exit(); + } else { + @$check_dir = TestWorkDir(); + if (true === !$check_dir) { + exit($check_dir); + } + $databases['db_actual_tableselected'] = ''; + WriteParams(); + $dump['fileoperations'] = 0; + + $sUrl = 'dump.php?comment='.urlencode($dk).'&sel_dump_encoding='.$dump['sel_dump_encoding'].'&config='.urlencode($config['config_file']); + if ((isset($config['optimize_tables_beforedump']) && (1 == $config['optimize_tables_beforedump']))) { + echo '
'.$lang['L_DUMP_HEADLINE'].'

'; + echo '

'.sprintf($lang['L_DUMP_INFO'], $sUrl).'

'; + } + echo ''; + exit(); + } +} + +//*** Abfrage ob Restore nach Tabellenaufruf *** +if (isset($_POST['restore_tbl'])) { + $databases['db_actual_tableselected'] = substr($_POST['tbl_array'], 0, strlen($_POST['tbl_array']) - 1); + WriteParams(); + echo ''; + + exit(); +} + +//*** Abfrage ob Restore *** +if (isset($_POST['restore'])) { + if (isset($_POST['file'])) { + if (isset($_POST['tblfrage']) && 1 == $_POST['tblfrage']) { + //Tabellenabfrage + $tblfrage_refer = 'restore'; + $filename = urldecode($_POST['file'][0]); + include 'inc/table_query.php'; + exit(); + } else { + $file = $_POST['file'][0]; + $statusline = read_statusline_from_file($file); + if (isset($_POST['sel_dump_encoding_restore'])) { + $encodingstring = $config['mysql_possible_character_sets'][$_POST['sel_dump_encoding_restore']]; + $encoding = explode(' ', $encodingstring); + $dump_encoding = $encoding[0]; + } else { + if (!isset($statusline['charset']) || '?' == trim($statusline['charset'])) { + echo headline($lang['L_FM_RESTORE'].': '.$file); + + // if we can't detect encoding ask user + echo '
'.$lang['L_CHOOSE_CHARSET'].'

'; + echo '
'; + echo ''; + + echo '
'.$lang['L_FM_CHOOSE_ENCODING'].':'; + echo '
'; + echo $lang['L_MYSQL_CONNECTION_ENCODING'].':'.$config['mysql_standard_character_set'].'

'; + echo ''; + echo '
'; + exit(); + } else { + $dump_encoding = $statusline['charset']; + } + } + + $databases['db_actual_tableselected'] = ''; + WriteParams(); + echo ''; + exit(); + } + } else { + $msg .= '

'.$lang['L_FM_NOFILE'].'

'; + } +} + +//*** Abfrage ob Delete *** +$del = []; +if (isset($_POST['delete'])) { + if (isset($_POST['file'])) { + $delfiles = []; + for ($i = 0; $i < count($_POST['file']); ++$i) { + if (false === !strpos($_POST['file'][$i], '_part_')) { + $delfiles[] = substr($_POST['file'][$i], 0, strpos($_POST['file'][$i], '_part_') + 6).'*'; + } else { + $delfiles[] = $_POST['file'][$i]; + } + } + for ($i = 0; $i < count($delfiles); ++$i) { + $del = array_merge($del, DeleteFilesM($fpath, $delfiles[$i])); + } + } else { + $msg .= '

'.$lang['L_FM_NOFILE'].'

'; + } +} +if (isset($_POST['deleteauto'])) { + $delete_result = AutoDelete(); + if ($delete_result > '') { + $msg .= '

'.$delete_result.'

'; + } +} + +if (isset($_POST['deleteall']) || isset($_POST['deleteallfilter'])) { + if (isset($_POST['deleteall'])) { + $del = DeleteFilesM($fpath, '*.sql'); + $del = array_merge($del, DeleteFilesM($fpath, '*.gz')); + } else { + $del = DeleteFilesM($fpath, $databases['db_actual'].'*'); + } +} + +// print file-delete-messages +if (is_array($del) && sizeof($del) > 0) { + foreach ($del as $filename => $success) { + if ($success) { + $msg .= ''; + $msg .= $lang['L_FM_DELETE1'].' \''.$filename.'\' '.$lang['L_FM_DELETE2']; + WriteLog("deleted '$filename'."); + $msg .= '
'; + } else { + $msg .= ''; + $msg .= $lang['L_FM_DELETE1'].' \''.$filename.'\' '.$lang['L_FM_DELETE3']; + WriteLog("deleted '$filename'."); + $msg .= '
'; + } + } +} + +// Upload +if (isset($_POST['upload'])) { + $error = false; + if (!isset($_FILES['upfile']['name'])) { + echo ''.$lang['L_FM_UPLOADFILEREQUEST'].'

'; + } else { + if (!file_exists($fpath.$_FILES['upfile']['name'])) { + // Extension ermitteln -strrpos fängt hinten an und ermittelt somit den letzten Punkt + $endung = strrchr($_FILES['upfile']['name'], '.'); + $erlaubt = [ + '.gz', '.sql', ]; + if (!in_array($endung, $erlaubt)) { + $msg .= ''.$lang['L_FM_UPLOADNOTALLOWED1'].'
'; + $msg .= $lang['L_FM_UPLOADNOTALLOWED2'].'
'; + } else { + if (!$error) { + if (move_uploaded_file($_FILES['upfile']['tmp_name'], $fpath.$_FILES['upfile']['name'])) { + @chmod($fpath.$upfile_name, 0777); + } else { + $error .= ''.$lang['L_FM_UPLOADMOVEERROR'].'
'; + } + } + if ($error) { + $msg .= $error.''.$lang['L_FM_UPLOADFAILED'].'
'; + } + } + } else { + $msg .= ''.$lang['L_FM_UPLOADFILEEXISTS'].'
'; + } + } +} + +//Seitenteile vordefinieren +$href = 'filemanagement.php?action='.$action.'&kind='.$kind; +$tbl_abfrage = ''; +if (isset($config['multi_dump']) && (0 == $config['multi_dump'])) { + $tbl_abfrage = ''.$lang['L_FM_SELECTTABLES'].''; +} +$dk = (isset($_POST['dumpKommentar'])) ? htmlentities($_POST['dumpKommentar']) : ''; +$tbl_abfrage .= ''.$lang['L_FM_COMMENT'].':'; +$autodel = '

'.$lang['L_AUTODELETE'].': '; +$autodel .= (isset($config['auto_delete']) && (0 == $config['auto_delete'])) ? $lang['L_NOT_ACTIVATED'] : $lang['L_ACTIVATED']; +if (isset($config['max_backup_files'])) { + $autodel .= ' ('.$config['max_backup_files'].' '.$lang['L_MAX_BACKUP_FILES_EACH2'].')'; +} + +$autodel .= '

'; + +//Fallunterscheidung +switch ($action) { + case 'dump': + $dbName = $databases['Name'][$databases['db_selected_index']]; + if ((isset($config['multi_dump']) && (0 == $config['multi_dump'])) && in_array($dbName, $dontBackupDatabases)) { + echo headline($lang['L_FM_DUMP_HEADER'].' ("'.$lang['L_CONFIG_HEADLINE'].': '.$config['config_file'].'")'); + echo ''.sprintf($lang['L_BACKUP_NOT_POSSIBLE'], $dbName).''; + break; + } + if (isset($config['multi_dump']) && (0 == $config['multi_dump'])) { + DBDetailInfo($databases['db_selected_index']); + } + $cext = (isset($config['cron_extender']) && (0 == $config['cron_extender'])) ? 'pl' : 'cgi'; + $actualUrl = substr($_SERVER['SCRIPT_NAME'], 0, strrpos($_SERVER['SCRIPT_NAME'], '/') + 1); + if ('/' != substr($actualUrl, -1)) { + $actualUrl .= '/'; + } + if ('/' != substr($actualUrl, 0, 1)) { + $actualUrl = "/$actualUrl"; + } + $refdir = ('/' == substr($config['cron_execution_path'], 0, 1)) ? '' : $actualUrl; + $scriptdir = $config['cron_execution_path'].'crondump.'.$cext; + $sfile = $config['cron_execution_path']."perltest.$cext"; + $simplefile = $config['cron_execution_path']."simpletest.$cext"; + $scriptentry = Realpfad('./').$config['paths']['config']; + $cronabsolute = ('/' == substr($config['cron_execution_path'], 0, 1)) ? $_SERVER['DOCUMENT_ROOT'].$scriptdir : Realpfad('./').$scriptdir; + $confabsolute = $config['config_file']; + $scriptref = getServerProtocol().$_SERVER['SERVER_NAME'].$refdir.$config['cron_execution_path'].'crondump.'.$cext.'?config='.$confabsolute; + $cronref = 'perl '.$cronabsolute.' -config='.$confabsolute.' -html_output=0'; + + //Ausgabe + echo headline($lang['L_FM_DUMP_HEADER'].' ("'.$lang['L_CONFIG_HEADLINE'].': '.$config['config_file'].'")'); + if (!is_writable($config['paths']['backup'])) { + exit(''.sprintf($lang['L_WRONG_RIGHTS'], 'work/backup', '777').''); + } + echo ($msg > '') ? $msg.'
' : ''; + echo $autodel; + + //Auswahl + echo '
+ +       + +
'; + echo '
'; + + //Dumpsettings + echo '
'.$lang['L_DUMP'].' (PHP)
'; + + echo '
'; + echo '
'; + + echo '
'; + echo $tbl_abfrage; + echo ''; + echo ''; + echo ''; + echo '
'.$lang['L_MYSQL_CONNECTION_ENCODING'].':'.$config['mysql_standard_character_set'].'
'; + echo '

'; + + echo '
'.$lang['L_FM_DUMPSETTINGS'].' (PHP)
'; + + echo ''; + echo ''; + + if ((isset($config['multi_dump']) && (0 == $config['multi_dump'])) && $databases['praefix'][$databases['db_selected_index']] > '') { + echo ''; + } + + echo ''; + + echo ''; + + if (isset($config['multi_part']) && (1 == $config['multi_part'])) { + echo ''; + } + + if (isset($config['send_mail']) && (1 == $config['send_mail'])) { + $t = $config['email_recipient'].((1 == $config['send_mail_dump']) ? $lang['L_WITHATTACH'] : $lang['L_WITHOUTATTACH']); + } + echo ''; + + for ($x = 0; $x < 3; ++$x) { + if (isset($config['ftp_transfer'][$x]) && $config['ftp_transfer'][$x] > 0) { + echo table_output($lang['L_FTP_TRANSFER'], sprintf(str_replace('
', ' ', $lang['L_FTP_SEND_TO']), $config['ftp_server'][$x], $config['ftp_dir'][$x]), 1, 2); + } + if (isset($config['sftp_transfer'][$x]) && $config['sftp_transfer'][$x] > 0) { + echo table_output($lang['L_SFTP_TRANSFER'], sprintf(str_replace('
', ' ', $lang['L_SFTP_SEND_TO']), $config['sftp_server'][$x], $config['sftp_dir'][$x]), 1, 2); + } + } + //echo ''; + echo '
'.$lang['L_DB'].':'; + if (isset($config['multi_dump']) && (1 == $config['multi_dump'])) { + echo 'Multidump ('.count($databases['multi']).' '.$lang['L_DBS'].')'; + echo ''.$toolboxstring.''; + } else { + echo $databases['db_actual']; + if (isset($databases['Detailinfo'])) { + echo '   ('.$databases['Detailinfo']['tables'].' Tables, '.$databases['Detailinfo']['records'].' Records, '.byte_output($databases['Detailinfo']['size']).')'; + } + echo ''; + } + echo '
'.$lang['L_PRAEFIX'].':'; + echo $databases['praefix'][$databases['db_selected_index']]; + echo '
'.$lang['L_GZIP'].':'.((isset($config['compression']) && (1 == $config['compression'])) ? $lang['L_ACTIVATED'] : $lang['L_NOT_ACTIVATED']); + echo '
'.$lang['L_MULTI_PART'].':'.((isset($config['multi_part']) && (1 == $config['multi_part'])) ? $lang['L_YES'] : $lang['L_NO']); + echo '
'.$lang['L_MULTI_PART_GROESSE'].':'.byte_output($config['multipart_groesse']).'
'.$lang['L_SEND_MAIL_FORM'].':'.((isset($config['send_mail']) && (1 == $config['send_mail'])) ? $t : $lang['L_NOT_ACTIVATED']); + echo '
'; + + echo '

'; + + echo '
'; + + break; + + case 'restore': + echo headline(sprintf($lang['L_FM_RESTORE_HEADER'], $databases['db_actual'])); + echo ($msg > '') ? $msg : ''; + echo $autodel; + echo '
'; + echo '
'; + echo ''; + echo ''; + + + echo FileList(); + echo ''; + echo '
'; + break; + case 'files': + $sysfedit = (isset($_POST['sysfedit'])) ? 1 : 0; + $sysfedit = (isset($_GET['sysfedit'])) ? $_GET['sysfedit'] : $sysfedit; + echo headline($lang['L_FILE_MANAGE']); + echo ($msg > '') ? $msg.'
' : ''; + echo $autodel; + echo '
'; + echo ''; + echo ''; + echo ''; + echo ''; + echo FileList().'
'; + + echo '
'.$lang['L_FM_FILEUPLOAD'].'
'; + echo '
'; + echo ''; + echo ''; + echo '
'.$lang['L_MAX_UPLOAD_SIZE'].': '.$config['upload_max_filesize'].''; + echo '
'.$lang['L_MAX_UPLOAD_SIZE_INFO']; + + echo '
'; + + echo '
Tools
'; + echo ''; + echo '
'; + + break; + case 'convert': + // Konverter + echo headline($lang['L_CONVERTER']); + echo '

'; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo '
'.$lang['L_CONVERT_TITLE'].'
'.$lang['L_CONVERT_FILE'].''.FilelisteCombo($config['paths']['backup'], $selectfile).'
'.$lang['L_CONVERT_FILENAME'].':
 '.$lang['L_COMPRESSED'].'

'; + if (isset($_POST['startconvert'])) { + //$destfile.=($compressed==1) ? ".sql.gz" : ".sql"; + echo $lang['L_CONVERTING']." $selectfile ==> $destfile
"; + + if ('' != $selectfile && file_exists($config['paths']['backup'].$selectfile) && strlen($destfile) > 2) { + Converter($selectfile, $destfile, $compressed); + } else { + echo $lang['L_CONVERT_WRONG_PARAMETERS']; + } + } +} +echo MODFooter(); +ob_end_flush(); +exit(); diff --git a/msd/help.php b/msd/help.php new file mode 100644 index 0000000..2cb4b9c --- /dev/null +++ b/msd/help.php @@ -0,0 +1,33 @@ +D+tYo%i0nfBpZO z8P;mf={o!D+Euk{%U8Q2loX_p;PK(Xz`&4Xq$R$Bu7jY@Dl9bUKPSmK2IvCkAg$#L z28IB-N5B9FOV7jw1A}g|RMm9Rl$YZ(wzp*hn%EneGI`iKfT+R11cW^tfX3FQE~G}L z=9YGX{t4rY=BI z4_g~MXFd->@_+E<1Ks~^W+o^72Z@WdAi3z@4oNlTl}N?yolHr&m{=H%Sy;G8xp|pP zfJQ)LBO?YoY!3X=a_HAWV0MpkxJHda1PE_xO=J{FdL(#hMKSekkMH#%-r zR!%+;5gYG+Aq6Rn3D5=jza=&?<} z#{Qq%WyHmmob1gkZ9os4zeT4^ zpzR2>`OjScN@em_Dq}NaBU290R$=AjV&pO7FlOX64TADRPV_jgAKk{kb31yd99e>GeFPxJAA((}LGcegMF zG5ueu$UnPwwl{Ne2RfOGnu8|pf2)7Y|0~Izfo}gBxlMVDjJSEs7|l#fLDR;`!N~|T zGcyG(1P^G5S$WubO^yCz-2Y8(kjirKadPspvi*n3{=X6ZUvpw?0kktW1sPXn^8XKm z`9D(1e@glP9L#^)fB*jthWW2a{ik{{|98#%$Fu)renAWQk0y{!|9ksyZvy)AZ+~QJ z2bw1*khhTzWbOn5i(!+I5LNZaJk4@XRh3-6_b+ljAMy;%l;{GZOZ{YooEu=&eT&a} zzu_1|ToY4xaD{#*QXu1x5gQkO zD4wju5fMb9<`X!)H_%rLF6yp~r;4AGV&g2@0+!I~11%U77_bLOqEX>OtCq&(1g*G< zTb%i@YqmaqO}{P1Z!PDUeV4Bzg3ZmJ4Fuf0BrD>_d=p09dNLO-EU$`kH)usI0ERWc z`Mu&)iZf0(%)6r_O8U7UfQ1i0Q5VFb8CyMCTv?s_o(6r0AiZ3D5V-wDuKrV>7QZUYAKOE*iW3UDLJQG?mHs$vOhyPKdlN=}-( zXa}O^&RAAz$s?9Bb(*ohS0TKGZN~II6%WNJ6x>?XH-i08&+GhY>XFTAUGuKY@tprU zOWd2Q7TynK94Vie>U=_$6%e+!Q9hx<%)GN2mD8iOEmCskD<1>s%3V-SwKv^v6kYY{i&stDSFd!qFOnB{ zaizx(BMuoQCPh8ugs095#V?7)2<3Sk(Wg4(^w(ExI=-RS*P04VFc)zNYx4Yx)o`YF zU6l1P(TRK$skSmHsZ)gWqslyIUW5|%>bFl)0;17=9Dx1<3nI7^_kKI#nfLWgeZJB7 zL*2Jsw+hV#AO5MevtuCtBn-H$n^d+fwJ`S+xyAYc!b10To}!O8>X|E zV&Zq7_Db7`s)~AqOnkV--d383~ue=1ImCVB3HBZoxII>le+&Z>WM$a_95- zW_~%CrLP+i0|XL9XG`G_XmN#OgkO?#dN;73IPh=dVdszW{caC=_u90(t2R0=?awZa zCTj9$tSVEHg1eQE)0=k{#c{Tz5i{mQM6*MbjbW4;nvzZ~d`>pn-h_!<*I|jeXt4{_ zvp(x?@#tm4W}U-kt;0q_sJA7vmBdavA&ktsIZrhBk2jZbq82WZ^x4b~7nw}6Hc#Fb zWxh=Gf@ZC*sXj|nPNV`)Kj4w5CIA&yl8ncYC8SWjePeQPnvYousn-7Cb8Gu|pEcRi zFus;o#NyAp2{p)&xlMh2{3P#k<_$RlVQHXk(-&Z*US3|9*OW0_-FjAW;SZa!hPDjB zdL83r=m?chsARddNk`Bwof%Wka$$275N zftlut=(tbp+0x_ky$mU<4JR@ZdYd&|s=7kZOH6sv#T~7rl#I#mw?n-UVOd_6#7>#)~6Bp`r^b5+fQ;Zdte`Gw%K>UA3RB++0t9N*0PCUaVp$u7c88tKR>npB^;(>3V_R4B1o;&1 zl*PrvyC#DIN3$=xIS(NfF%BN#G(bahtr#N~P7a%BZ+?{da2ALx2Pk`n$B#lFgqE)e z@uH;GTqE9%0tgG&!^jjBL!!nYkPAMRAguwzM{vGrQjUYQ5TE&ztKP%Jd+Tu_tge$H z$*&#L$G}Tc;2e-=3$~UdtB?``R_4E0Tq_4ZWC#2zQPFGsSnu0PZbl}L$BoQKjp?sk ziAE$=N>rvsjh>u7?>9+Q=JqG;&P^99roWkELlJsHL)g$6`&D)pOu>ptDm>8*Rdg{> z6?WErnxS7%HWLL1U@Oc)vCNx9NdUyYp2kX^_ox%ARWw3t1kBs)W!}30Qk<1Rod}*(%n_O&iIOJH z%sx_y8P{CqtldtoW5D8MZhr zyaiV1`*>JQUHcD@ZpNVl6cy$4fnLWT_F;j`iMU1W1sPXKA^aFI^P-eLQfHAvzl;9# z^~rqPa2WWtV_OHtd=o&RE+8g@v?bEFYdv9nzdPoh<#Iuf=!1nWpp86rf%3%0*U>H2 zS&(J;L!`zfn9(d| zQ48;AkZ7U%yQUZ09n$5G=-Go%&;2(P)e)BlgD6C5`in=UjS^cNz zqP>X9h=|pFI+CC+{1mk!K9vE;2x9}oou|@Xz#Se(rJlCJ_yFg+@&d#~c|(%Aur7Q^ zU53H00tk#}cGsC^9QYyk3M2)gNy^H{63Uq4JeJIMtvm9O2sWuPUVhpu60#X#)7!Jp zTRqHvdx11UCY`DH(a=O$j7-Gs_S6_Ohyz%$Dkrgqvm?kxvzD;^koGoC7}4i1>SUcsTGU9mdN17I9EekcvU!k*79LJP$_G z+N4ILIZ2nebnArULaETm-#eoM4VzU;$0#>Mbf@jtGKS~%3;z@YL9+1T z@z~-<2+df`m=_=#p#a*2zo=0AeyYLtL2}iozcDOSio1MYerEE*6X`cXg~Q-Gwh(JM z|JF~ENTwSDjp#LbjH+Gc(|7Hu+^V0%ql$ScX_TwhX!MYOigWhKHD;~0){}(__sv7M z80SZn%TgFteig2({F;Z3E}`oxv4GnF;xT@+$LZ6=AN;Rh1gL*oA8xp+^~Xi+<%G9_OkEL;7%gT`nkGpfx^T+;R#Kg) z+#xg2O)w}+OF!Njj2{4FDcv?rnI}Qsn}tl?6FPVo{5_tSiWtfta{EednDT0tGctO) zapjw?rxKKRa=O2U9@m2Fh;D)c?e_Il}9eINc zy}8G&zU}(*hrxAv@0#7o!V+*Gy$!O>)fbOsKJRzX!(Rw#;SrC}AX9xdX_Nh45fXWa zCTgyq76`yK>zy#-gkL@fK_{WcN?6*vUJYX$eEoqZVQV3O+4SVdsY~rmY zhu^QEnbu}aRunt>X>-Y-S)@qxlZzJxA^U9F6mLSK&jLB|OCEwfWvom|?jh%~^=#-l zs>GfbC3=L4Ew|y+o|y>e0e`qp;gPz5Wr7m9^0pg+n6eUtXw&&3H0CZ&ULOIte{{_~ z(_Xvph@ZP}Jt!cD4V|*$fX%I2>tO}?B;~MEISy=b(JnL2ugb?JtBdK;3*B`D=~H;O zy%Y*5juH4*$2%ua5?)4u@Gwy_D{k2MgC?4`Av)? z)O{9-jXkj}8Iz5IcqEosA>)RLhCE#)y&n(x|A@> z06kd?B(iFr@AianYv*A7CX@=IF~N7e)kzte_h)8bqCh@d8UCgev2?uj;y2Lrv}r{| zNUuKoc-V&nUr=MM9kon7ubDjVdEc(M1U}W-`vR|co`4Lmk43zgc?C0WETXW1TqFOrz2@M$hWqf!3c!AcYbacpGl4(1e$$VAqc<}6G9 zOO#i%YNXgI@I+0&KtzKU5F$UJ00pRx{5F=GxBSaI#|c3SvGztvdULFf==lC#gr&Rl z7~)H0J+pW4oDC^vB!m*k1zX$uPw>(xniOsA{kd6ib+|&hnL6UYb!o~IgKoEADewZ+7|J>feo|KvL_Z+-Dgg!{uY}jTANP6N3b~^u;`W>A+ zd^v5Tt=^Ng_1wLSjRtnxREiy}qaZ$Jf4%j{v0oYyowu}e>WY{#`fQIDh7PezlRy|y zka@xn2VFC0!->D`sepGZAH%>4Lc%2-fXg{V4bJuUcO(b zkz6om1q!fgT|H8r#(!k`P3x;_?AAES5>&KaF~*FoscP(F#PrWu-=>QO7ND%9bqKZw zE73-F<@Om)=FM3xR<=jTP6#Jbpx~{Yq%xkfxUPrecabyIXZ&Qh!g6EcsBT}1n=e%< zFnDQePP6PGTD;R%uYLBkEJJ{ztv(FzgX#t5iqCgU;S>(xbp2Eb?}g$EN(PU8*C_QT z&^hKep(vuj7RN>G9izq@`x3;AYg~c9;{VgRw%*&q(zV1u-JeBHzR$ixo62!{_GvWCbeU`tj1*L2NIP~8A+z!c@4s_r6EZR|e+-UIisIMLy51%%(N*C(Mx`~* z1fn11(Z|6jjSK`7Ox; zW!k|SbYv-3e3#L5A@D4Po@Gv?gsKXAa}l+TEQiPMU%%y5BI><}L-r_VH$Ipk`tsEK zteuA4gPpC@GOm$bO5#~;yfYtyC#l;B{U$Wf2=6Z^%?hN&&Vp`JMu_L}^n_Nx(-Wx? z{^G0=o?MRlXnYLeD=8b`5tV~%A1tB+7NwBKGoTuLG`N;d=3ACrwkWqqlv_oWbRgB( z#sfeSJE%n)uLjy%tosADwY83Sf2O?{PFjOJK7LH7<-qBC%edn#8Gnt>l$yX}uT<&U zKy9cA+0UR!V^y@xy$Yt?KpRGrj#vO>n#d#l-9M5VS_Bp{ik(#iDa~ z5Wxe3*?|~_7g8;ZQEzZ=zujgoT@&_l;biYt3l@;m+7ZSmv+3r3qVof3x7JaY1OMRo zLvOH{Y?-Hupqg8a2`jf)T z1qMRj_P4g!<}i{44mKYLI8bT@TwMfb@ID#-P_nHl;K%GCL0gQ$KYh;hCwpoewB6wW ztzuAv!r1fDhQH(0R*TMOc^zv?9N(s9+dP_5v!Le9kG96E@`gvZJu|?Ws*HCLN}K)& zJ4)s=&BA3vy!q@kNqVI-1n-(?eTaR=D{9hq7Nu?;u3J#3C~vdv)ON!HsOP3C0GaX4UgPY8|Fn{*7bn?Aj6<*+E7I{0Rcc&o;59A0e|z&bs{`iL=Xiew8LTkAlLAM1YDJ6@{~GN{8)J)I6#AlS_;rFiq~@NsY2AUm|*MBT4)rp zpEP1UijdfO{NzjdAX8GZX0-TA^k*vo77as_p+kAWAI8#k+^uY*^ysd#O1w{f8B||aWEO|C(hQmu3p8N9@TDMEk z;{q>KzYnvrG+Yea(5ab;Ju>!I9+~s+FHZAwYk-Hd}A(KpCDSCquT5qMp8XYhZLwTWPU zRenJ>JMS*JqKC)XsKfLxB02T4xMKlc(=rJU=!$Dt0ubgBTlHKrKcezX?2FMx@5@5&M}^jii)^pnD%{)5KArMmlW~$->&$OxS1` zgddajMU=o1DzKkdv)TD?BV_wt%Tg0_B`ndzy{P0UGIi*S#=4_QH_~`Q$YpRO__~!W zmm3#EapGG74 zk%O`XqMvc;Gn{p++8W{zNk(<&cGt%eD!ZNvtMBl@RKI7a!r$=?-( zM;02sw&cmjHsXhP=x*MJIxI#r=&XsX^>bC?cj4r(zYV?XHsn6|~V$PSjT(SKz9nrf%cB?#+`ju93cJ(+~Rz{X(i4~Q~pbwE!XiZ zAGPE7*~RrOPDU;w?~VoL@9{7*_llj4x56~XiH;DR5);Z{(E=!YSTW_~j;Bu}2JhI= z*rfAonRUJfADbHGJZD~$XTjm4;D*a}_DU@t;KUp6c&0moYi92;2R}|0xA?-x@>60A zjz)dJh9~y4Xt}!z|Ip z-FurRWla1vxa&I?U)U>%urs(S$;suzkZ2zQVWRD>Kv(6Alf{^N**XROSC$jqGB{N0 z>pJtH4;e^(|KMHCsU5m%0HXnp?bO(?ZL<2@KCyx9_zL`Lp4L325(5j~q zodO)7^7sQr>_E`M=Cn$`35Jg~8I zcX3MUig;{azRT5}$HzW)wquOgUed`y@Ez)#iVk8%^E7UMO3=2GS@{P$z@?f# zxpTxet;Rcv)e<-+^3O8+lVVb(1DfF|6*uogOnIt{T8!~QPJONrdDW4OH;@rJ@`k~q zl`MGlHkn7Un}%ltzMPzaT9C;ewnCUeN-sBWE@;fC6DK@iURpMcB#t|n8#k}IHA3AG{C3#K5WB_X& zL1bVXUmEs`>AmG}+M+RXzl5f~+&)xLG#At<*F^M2m%t83XWBegpCbeeMhpTYtCt zG#4hEF0D<$zxf*@8&WdexMl_w+qXIU*q69d|J?AYLt(&p7>jA| zG72vKXT^o!i`ew(RT1AjiLX#oBsTY``4knve|n zuZ^2ZBhEN7P@vY8R*ykOF!T_Xt%z(Cc0&4t8qwsuJL(yh0(tem3tzH*_%@JIR64E< z<=rqi>cWepgK97)&M#eiw%$YangQfNr?M`Jx@#GUi{1Vhf*iG0dwWb6$rI)^qtm{0 z>@Qb6xm1n>S;h0Vb8xnZb^&&U7PtoH=pL)htrU&Q*D^I-ueT{VT%FHwBT_;X+bsDr zmf?Iid-#w%hPp`3IQnZXV+ji$&QKV!1Ubqm{w&>+?(K@XQrgEZCK>V{lQ%=6f9*8B ziqv>I8w@AhbQ!a1(jt)GIlPEM2Gy)bE2RzDRWE^q9d9=oovWr_I0)p;du(Q}PHiv1 zY;!*cHD*vS3q`(^^Q0cnAqn^_e050AD_4o#q;hizS@g81`m%o48%AGW{aQC?$lAHj zrCXKS{B(U3Qu1_F=kM^E_QDRdq0-4%9rZqz%!LG?Bn?njmT`IS{BUKscn<{M3%e~I z*n9AoCXFvABba5m_?WtRvL^*0QxJyS?EXjENzu=YAb$zwcEiP;N)5-V7nrnzj2o&@ z6u-N%44@`t`IgQmMQzFDEC6)>ZhE5gM`vZQm;Js_dnvA1k1qA$m@Z$PqG-)S0>7#r z+IxFMcVjzHlQ2tk#L-+)qG(1My)sWP!BaY8Zb4WRVUVQ)#5ku91Gr8BFm_1wI9kh@ zdJ>aMXGzVfJ(!u?2d=l;Tfp|*IxA2evs~{NokaW^OZ-O^@Lcjm6Hwr&^=l@Dl(R@h z%jQDTs(**vIK{y$gQ}zzOGM2+s=K=Kn*YPn2J4GwBBS#prQw;LhHGzdp`qxP@fjpLjbLvSj0hklr*t(`+^ zq~Mq7v^NLhvD=wXL$P~)RN)=E;45TaA=Xyx+@YKs4Py_UX5t@16HEde=du_xQGyDyi%9mug+VAH9^y4S%I|am*_il_#rEbIeOO zJ9l-?dKN7PPOIPOd&{>Z%(&bcy2S^=+;?GD_Gi^pZqz)V1-jM}NLxKWfo#$~Pwdls zWj4}HxNRiBAKI!Ndb(bm@J%O}D;SF0L?Gvpqo>#-1lq8BS^tY(b=_`<+l#at@z-cw4 z-odWtJSde5Tlzjl{DGVx#*r}>P>(XD4sv>+BaNC{J!+}=mGkq+Z_Tu8*!60F@LkVc zdRb77#|aX!2G5aCj)#aEKVxljsSMqyODR!_GI?nV{jK6>=eHS4b*VWMNlD#JJ10?N zcl;{AJsSkx5!>M|?FCXEt>A2&*x=1iq$+&LJhm#_@kT%V`UW}IKNNGs?JR(T@`c<4 ziTZo#x)Xb0k{ac(ra0#VJ|{0w@{aA(7|UKH9OL_7HQ(;2AG&DK71rUje`{Xt$90nI z?(x>ukmmI?^*6)x@BtQ63x8`VCs1iBXsDef>Q**8D8U);w3{!vo}oN>e;znVbv++R zNP`YX7{&e+8fq8IYs^gCMr9nmOaK6&>fc@IS{u~6sG-Sl#KS|(*G9UhcN z8}E>aaG^h(-_bF^nnKuRyr2M4258$5q5Z;xY1%b#!ef0*A=h;4kliAmJz$mmbwR74 z^~YC5yE9d_?;so_o<FAW`Hyzaplw%_Q86x zOPA7~8=|&E4@M%tr=^E46%+LwO_v_?mpA=w;GY#;X;dA%keR!r<1USP0*aafhS9wN zs~a}6fyx7iWRhQX&~YZo3wS_YO{5LMfb`ppUH;F`^e>WY+cIdb+9AIIK_g~~lt$N# z%A(cB>!==lpbfxtmS#Y%V$`dF36;_UC#hWdu-KeU*dz;Iii)88)v#+lh;mFxu^FP) zO{UL;gW5t<4dyKCD_NR(l8KQsfA%kkeAcN4Pp{uBHFTUWSRo&H3W^Rd~h zEO4eaO(#lyf80~3)|)4Q*{C0qY+#5~c}8&G&k0q<_dVCrh_zv^n(T#xadPx<=@63y zIRQuRTQlE^Kyw|s(b6wtL=wv3sQEglA|88f29T$8_q;a-#~Yhs;4e`c>o0{Gp3ocQ zn?B&?usmOV-fhovzQ(c6WMi|)bb3NCq0g@V^mE(oyMZDkzfZgShAI|LgX6=zNhx|4 z(>8uHen0@3$y8}T=Air8|5O8(M-L=qXmoW}c|h(lz_TNk9x7yE&|lVW@O8~EBtUCB zVxb*F43^dXiuv1c29)W-h?hg4#mTjKrnj?rLx4&zZ$sNmN$j;BGOComzz6q=>0JdQ z#4nS!g;6#Ys3h)i1&vMai6;XIcZCVkc#BNQoN~pO^LA9Ec9xl<9K}7RBsx*3To=D7nnq z1rdOjLQ2L52myh@hRCBeD~KJ!fC1WP4EM@Vfa41OOg&lXMaJ^$GjdZ-~I)o*-D z$#6VVlSVh=1qXb5jv?q{UEN zCXx-5oU1dTrMQWF0>MN#>+f)FS`(}=h~gD9l;`Hn#eS!Ex(U)MK(iK&B{!tmFd!%w zXF>TqT>N?Hb2wqPD4!lU@25j%EKOz{7I{LCb6GK1GLRKg=)C%tZ#{c0HQ}e$sRePM z2GL^^c=a`Khpy|~{f^c-IMruM_s$-%U8{4*=zMz=@6aI1FL8mk72who1Y&{Ab07&m z5FXdD^qkXrS9*948REPF11WBYh1jCjDY+eWaU6^)=5wP`(7I6NKA7{(x_2dK2=DN0 zf(w>M9F*Um=^v~fjN(AbVPNXfr#^}p^$Th!xs)9q8oR^|gycSIYmKw;-}P`fYHGKb zb)T-E69}tbqYntOQE(BDQlrZ2$LLnJ znP3pN3`OqIKJHvlO865Kj?i=|k7_J)t#H22D8q0seu&h@n*uU;9O$5e4sz^)$`}@N zN_-kzSaXS-=?M&Ez$Afd+V)QiLfJH;H_|-xWMO-o6fPl|Lk*|nMylJv6DbV!?4SSD7auXZ{4~7t#>c^ZCJ0wft$Z8o=euY>Q6A0Hd4LB z&(XhChLU@Arv+@;z;cQreppJm$#gw|_lF;;UXu>D5ucw@KZR1(!ljS0{Aq|XMp;cc zu#^36FGks&ZfSZ7g~x|zKnl_GrSWq7q3L!V#Uzc9h|s%#z4)ChEk<1#vy!)QGnQ(! z-J#s7HaRxx+$RT0ky#v(R>H<|LYq3GL3pLJ^hnWor7e_GFnZ@Nq>3%@$fd$0oNjI> zh+YN|XCMyy^*0qGry=Bsjsm#~$&VZO_+lSCfI`2q?Ah^~M1f-^@2m!oeY)B1)Pdr` z)d}*Ct>e%B2dT0s8sfuRu55OA*>BH%><*fUX*ymQ_^HQT4Thi&>kuaT%vc>jsGR2I zq!F?A9t3gQJ~*Q9XEbM25RSg>7}zj`w8aIT%wJU$!sn_j&y1~!?!p+5Z-;pbmm!IQ zj}(vW|0N8_{Y^pE!I=eXABnrB&{4I*y4Llnb4C7=2KE_$ahjuff%9jY4voalWGkz6 z8YL*HEx%XSW%O(_L-l9*HnK+95J`D_ZhJ4Hncl>EMlHiJ?>@ZNg8T!Km%9ZV_F3%O zkI-zgkl3~P^J*vjQh7lbrYHj?C*{46SeLCn-n|{8>d;^|uI5(n+DsRII!*C|DIL@Xvj$zv9##}y+8f)!qeYlTd{ z2lZodmbc85LIc$}mTeHuiW>$(Sa`u@&frs=EpG9OmzgzmycL8$^yGR5gaqP5e zkZzPt;|_u9!H^01Oa~N;x^T?;|4aahzm5vq2@n>DX5dj1>jo)Qm<}f)OrCJiP1W1D zewvLoS~`aQ>lT7~dZYY>;o5{pgIU?+Y$vU|fbm4VSiQQ2TU9iQM^!xK2U{lwidqSD zhVH4CiOG(4K_n_F=ehMmNYq8mtgJ*R2F|puD(+Av;ApXE%9sjmj%aA^s8p<|Ln;WK z+0$&Na878O@zylT-m4bW(Iv_kCtKdhM#Nuxjk)E{byl)Jff9-Q1bSVP+3n~xUUUTp<|9W(_#8_J6Ij^vW;Y#3=QL+DXASQqx+ba9}# z@O91g_z#}vLsX2e>$7mzF=P%5YT5!1kfCJ0)}hbRU3^aZPVHoknX78I$vw{q6_J+o z7^&(q@L~7LjRyBFL8%t^&x;xT4fY4bCxpj^53{9)!l`e<5CqF`h>Txjb`o&ae@yf& z2uN6msUr4=bk6KTYkc3ce;ahlLe5u)eYQM-zvX)iW=*mRliRM{`GA{3u1pzfu5Srs zF@*eqrWhra)%QTGGPG_v;QwY!abw_zmKeY4-$w@0b0@Eoh+?ai#qr0Xg5TBnv+*rn z(vCU6WudW=1nKBkdZCq6&en9w#f19&zc{+r8&Hd6x0T8|mGw?$lwZRnS*8V`$1_~b z%+}L~_sb!I#c>+PdYJU{)+ps35;D zcu9<*uq0K-qStDpGThCPXG3UUIWZa3nglyz_B~+WJip$WZ@;xV!PLIoaNBVw&G(vz zbM)jG-eJm@#AFsLAUxAFnPDSQbeejFQ_#oHhCDH|z+ulXwc#2Xe8wx@%pWr$HzSGs z5WXTFC>x9UAoNl9HyY4?Q3w^Gw?Aaxzx%nPu@8+hmys?kSvkXFzC4(>nn+q&PJlN&BkcGhhS zu)ELWBs^+Oq8VN^R`R@RCrk8@!7iQtRycqYnxBx^l_uQXTL4h8ovcwbqI6k|Hl*zl zszlAAu49tcAR3PR!Y!V{od|^8x(eyCEAZMjq(0Y9Vs$E57>06PzLLq9WjB{6t#nNN zf;r#dMT#zNKJ9plB~{=aminD_*jmlhs5#{5`mSu2X@zpHQFh+Mc86JmJ3z=lu)Ta~ zteSY&LM?*mR=g0!@me54E1wxbC51bu=b5A_3%TrhK zp@a@!lleNs{Sv2XCm{%wWC)RwQEx6fR$|;izW+LacdLEL;n3L_p`n=D1hNVAIX|f)wcm?v z_!D$&Ndce2L_(t95Ww{fV3kx~WJ|Q-YJQ0%gDY`>@FQ3i@~A`*V5y}VP&YbNbj+gF zuYRstNFZZ03QD7062EL|h@EL@84X}y6!$4o;ty<4_EZb55&ZbRK-;T++_{5d+<>c! zuB`onIYiVVXvlixsE&nN?tp$7GKqJWAR-StC2cic_xreqlGkd5F|U^6#Pm^sSlqy( zE76A89oF14?m0N$%3NdY9#LDn#pl@9kokK}J>yI*vpJcQ2h#Kz9Q=UL1A>(u`meTg ztb5;coPE%Bu>z#m zI(AFQHeJomP4(=_=ss%HY<=(225p%wX}QKVaOI6{?7biFOs63W;W!&lGN2~3*j~2b zMqjeJb@TPt`pT6F9CmvTSr_-0zNm{=hD^zY@W`Hbreht!?lYueKJRbaY24}vh&3U$ z+rIfL7^BO`@fLC*nY#CoB0U6Lk&14NuDVx*BhKwQKqvHVu{TiN`72o87bdgm6&kl) zr_SIapRpS%6FrVfb_ks0a?dj)QG!;oiCnaq7ASI?I^dN6TIJ6 z^@DnMv}*AMr7K+O4AJ?@s6l~a1O8>57wL34+p*xtp!4U(RmNg+rHYx{hK$M;c@AF# z6rvM!{L5K-(P}xqg!CVbF)zQ5#WlQP%I5{>qHOU@8L0(>S1!5sOkHIa=j&*IOUp++ zdsZ4_Th(1g(?K#hlR=bb1Q}>WW8p=8NILvL3s%hKaE_L64x8LEZ$!5ixE4L_+{iLL zC90szp`Dt%x)~a{5tCmf9)tp-ho{{c^1!AbQV{GkW@Om)qX-pF0@VvCJlJnL6!R!4 zHq1bOsQO{y=P4)nE7~nG2J<*CTo!Zh0KK*6R@xq(sjAWWi&v%E&s7mWi_N^2lzbwk z&Zvp3&~Y52t800Ix62n@w;d29t1_e^%yBunAuM0^neUCT=0k~zhog`eE~H>{P`qS{ zlj}xlCfAqSZEkemclLR? zeywGLUnEL6jqK|r_-dXucDI%=IUT#b#~%x|nXQs-I`>+{a~|!R20DLARuI0)_98ij z6E;wU(J=61@jg}kHQ#xH60{<2Q<4m&=m(drppgB!<5I2A?N?S~hW7j!($(qANM1WZ zIJk}`3E$_Elh=E(b;Xuu?8(BqlZ+9rs@1wsQMDhbZrz$f25J*YMQ_YXh)^MH;iwp! zN2BF+in(O4A4*-sXrH@!h3#XJY=aM<_+>&|W zHwHE~jkddbRIq(aoFdn&iHE`F~%<9Z`Aof45NqUS_--BK#BFV{)IayONk zdDiet#x{TBq0h4Mz33J)SaAa5hgasQQ_N>+8$CAAc`rNRdGHqL{0J|LVFH;M_Hyq6 zMI#0{t^HX!s+=!8*))a;xE{O4ZL#!=AEI4@9q_d&usrNn6l#7lx2-ugbZrz&=`@?L zYMdSyHwFlc2Axm&H986DpW=MvI>(JhYm2<+J~0g{0l$2`VPQR64)I;DVfdkk*Tg8D ze>E%TRO=uAK9C10Qh~Q5$b_gjUgKYp_y+4Y03$o>|xRF~SwW17^pe4N~u;c|D;0{W^{i3A`M4kp%Q z)s;H@G`YyjxK})Lj(6l0yZO~vlsV|E-tpYo{}?~8QjUL;$mH1*Bh`Ds5pQAnX7)hq zYPyBR0@KB#X|q|TnDNkUZ9?dwCyqe#bx&SSL0a?W+n3Tg)k5BoM$nODoW^QzV8mM0 zN|dMUJj>2yNB8Ddcua$t&ppVLZXN}@C@B7w&GtzHT;=bha!LIG1r(~tbPp#W~xgQ>edtj`&9tKxPm@3+;& zYewIjq0k{SgrjLt^NL4frC@!$7^jpVv!)_=b#sfie~K5lsLK^E1B$e4?rSFyL-Qq; zYY155Kq7J~KHw*cj>S-Pm&Vn1r>^dl?1&38sQw-LjrAUb> zYCmuMXZ)mr_^kjH!Uz0_jTQ_I;}A)kfx68K*?greJ&8FhVy@JDzU-p%5(EP~^-WEa z$}3lDA$q(50mL7W*=nu=_pe8UH5Z16)NOP@^2_Gb?)?+ZdqmhmmElJV#2ePo@u9Y? z{z*aBBn&0B?^J()#*WSD33@Fw*Y54=(GTTKVp{R464WtIhOVDu}WDzY!osTA3E&KWWu~#nFP}@ODr=P;Pn5kM48yf|32h zQfP8tsKC-Gar1341V)%HqfgWa~O_WF2GT@HbX1j6+#oNnR< zemRf&iSyjdyK7#G?BB@fQ-DBvcIo<3d6}Q1Te zNhNQ_#RBtNHffhg21l1d{XlYzk^$+61pnN`&*Mod12Ub~M%Q%xpFj1BL^~MKC+El; z@)n~L4An$>caG8Zgk_D#K0droE z`G-URVOCa#!a4(%(GKag{XuCb@caQTiDERclq5+7v96LI*|WM4_0CBuJyKw}zb zZ3I=?fj~RV(_0OT#ZHV0y@@6j_qFn!eJ1^$At~JuRZQprqhJq?g3%TO8xWdZ9x{yP z^6I40Zv^^{i27XB2Jr#S_>V`n&+4dt>K|U9gRB&jL=EV5M4g4}UTbcXGHcyVc4y(8 zl{p;;>I7pxTL2yXjY&UMjFCpCQiYw z(1_>ya|UyI-4P|k^N(vEsMJMH+C=9#f`H^rHI%}-IJY-8pY~Y4v_s$-k3{&1%n*{x zxs$i(dgw=y38DX^fm)jucKV3M z+^VXe4!sY79N#tv0D!jT4t@!A(QHodi*RgKoCNfkoD7aQ!cx17?03ECN{ z3~*EjR?BXY?B=JzQW68lk1&D8N8?}mKK~bAZxt0s*M;2@2<{RrxCf_k*Wm8%?(VL^ z9U6xa+}#Q8ZovueZXpCX#rysL80Xxa3$EzyQC(HNYwxw@d^R(xt`{6cls|`9z;<%# zv~$m=h*GnSO|;OgYumc}Heptcgj+%ia@frS_meK8;v9ZGdlaM*4?P#u&vLfnDW;ON z+~a40SUK{^2oeW6y*U}ZOp{ZiDGw^;|F|vY)D)ym*t?ysukSnZgjC!`2#R;8L2m*b*x$nji&>`|00gP3zOe zuhzG;O#@S)aCo2`6Q(Xjh40qr`d3(Io~}!z-ObX#%v-dg*ePyMtQdr0H6Wzl#%Ahy zXkm;Wrcy1CaJVEXOcRIU%NfLQ9lkFpDDF1AZ(&308 zlgmSCvoe11B9!DAUcc9i7TKQ8=h>H!7= zWfk*0zBm{Afbrb5e?gY^C zGMB;bm<2*&2V@_P8*y3hR>My3BQm@<>1vx0!?A(}3oZ<&!hcw^co9{p8;#vrm|BPS z`OaAN=7-fQr};I;nK1<*vOj%8Zh>&TJJawwykxpr2>$8(0B^E~v=wqTskY|}`+)>& zknaavj#7oT&IxiCD;cE}AIuo^547KQ&K79eKjFU)Pk&y+Y)$%tpCXBByJ`K+tybCh@%|FI`3SGKzL>W1&53`2zMh_P0RDqI?` za-?r)f;X0iP!qEngWj%)eZ4F#LUMD(u_VEzEB5GZYsjBqC0dV~p^d$PLNyZnVOw^9 zX@}>H{fGrEkwI7^7(AKO=)<5a+Q*CG+#NEo<79buFdI<}+Rxvn|LaS>sHWl4XBP1| zXD{gAZ+f8XW|?`$Dz-=(Fq|dnR7}>(MlL39-~XfP2!Kwe%^v+pBql)AnT>AJRLaBJ zliomk85KR+WVCMskTQ-g?hKjlESNzC~N_)U|I{Ku!6kniZx2MNBV-G`5C?OuDA)Yl`6j}1rBa!9z&h)Ww4Pj1^@ z-&*sHZwfs%7H^h28#25{;Pl_FG|c6E@Rf2(n(eAB>Nl`~QT&Haytb@&Hj0WdmW@)2 z^^g7q^6A_S>!YuhPmqaNPfMSAs+2&38u-YsQ=v-~XhG(v_Fc_588o9k4@k<6I!}L> zDsA$s%cAFk*}fKh5AsYYt$;$09^GmQ(&Q46bucp19hP6ChY$T8159E!>{HzH`RQdh zCW%T<)6koISoW2q>5FYT=q6E=Xw7p&!hQCKlARwVLtuPP(F@ej=QY^S2Oop%JbX?tRuPQ*H{a(sCLWn~wxlz#nWNOC1B=D>z zE}sf4&;P6IcggoJ(EDW8YL>3e40Y~j%_GN4UcqILN9%4)wPCdq1LU)Tu5{w{!~Rzz zyDkbS)_fs8D%PP_@k1e{nNX1<2_%?`Sgp&N-J=HgVem56@k4li*C#S1Tt&Di^E3uM zx?6(P7)2FHfXO{zc*V0;#x8!-u7|lRJ)ba!C@vW6V;!Lb5^MOKh|= z%WHvL1Hhlye)YMMA;20kvNM36f#U-lbcW|TS9z+ED)!nUy?V(e7qFd`drtd7KnSaOq^Y&0$VYW$*l+g;qm)2<`sM1z#ih_)wN?hq?Z! z0a>h*l$(x1Z#l$>+XjVpNb6YD<5oVG3$EjFM3(TUHb1Z`ojgF{t0I!BK50CX^Nwq~ z0au1QxB);jFv=M&4>UNuCj7mD?K@*{&mLoCZE&`$m~{Torq@Bw4A>J_3|sWPl6-Z) z?eDKzqEUYLT(7WWK*REhrEipmM?Z4k;&yvJEX06B;NV)lj!s1J1lzM8)9uV}1T;*+ zrIe)is0xn%@kN{>X^?;1ToBcD%squKn>-!(8gqUxMwx?68h1}`C1+O*kU;jNV1p6g)3+JHn zRzES|?jz~U&?)iyKnfko>-qB}=@~O2g=n&(iqMZ9T2?B`{u-WGYiYFDLHIu^T3LLr zwWhY@EbO}UIDp1p6}*v@X2*_qQ1vChYOTh+P8$CQCFyNyOp=j>1C3~ekreK9nHy@T zmCf-G_?5PRrru@Pm}X5on-1ac0XAd^?5y9J5*plC$}A7^6unl(52Gx}CK&}wtR1IP zo9o}T_3@v zJd)LQb9wO$EiE0%yUwXtQ_rWyH#bujvlgevo33;Uo6Z!KRxlbuD6{aeUju)oRSzZx zu$x6lQe$=u=9`y$-~H_`ecq=+$>Rcl5Vbcd`J%9mIFh&wFsd^wr|q0hDF)LzdDX)k z{+!5{2J3WflPa9V!g?D}1bqGwahX|;vaRu%qw&m!zdAJUj{nBg|FpIIUbYyvlH!i? z^#i)h1c_ERsYw34Ckq=J1_4KC8se1r^7-$3=?2W?Ty(E4&L{3l7~NbnSJ<9UDT1%e zmQBvQ?P#r8dNH{-6i{)mu+Yzf-%R@a_AcK@77Jg;%pU#nb8Ud5RObI}F3;!s@#62# z>wgCE;Y0R2BBkd_?jr;QPa3zYb4{bx)ECHYDn@pQsCxX7?ZnlGKnwW6Tfp7``+_ne zVV+g8y{EKfsLy(7w{k=@h1V?Od{mM+Rz=xRE-ZSDmlZ^silynKjdjkuKDt~0zD&f;W^jCX_FS0N`37ea-!tS65u!Tk(QMN3Y zH*_3^&r!$QIoiGL)-)Rmq+qb;%n{_cL{yPt-6lPfEWIL<)OFha)T z@N}3SDh3*+s6<$lBjEr~@CI|f5I11B_TNT2GY|1@d&jV~REW2aWGI!6?j!J={$c)6 zU%Z~^Y)~%mZ_wWfhQ%E1@WqV<;D@`BveFL=l(6nURXp{toX}xYWR89w zux7a#?{jg|L;`z@l@$~5V0i32E&mmHVWax#w^EGIptpjaYcCA zHlQ=%bL^0H>m>Cd>m|`2m<}#M?S|}+BjP&Q7E)z*Ntbs{A7HpCX4=>Q4v8_v3@hxNaOHiA z5`*(VpRLN#^`4}liYXgm*3Ig;I;r#$f2W^blZnf;()Z|awOXPVINXhy9}~c|XqEI4 zhLTe#$E>Q;;*zhY2qF|i{|dzuEGDvxkh~die>gZ!^HI)ut5!4{0X|_9H3)tg9`Iy3 zuE_57=NS~GE!{-498`+H^X!?t;9Wzrjm z@>Y^Fp_F~3L9=U>>)X+tDBnh^7!)sRP`PhfS1Y8k4IZl<%%h$YHbe>0J#5 zw8QAY?eGvfg)n|4D)xwJO%NP?gDpghNzyRgzP2LgvN!av`D-g(3Vf4Z={B0-wB^aq@#A)M+cdatVrzc%f105AHVB;m zl*XXU?ZbmEi@qvy2g6oRoD+lrJ?R*jD(mmf<}R?g;c=ln7hF^nvMUQUB5YJrEDql? zo!IeEDUwFcu&OST!tKU#%gb~~u=Xdi;>l{AnC`)*;zjxy8IVR)7yRE&KoMu9qm?oa zZ(^#2%(snQ-|Pv~3TW0Fn_?eOP{l=0Wbz}2zG*RtWuw5Au)N_v;Y)JCMB?>X&(*1v z#rDJQIlU5nQoXCo`;s+8Cdyx?&^32A=9-#z%aUP4Ys=zzGT~IF8=;j@O($HV#tKF4 zcIx0?A9n;3J639;;_^U;@CE^t)!I!olGNg5j2Yz!slB_C7TlZ`w0s^xF5_#L!n#?j zo<~);c!E6lul5`#-scDpfBVs9$0uYjUy{hkdD}e?Vr9T?>x^X}nWfr)Pt(s6&5do% zsY3_$*0jon-5Am6ZmZw486pRL0Q`$8Bn>ByXmo~;TpMeQ!8@wiB+HW{Hmp4U3;sur zh<5T7U_f&lYEYgwVis-HV&C&QjF!`7KCe;agPisL<)Ghk6&P`3doTS#N0c*J{&U{k z@R>JtYdbTQJM-4-01my^8GmV+`eJrl98fs`=B)|h>Xeb(3TQc`F2YrPmFd`An`lu~ z`_Z3pAY)+EPDvA-ms|^*DWnl9xhk?jtjU1O@gOGANFjb@ipP?Vrer&;L=`(nVBVEk zfRJBZ5aP*}d2KcmN-mZykJlxgC){a7b*4NPDT?Utq4V{-Q*e;%4MSOcr20PQpCT!S zih0`dT&M{2=4Dm#t|Z843JH&@ldo~bS?fV5m|`X(Jlc>)O?ZF7W6H3b-$mGcQU`y) zS}3WrTU3*k@JdcIUl#u2>-o;ap`_*YNrjO&`_NtxU(WfLr*L>%2-g_lzq`NOc>Zefx}1@)HC%uXQ?pz7125})X|Xkl zXdkM9*SA%#^;hOy1wW$8a-Lk7Q27u3+B#cw!C&dc7tGU!kb5Dl`P6%^I_cL$o1vlw z^rkcB^Lo2|*lo7BP&)>D07T`WfDSeB(W!51ab?Mx<7zzEW$}XuI;0-ik<;0$sMWG> zRy!i;E&gY4>JpI%+Sg&QZ#YB6eGV000)kWtI@yS=Aq20Y(L$cf!z5Du&FSR zH2GpXVpw3O$;62(x7BSNLw!_h1Pp)-ey&=VvPpL3RKn}?on%2v*Y z@rGZTua89MbT10hHEtGV3`%=jg=lEkY)yERspEIfL>+`Qkt6MdZ=Q}O{|e-=x)Si*MCY}V#omzvW}n_&$0a8a^?k>Ve*<^x8d z&eUm;i1rR}5tAeA78jGF&&W!j!q6$@7+PZ;i-(EBdk5E<4>8M;dCZBpGi?-BW6eIV zX59WBK0>VW!!Y5m+r%{LA40*Kilyuh>}FP7#@qR0lG_CEw7=4HWgF**qna2x?@v4y z;6FfBX#Krc2k=kV*32Zx5Ii(bm8?>|1eozjL6b1b9GQcGl>IQQpHqZe ze!$gLjtzXAwdCwOK(Vwpysvo#nR~+P;OimbKWX4W$rLf)ijH9aKV0V zd5+!5NPN#g)Ywa-LBx^-Wf!msW_SO~`j0t)kr_@Jp`ahJ&V0`9>m&c^*KXejC`YVO zAI_5=Taur1Qimn!X4#h!ZdEaaPVCFkKZXj6(dzCWDu20qi6Zgsu(b0+)i%TLWC6tV z^IWAw(Yejf`y*l@9wjivRuVVDXOZAz)@c#W{6GM-!0SSzJzh+($IE1{nW9OC0Z7E6 ziJ-0gWL8ORv+vU1+CrSqjZ2lPB+{$w6Z$A($PuMa1YaMech z`6;d1cCr{IZ0;Y*3o8dWoEeJI{f@bw|FF zp4!Gc|J%naXa){0AxD`xHrpxlQMLn1ie^Q4A$D&SeCx~36|Ubt4ij{oO3C6nNmB9h znUaE_5p@V*$hnDL-JD3Fi$5*$9H%K%+k1!=;xbVo+VI!h8p7Gq8wS__GXew09#Pjt zH<^&eR#j#Uxbe<&(Wt$r^$(@z#=Xxtk7HA7m=bz?c^<@mafOTm$t`b;$VElhx|w)c z8W&w6HDQhf<+n%r49HgtvKmwPj!mpjef?Vhj_v|F^fdmh59B`c(|nbeBPyNhzu!;g zvJD?-%1x!Pi?^Yeym@Ups>x29xuo7+zx=RnU~9TR&Dk(4U-ZT7dxN;W%omS43-DiZ zNos`Ty{yK|FUkz2fP)$b;=Rx$$fmN=4(!=QG^3aqrNP7jRMle=CuH;}<1e+}i7A%n zj*KS~$QHBZ)s6?D(|i%Vb6}K=MM+xhfF@#@EWO9~d`V;a$x?y`vy6rwCf3=ih8j!iVk-*#M+dp=#J>om41?X)I_P~ttn+FZ2nb{9 zUNzI}-V_eLow=+f{9$&F`0UY(S}on0{K^|)5cQa0A?VnC zb&-hEe{dc)DzqA|bkDF0c>?tue<^QzzgEa<$OLe0x0epjob1j@$nPw_gqQDGdt+A+ ze6^n@nFZ%Nsp-KyQMG`6Ag$SATNRaE__LozHhM?e+7HZAwAkKxLj=JUg<+qmL%COc zFsPs|imlDUU=@-HpUrGpNe0!*=$IhUdx(^NGKf}L6tuN+-M+5eK8*FT&|R`;QT(-& zmfNZ(Uz_!<(bH5<2Z42RSwyKVhq7!Du4UV9OeM03BI6iU9K@+sxpCsBDd(+w zZih!Z2E;y8|CMhm;>SonpEE&Dr634HON!B@%#%|G)lNpQ9&wN#HGXVo|Cq^-ROx6u zz45E85?D1Fx8Qo(ZE7jE)aFraQOV(sthWUe2vC5XF6hJv#krMNFm%QB(%F2oT*&?o z*Z^k0(24K6y2qONBXad8k$@5-+py%#Z2qs{Hj&JE%9y2Bc4{T6I<%>on2DbVfWK%7 z#4wa>1NFi07gyWXBdq*qmA|aMsdDePe{WW)vQaH`1iIKemv`gOPGC%)xKMR+r`X(q z)yc#2@k&wg(v0f4>nl1st!~FA>w0RrjWI{A$&-Ae23*s-DdWH~z7@vmP*Q`w2Wq@r z{&z?8a4WRBCu+lZWCX%}c~2?ZZRCFC^!i3R?dLtzFTOjcnuT94Lrwt@D>1uCV%w;v ztF0$7!#(<7<@HZm1@lZU;Ux^XFTFI?41|hX(4sQ=3&Q8&9M(8*AEBbvFqY18>_HXx4t2A4l31CW9q8oGg^nq@wS)nzPM3_ZL0WOs@Dmr z*pmq>kY0Xxq=gF9kzJ-4Bih;59dFV7@NZsD&-3y-BF&~d61QETXv9lho!>tBWu5Os z0xM}#QBeOvy_=n)dSv(9XdNty%>Jm&{r`zQNY3;d1Ev$=oy0k)60__6RYBL))N)~#Qv}u$n<+)q!0_kbHDRPh^mP|qF zx1uN$RN}BHLS=U5MRK|f@QkLZE;$3L5k~n#(Y%CNUCyzEeA?snagFMw064N=T6raJ zCpeFzakT0Lg2CQ;NFB6&$3K5ZuzI%AmB8B2Miwt*xXgk4^n-h2+q$)_dWB)l%}{tM z!V6u*HJT7kfkBhuw=Uoa1(UW!y%WEJS-#tRA4(8h8=$l4XLRXTJO1Gf8~AU*0>E&v z-X+$Vf4boWVepO(bT%b+KpRWpS$kCum*zKI-SC%b1kzB?%V;@Bb9g{OBG4Gi1At?yO(tCbj{G|w;n^nObC5cvx`sX+ zc2XE}-t&pTX?e(F<1&46?Dn+oJ&EylYljRL=0pU40opqHaHIpDiEA*Kr3l=6>B?>` zF%Qc+m1-|V>jkpu<=f4T%4H`FK4w$odY*SoG&6OIFSYFP^e*%6`MrN&D6adNVDk$^ z7#dxGJ%%G7g2T)nBm6UedLwIh@rIEiMotz>0kRtSgu6fSksd=_qM9_|Vyxs*iAEA` zqgJexkj-*A1T+Upl8~{Iexm-y%AOaF8RNbAVvMG#qUhWp_oPS*OkN7oL@zOD59vRZ~XEl z7=Q-jmCRD)dDF}JtyK{hGJXLSdX#!wJ;eKQj=QlofVx^9BlJ8)To4ks3JN=(F98`D z9Lsz{80NYi8g&-mm=V_lwfycDEoF28y)|9#HOX%zA1xKoN>wxip)|7VoP+{YvRfh6 zW~fv|OCIi9HU73g+~xT1Nx=fQM}%bks(`;$HhX3IZi4+MFa4C9mN+Ry*IgnujUm1 z!VdUOv=Yy~oE2sOf!Z9%3P??MM{f4R-H(WkI2JFJ)Qqhrb%Du)D=l5*p=dkrwiEaZ zuCGi@{hRh(q3yFU=QzjOIU{!nS2f1gzlA?pzcK;O>O(~I7Y)Wgll1_snk&38TU&q| zpjbYav{b0d)q~!rZ(Y(o7_w10Y@Pa$&R-?rwkjrGWJ%!SW!}z0`Xja&Zg{UUB@ZMs zgXc<1idW+(WRjc7Pm30X5j%hu75oj6FNpb*8H#W0pp_CUbcY|s;8VuO&m+uL4dt3~ zL+<2pa|=9u>IXeK1`}yiI79(lsmJ9L1?F})uw?m1W?+t35>#mg0V%bfD zBuCrhs}SQcMab_=J?iEf$koZUnfF{a2x$8#d>^DdAq^MI)T~z-l6EDXIe^t`it}9L zI>Di^a~6#&FlXl4VbOKl&>SN?I_=;(i0Sg&Ta_yBDjFs>pH|5me%6bU)xFxTDQ~ad zeu+jGvG=*EtlVFD0%0ZvhLSN3yiY5voMkZ3_fRew&a)jK#8QvuDHcNCAusj*N}bK^ z`;v5k_9?9rl<-=-o{@T3uYIg&e%m3afM%+|K?ehE&5t4Iixkrvdr23uB8D8tvy??G7YWk7tMo8URgh-#-wWV)3W;1alL~** zkEpsF-~y*ZwLr$yul!OKc{d3N)nW6?lcFj=X;-U{;Sr&1U@VG_lFw3Fb+kVZH5&Zg zDCEM~x(XPscm@fswpPwX-|8G86kI-}Qa;4YDwZt%P))G<`1wwqf(kvB5-aKWWLWI; zkHV^P*iC!L*rgN^<-#AT{`klr(mC!k^vT_RkDh5ssUCy?wz8S@O;s0VAN-LU{v8K7 zP+LgK0DiZGK|qcN6aL79C9UG+{bB6+m#+mYLTqShQPYheAm^emWFTza`l9x({yt`C zr;~#-Sys0E)D>EJeGgT9!L}`ZueH8Z zB0>#WD;mU^2$xi=jIfregL7KkHROg81X%x9W?&lNUu_-oW}7jW+enN%m+ofK6u zy7sFf)?=Ey*-;g*pC&H0=3syg&0B68$6o z0+Pzg)$eG!<(mr=-}ANvAd6yL`fe127$i(<{kT?aG{eQFeAVPm!X;+(MNJRj0vq_?sw_ zvttK)=BM@|L%2Y4*4c4(i5dTu2gO6wDgV?S6$~aLR8S=_VCk-%nLizVp82H7txkzR zZJK*nyJ?kzn#YA}@hnCE;*Dt2z4jf$l_xgX#PGv~WlUs5EU*ku#w%`iHJu;<>>(sz zp0ZyOXOZzDLPbPf)XRTbnD(WvWMm`=+Pb+2=z~a8ply#YA{)*~vo@29kFN(Gx2Fkl z3BwSTtvrq&uhqra^q=cUzvo4VFH><@-{-spAoa7 z&4=}(B72Tt51#}C=xN2s?kAZgI4-e#-GykHb)qwlUAzw+Wj{1pcxg&Ig?l1**4QUS zzEMPagi=7?rz3=Kt{y%CGXkX^q~822A9R!BA6gi++hC8TKo!=}4x<*p`=QVpZllGc z5e0;~giOF2Hoz$P#0f{WPA$4L%IqG$+pt)ΜT>=k&Xc37$(Oyw@k8!LOieoJo-p z!|@yVr|G0a+O*Q1JD8JDDyhudloE63N3jhag8R;SDNyHV$ntgoTa{!-c}XhOF#Pt* zp(eB-Xh7`WIRss|@`h{4TE@M3F-IjmmEyaaxkS=Wf@HNw|6*5?lV*CB2BH#AT9OL-jz`T3d z8Fe0T2-QlXJ-`h{Yf&G>2_IFB?ieS{=6|i16BxZ`ArTC))8G&#WlS zF>xteWmTH}+T&H69Py+eDafXGZaBeeGL%eoHaDSiH~;%)WFS^-xxXrwlr>7_kzAxy z)*@u;LSy=BnZvy)#j(_;LVLYZG7-GW7{$sdvJ}~(VocR~;%(L^mrNShXAyXjF}a!2 zNs>lv|He4hDSvpSi-gg$T@iswS8j&<|DqM2j_(_sG-Iu)HSY$g7zJ((@J`xU9sIMP zQ5g=}!W!ONrvE3SB1ia&GX5tJ+NXIp;zQtajn3!3VdM#kE>7?!=0N#-L4s?0uHWln zsqhM#?9A=&t!L&sUX+MTFx&I-y-R)h9hutG$7G^jDT+v$WFkF3oZ;L$&Uk%Jcn1=) zwaVVGqKWFHig-N_&2VS>3`e%IT)u-Ha?iaJQ5NuB7hk+jQs#wFXqQ6rBf0`yK4fpH zG^DoAABQJMbl^EkRl%YnqMIXOhYA7>C#;DX{g>lFtv3RzW{RdAIPtomNVB3FYr3R% zIxD24zD@*;n8TDwLyq`r4Z-pIh|h11pGkC6?=4 z{3HvLqwEt3Gv2q4eXaN#t;^xkH66<0<8yXAh*b#=l(hw)R{wL3wTtPX*`$MY?Nm;| z$TtEQcpcd*dV~!tl^l)FXgWNkScwgZD! zI`OaQ%M393dAxLAGb!P#w3w9Hi^+T*O4xuTf~odbhCBSCU;cTH6jZDrZdnT6fovNp zC66;pcApegL~{&ZDGIfSyy{9bz<2~=)O@~t@G*D4`O^pV{B^y|pC}_KyXn2mvr&@0 zV}baN2144dPg+uPEu1N!+h$xN%ksE-B{IArrzB4~voq~iQF7F+2CUlzGNKM6ka;0v z@`70R<_RSFU4N9^Eee*qNUR${84x;&bHkBRa-FE*q2k{H_8O#qnddeQN( zC3!9nsO5^6-puH)?{WM02W z`QwjqOyveae%HkUaBRD*as#tMyRbGA*Eqq1v}OZU9RS@~P{_JO00-q@{*NX%o%fouTcvKJQo8cU<^#KWbW+w%|gtr}eI5xzEGyV2=Z-4Ig z9Y2N?pwWw`2!{=5=w&(m`wt!;LR2&kxwmspHXsW*Phxr-Gs1*5OVJ45M9AV*xmkdu zV{>=uMUJ>1GjHMtLIf!JUVjZTa~bjAM2niV{HrBX=)Ua;nOT?!Iwa~FR>&4l0)fwsSuT63D>(NTh&D54)2&iLy z_Ed6Ou8lF-zM6`$bZzC{3A!O={o7<$nE6MPxWSTEWRj{MX>GL#}Y|=H85mbeZRt#TiMRd{P z5l^(&x#(f={N2H(ITZ+SpETh(+8Ua@s!0J8bC}rpuq=rN%iZJ>PL5bn=-yv0$styT zNqpdA+JLOn#E1UUHQ&z{AA1k&okKPtV(p0nT=5k)TtC(XG{$LJ{G{MUB+Z{4EL4~_ z1yV54BK_3M8A=)M%MBZFh2UBCm-XJSqt>5U9`l^%dego|>9M|1wAx?9#XAYy5N!Tj zpR)!N{?Pjz`dyWawjCxxwE>Uj)<6F=;4}_|&4?aA)Hc|;(UX;jJ00oH2k@w6*yRzI ztl*0&A69pmyHKW?kc1x*ZP|3Cn;qYU_Ko6AHT$VVHv|H{<&^M+&Fvkml*n&~hD6-{ zR*P_cq=X&QP*C(h-2(xY$AMkNbj4qF?At@#{LG(w@>On8#}YYRQkG8Hj?Ssh+%b_W zP78{lT&cdfODCHF3)ZM+k000tggG~UJm+ro{n%`Nb+kJtX9)yqMOxVsuQ~}C^Gx`d zp4}_uuYzHkOotfv?NWX9o0Jaqqp=btrP!tX461J0$WvP*gLMpFjd+qmgcWQwA=vQ6 z2)>MNtQUy4C1Do>xOaQ=|0}1sbEBZwtm5-~EFFQnNgiU4X&U)PAf&$M-vy-D!Dj=z zgADFyN}w0LgN26&*Kl-u|8Mv8vGwk6p_%;+n~tJQxM+S73oja+Ni(kJ*{F)L_0RO5 zsxoU6-Ur<-L4N~2nU_#VU}h9}YXhrf?HLKMBM?OmiCL2wXx@pAGFeIk^hXDVnhH3vMQFHLvF$yI7&N$}L z*Kc-+YLxB=x^F`q2BZF~09Ty88T5hN>)Iis4SK8fRqkLiYdA(Y{jvU&-|Nm(C+DRc z3b$2FrF*tt9}wa`S7!Y)^(R~1zb8fj<~?SU74$8M5e1EoC+(>*x)>TYM}EV_x?%+aQXIB#lB{Zv#o(zq*Y`8iG|WfUPg$dH+~312)% z74aao{G%R+X+k3PMR-xq_i_~00=1QA{cqIY`l5tyqBCl_<&qL*((HzI5oLmF^1+$> zHelvTHZz42M6(lV0c{Byg7ZalDIjk#!8zi?(beS)Ua@-&N>ccLs!NJQQNO|8rSmw( zTx~}di@b!k`+^$XdyeRJU7V{3ZoqN_aYABi8ZD5d1vDq_PY0lUg?6=TXJEigQsj=` z2W~Hxa{*komd z^7q5|uU23*_9ni;UvZdwSB_C%nN<%P{#7v02Rt5%LE#q6jAiqtF;kr4{m%Eifg-L z-^{%wN=XkAj#$GyxqaG0cn1L%UqQ2DE7%RANaFSe=B=%OXI{vTPs!T;@ustzVZW6yr?<92DO}QA}u5z=qO|AQ6)8RY>!IlgT4k+?g@zJ4saK=)yjY4zC($_H&GKx6P+bwNVAk z@~Rma6)~w%&e^P#L?0nU*nE= z<-C-`oiSxS+3?c|$qOn*0?}%lLQJL-wMs$#i3pnKx~_-My8c@c&ZPQOZ7DHfRSbDR z%2&zXD+wTo-jD?tS)bY>lvaUx(|)Ckep-R406ws%(7II@>hYzJhyMGD(OVygs8*2AKeovW?7!2g*Kl*)ounhI6(x7=C z;q^8y>2IN?7fD*+55XwU33?wqvtMGMo98@FZsHCsF|llL#hb(ea|#v&c4Uzh$a$aw!yB@ z9gJHMPV$cE_E!)E5o9c+Qh4k#>TL96irB!l5pz($>2LUyMF)0V_hig!=4FZPv*KhVI|I4}eslx7XI)!pjJ#$9@7Pdz zTef_bcMTZGI0hggGJ^mzCo+WWhxD&v>!hJts0{W%EPIrU0$#}tZZ)6FfkEDO>l}08A3JOtl%7v(c+J}Du2N5?bN6tvt|bYS&CO4e~W_F^Nr27 zx>7QoVVmUaaaCi6w|4R2+EhG$lK?rv@)?+1lTuW^=3+8#zv~<;uLkMnnv|cc84L(`>fi#%dv~x8MMw zN$xiH|{g?4SvPwYd1VDwvf|bk-{LwHYu$3+mEvART zV#>EHrMMPFDmev7(mgz+Bp7CWp}o4^(o{-Z&lg|te&zQd1zCol)cyww`M!u2W;R@h zalTQz)o3N;{=Ay=DFzwIh5?VL8xGl08ERVplfJO$#3v;yrd(T54S2y*1f&xL+*fSl$e0h@6S#3sB$ zIXc3qpy(cl!RskXn}G}WmD^?MBjo3Zva|xIF~Tw)D)R)SMWZA~+38boao@y}d9NW> zrMa3jeT~Ea4&D_O9dbPv4cGOHh>cIea%C$687)DiaJoTvwI!S30_&kebjGgzL7kVz zdkC=SVMFW}GvhWW{Pwn|fCm%j%vEOK)m-xVD>-D-AjbceoWsKkxi@R~k~er{D0=>G zOd4DmP#OSv{-I{W1CoTD&MjJS=OBH`{ANIyUaL8y-{23-PZ&U#!0CT!ZR-R~f0Fzd{jMeX_Vo38vSR!xgHA;{pMw79UwPo!7Dd zXV*p-6B%s=!{Z^dNCCO1nri0ZsM#OCwBxt3pIOF z$ZaIODyugTStyYfjObPpcb6pnT#P}U z?O|3X{#$m%avKs-K|o5t(N^_v02Fr>&3c;QNeilZR?^fDv8G=UB8DuVRYMwLKvsD# z2VP0v5bt2@@)9%8t0M%n7oq2p%!eK%PU;x$R0$vY+?%!Pr@RXpOUN~>9QR${w{>|P zcijIY5B9&?{TcoL-QeZ_Z`0-XongpJ(Ef48N6+X$Us)Jt>Bud0^z+?_;?QYBh3+_o z^CD-1_~Hw#ZT*tU-+mU367R3>SN$P#n&6?#SU{-y{u_P}SH*Vcz-e&Jt$GdEl+!II zo^A-oMN;kI9LJ`;Z@slMLpdw=nJUfrMQ{S{d4Nfee)9|n_r;L0HHQn`Rb9ScZ}Naw zfB9*UZqP1b`w+TP)FT!vSAT-Y>DBuC#XF8Lbr=&i#o@8-;;A}kSl!L>&Ji~LtmS3c z4)yODNyTa~!Rv$i>WSaY=X@p2{kW^YRH(5QNF^hUb~-t42G?#jfj9yVNo$O*k>|%$ zRsUKTByWknt}O~fD)oj`@E&iT9mMD%G<$W088j;j^o?#k(mk^}>~mf(WZJ7&9z%AC zqfA3yiO~?{4u?FaycW3RT`Q5(1)Lv*VZyqcIJPD37Tq&y`yZRi>|d@`+&dNKRsD3I zXYYB4I7va);{68kdLu_?cQ5cjtkP47?llmkvD9V>KfwGT$w?A8Wr!3l9MU$tfDr}X znaCwyh`r%3!ISg8u@LqP#~5XeT5BxIDqfV)f_3fd5cFC|-PXpe#6DgIs~Z}2PO!RC zY3)hd+pzrRTM;7APVKN!68BP|kf&gzVKxT>;c-aOq6<|u!MY~r4FYoAG~h-8O{1^B zO!Cw`XyU#H&extNcCupio6i{jA@qW27hzcqW~n{l4u~gvpld;kf#=)C<;4Nd6!9NU zDI$f4_crISN+SU~`~~UvFYl=85AZ%Rnr(p)RU%BSS4<$=Mg0U_3@A{%U>=w*?WSHN z5}QIkes}UeLkd#gKKkH3MUVm8qeXtZ{2ReU+BE?j$g9zG`&S%ChfnDYztuV3n2}IL zNQY$T(|D%W&nX!a+V|aD=a{KE&l!ITRXfIf{%PM|s>WrXki#wLdL?BNh|rMsdH55H zUnzl_fYsIQ@`-yf(zGaCtJ_z)jcr`aG5U#>7TKKK4+r+Hw^xsZl^1Y{4s*1+|Q{jz>aV!cH}omF(Wdue+*inpPHAT zSbBFQ)SM=SxTC5pk+0It7<}i^MGAY4_{bYP81@EwZ?=CueGm0jdcZf_-Gvbhc&kfZ zJQsHV!hG|)8z{U|xZ!85JKOv*^pXDhN2xHrEmG3TEztX6-<|6rISof|nVz@GRiHM>U49veL-$kE=P-J&8ujpB3_pT~ozXSfs2AN6ghVCwUS zyd{N_SKrLFv$tG{<^H8_K)`?lin zdY%ez7N#$>7ki%tJy=ZhbG!{wrUTA*ZpDeWCUN_!zfy5@^yl}edxU<8K1G}55`)fT zd*Y_1)pXh+!NMREt6-*AK>2vf=>>irGKU)~t8P|4M7f>Ak#mhO>GTt0uX?3<(_Dms z1colr15hL)kG664I-&Wm?*%dIaZJip)JO%mUQaNoQm}`5WG49c?9Y`nfZMTF?$M2PWJ5z;}LZ-lqMU{h8agh0GoT{{iV_*xP~jar?5afYtjtib|ytF%U(JBxp<4u5XuA8~T954&L2 zAXFbj?!Y$p^?%MCY}DlrM`n$fb6Ro~M|BURn@FGh00Ha+jE30={hhDV0q@=-b}u}# z$Z+;r#UkM%q49X-H%f@suznHtKc(I;mrKTMmQQ~swij|aV%k~76;;jlbKEvs`<>Pk zxup9fPAe0=Y%|z{y@ru`^i3fhLLIdK*`l6wDb3J&_BlQ1wPqw)EP?I}PA#t_tnMV* ze4PmQ2hIJtB>tb)&iow;$BE-*UCGTlcag$c*SgoSvCi=MkaFiHci52ZZn>|*ZjL0Z zB37<)-&dbnmO_NenO#S2mU8>i9Cz+1g@8HT2t)AS=>prm|e;em{~INAAR&fn6GHXe6zo_rQ-k8}^C z_>v5+4j01kM`OuhV~-aPPI%t0e(!efdO41kOgObRSrUEQbb7d3c73=d@@?KbE0NE0 z(JNHCT&+5tOrKc;072SAB6)q&`-&rwldaj(f}Dp;7SS`cLTVkYqyp|zJ$hg=kyWen#7%CdvWEMp-Wfh~Em`Hd0=j;7+cjhs!6*5; z!wum>VnDl}@yZ%)9StmAuO{blq)UBF`3N3XQp;J-{h=RbY)^(na)u4%e!~2Ldp|dd z5E^Y)K|3Tn28G8%*IP0%e^(28Z6VuxN-ja6iNeo)d&;eQUP%p?TMr{81{V1&TCdgK z(8Jh>MOFnF60P5_@Y4k>z{NwJj?V?732syWeg^t9TSxiDL^h+p2J(laIT9#z7T0PA zk5Oa_!j!tRhXmHSY@q2CU?>ud4C=0RHS?^OVR!~ljADjXqqXCepL}r|kas$ZysN`l zynbC<-0B*s{Z1z%C9d36F5;?wp(Y!FHH##$yZc)9DmxJ=H)nw5`6H_uHhtl6ptbEP zYb?ZzWnfPH>@}bm6+F|PplZ@TxiQrzNE<*4impCl|L3!S=!}OSmrA&|Yf&oxjJ4^< zB2->`bGkj@4oOhe%O4Q(cx<3pK`cYmn@ARCN{*$l>}yD1x`zhSA_IS~vU$nFipL>F zo(yRs`!3P(78$QtY~M%sO2ZMKz~HLuj>x`#pQsf1K!>Gh^+Eh~z8$zU5>3wNb|=MHB96 zt7iwv$2l~c=b==WxyaB{Qx0wbrYp9bo3WT-UE5?i|JLM0;CIb?9<9CL*qa4NW9c-f zM7Jq}d7Mgle*&^(+pI)fURwmglYwo1?rzgnDaV>*>nN+@?>Gib3N1+W-xwE5`-=CQ z+-v;Y^!_V2$vs;(eqFaMx)nOda)?C<9n`4zc!Uh1Au+tTF!#owIeOd3%?EOt(Wz_b z(7K?_G42ltsFq=PEu;gyvAFUVd?i!}ULj385Q?$%xVdmW&55Ig`KOlrV8j`__oE3yI&_?5B-4jEL4~D9t(s8@Hen=XV7ax3i8jlbo2+5;z?Cr%@9nf5dPwJlGX|< z>3hVi$tr@UF}^C&Ots!TaP>P!4Xls=E42hH3V`W3-iF}WT}M9|6-d>KOp;K%wf4IF zLUi`|kej-YCCS zst?5Lv#wm8ZGiHmFaTo(5wFZsc)xMqsE~e8{4tob59Rre0&<c{Mc89yLmb=&8{Ng8f zRKI=eq%O9k{|Q>@8J$o>&O{E+Dl4Wh9ZVmQrfSkO3klF#+K9SAqicp(91Fnrif1$Yw|Foi&tc$hgD^7H?>@=$H=o8}=kFj4s1Yzl$ z-+t8A=7r4Z6}>5@L~5D6W!@K)ksi^rWhh!}qA%K$J=)_M(I!lCgz=sRv<}NW?BW-w z0D`+o5!&5}_o13RyV-A=+&LqDk8S%ug?mBAR?O5zRWn6g|H-Do%hD_2f}?DGeRcC; zU->GfR#3a4HlAA#31HJ2RcJC9!t9!xMa=!?*l96hXmelH`rF}VEhA-23hyjm|L$n{ zwp~t@7wHj2$}zEu#DEzMf|cr9mMDn4iZU@o>p3!H4Fu*pNzLjNE$myU$SocET}H#v zh+!MEw-&u2l1;9{5+Zj&CQAPS|cPy^2__Wz>sWhE7eWZBKY@M1pg{rXiub z1|myQw=)IVWy19^14`@-)Ga$jZC%+ZwC^1bFSnvb*v-b9aR z&sK_@d<;PBSyF|csT1x9OTWvlcAeQOqh$;W?y1~11~_xNiVn=9KD3c8cxiDIWSCoZ zNiX>75*8CFmsA4sYO6?AR+e%8b+0B21Jm=eCxi5r?WG(~t{|;wFcG z43>V4TxXX9B|#SaS}J&Nnj^+jj!1jEnF7RTGdSk>nd0$?81-1bKN9CY`(7csm&v15 z<5h9N_d|6SWJhcNWkW4Kb3Itv){c}_bIOJ4o8@K8zWQaDBr0RAyL#Aw1EWG>#pl%+sR(D#aGcHh|Bisq2a~-bH~J} za2SWH3^hWdhLbiu05g=$!PX_nQQOyO`>ulGfzJ4}d>5ODYb2w7(BJ=O2_ru+Gn24>Jma z>#9{9gv$llvu1oV_oU3>;PX-(~K)E$=R;%TKk4Pa{{$I5BdIkl>Ag=hI2%tB~4vOX50m6mU@DRErOgREd5 zpulS-ywUB?eLv}nbD09zOKoR`hwHIjI8&$Kbl@M{V#>05)29Ic%xO?dCZ(=w#{sk5 z#C*QitCsw3$(kc;{Mm)6+$2BtmKTb`>*>pQUCt&_+`$G=;F#AK&1bBkW>yujt(t8e z^~hm~Y#dQP1pZhr5)B?!o!eT-0wWpf`C$!pWTl}pxQ2)AB0U^iT1n8xW$hG@&T2sl z>SAcm-gZg2quRrrJ%0N!=x|MdyW#B^J%#v(HGM9%7=vceE%$RkI6cem1URPr5&k+8qG%)rED+N=ke{^uem5@VQ4> zX+X7W%~_j%FFgtF$)}XZEao(ps{ MGqyCUF>sIjAKI9?m;e9( literal 0 HcmV?d00001 diff --git a/msd/inc/define_icons.php b/msd/inc/define_icons.php new file mode 100644 index 0000000..d50dc3d --- /dev/null +++ b/msd/inc/define_icons.php @@ -0,0 +1,44 @@ +'; +$icon['arrow_down'] = ''; +$icon['blank'] = $config['files']['iconpath'].'blank.gif'; +$icon['browse'] = ''.$lang['L_TITLE_SHOW_DATA'].''; +$icon['edit'] = ''.$lang['L_EDIT'].''; +$icon['delete'] = ''.$lang['L_DELETE'].''; + +$icon['index'] = ''.$lang['L_TITLE_INDEX'].''; +$icon['key_primary'] = ''.$lang['L_TITLE_KEY_PRIMARY'].''; +$icon['key_fulltext'] = ''.$lang['L_TITLE_KEY_FULLTEXT'].''; +$icon['key_unique'] = ''.$lang['L_TITLE_KEY_UNIQUE'].''; +$icon['key_nokey'] = ''.$lang['L_TITLE_NOKEY'].''; +$icon['table_truncate'] = ''.$lang['L_EMPTY'].''; +$icon['table_truncate_reset'] = ''.$lang['L_EMPTYKEYS'].''; +$icon['back2db_overview'] = ''.$lang['L_SQL_BACKDBOVERVIEW'].''; + +$icon['search'] = ''.$lang['L_TITLE_SEARCH'].''; +$icon['mysql_help'] = ''.$lang['L_TITLE_MYSQL_HELP'].''; +$icon['upload'] = ''.$lang['L_TITLE_UPLOAD'].''; + +//other pics +$icon['logo'] = $config['theme'].'pics/h1_logo.gif'; diff --git a/msd/inc/functions.php b/msd/inc/functions.php new file mode 100644 index 0000000..98e3b2a --- /dev/null +++ b/msd/inc/functions.php @@ -0,0 +1,841 @@ + $fItem) { + $between = explode(strtolower($fItem), strtolower($string)); + $pos = 0; + foreach ($between as $bKey => $bItem) { + $between[$bKey] = substr($string, $pos, strlen($bItem)); + $pos += strlen($bItem) + strlen($fItem); + } + $string = implode($replace[$fKey], $between); + } + return $string; + } +} + +if (!function_exists('stripos')) { // borrowed from php.net comments + function stripos($haystack, $needle) + { + return strpos($haystack, stristr($haystack, $needle)); + } +} + +function Help($ToolTip, $Anker, $imgsize = 12) +{/* + global $config; + if($Anker!=""){ + return 'Help'; + } else { + return 'Help'; + } +*/ +} + +function DeleteFilesM($dir, $pattern = '*.*') +{ + $deleted = []; + $pattern = str_replace([ + "\*", + "\?", + ], [ + '.*', + '.', + ], preg_quote($pattern)); + if ('/' != substr($dir, -1)) { + $dir .= '/'; + } + if (is_dir($dir)) { + $d = dir($dir); + while ($file = $d->read()) { + if (is_file($dir.$file) && preg_match('/^'.$pattern.'$/', $file)) { + if (unlink($dir.$file)) { + $deleted[$file] = true; + } else { + $deleted[$file] = false; + } + } + } + $d->close(); + return $deleted; + } +} + +function SetDefault($load_default = false) +{ + global $config, $databases, $nl, $out, $lang, $preConfig; + + if (true == $load_default) { + if (file_exists($config['files']['parameter']) && (is_readable($config['files']['parameter']))) { + include $config['files']['parameter']; + } // alte Config lesen + } + $restore_values = []; + $restore_values['cron_dbindex'] = isset($config['cron_dbindex']) ? $config['cron_dbindex'] : -3; + $restore_values['cron_dbpraefix_array'] = isset($config['cron_dbpraefix_array']) ? $config['cron_dbpraefix_array'] : ''; + if ($restore_values['cron_dbindex'] >= 0 && isset($databases['Name'][$config['cron_dbindex']])) { // eine bestimmte Db gewaehlt? + // Ja, Namen merken, um spaeter den Index wieder herzustellen + $restore_values['db_actual_cron'] = $databases['Name'][$config['cron_dbindex']]; + } + $restore_values['db_actual'] = isset($databases['db_actual']) ? $databases['db_actual'] : ''; + + $old_lang = isset($config['language']) && in_array($config['language'], $lang['languages']) ? $config['language'] : ''; + if (true == $load_default) { + if (file_exists($config['files']['parameter'])) { + @unlink($config['files']['parameter']); + } + include './config.php'; + if (is_array($preConfig)) { + foreach ($preConfig as $key => $val) { + $config[$key] = $val; + } + } + + if ('' != $old_lang) { + $config['language'] = $old_lang; + } + include './language/'.$config['language'].'/lang.php'; + } + + $oldVals = []; + // Zuordnung nach Namen der Db zwischenspeichern, um Eingaben nicht zu verlieren + if (isset($databases) && isset($databases['Name'])) { + foreach ($databases['Name'] as $k => $v) { + if (!isset($oldVals[$k])) { + $oldVals[$v] = []; + } + $oldVals[$v]['praefix'] = $databases['praefix'][$k]; + $oldVals[$v]['command_before_dump'] = $databases['command_before_dump'][$k]; + $oldVals[$v]['command_after_dump'] = $databases['command_after_dump'][$k]; + } + } + $oldDbArray = []; + if (isset($databases['Name'])) { + $oldDbArray = $databases['Name']; + } + $databases['Name'] = []; + $found_dbs = []; + //DB-Liste holen + mod_mysqli_connect(); + + $create_statement = 'CREATE TABLE `myoosdumper_test_abcxyvfgh` (`test` varchar(200) default NULL, `id` bigint(20) unsigned NOT NULL auto_increment,'.'PRIMARY KEY (`id`)) TYPE=MyISAM;'; + + $res = mysqli_query($config['dbconnection'], 'SHOW DATABASES'); + while ($row = mysqli_fetch_row($res)) { + $found_dbs[] = $row[0]; + } + $found_dbs = array_merge($oldDbArray, $found_dbs); + $found_dbs = array_unique($found_dbs); + sort($found_dbs); + // now check each db + $a = 0; + for ($i = 0; $i < count($found_dbs); ++$i) { + $found_db = $found_dbs[$i]; + // Testverbindung - Tabelle erstellen, nachschauen, ob es geklappt hat und dann wieder löschen + $use = mysqli_select_db($config['dbconnection'], $found_db); + if ($use) { + /* + Undefined variable: $old_db + if (isset($old_db) && $found_db == $old_db) { + $databases['db_selected_index'] = $a; + } + */ + + $databases['Name'][$a] = $found_db; + $databases['praefix'][$a] = ''; + $databases['command_before_dump'][$a] = ''; + $databases['command_after_dump'][$a] = ''; + if (isset($oldVals[$found_db])) { + $databases['praefix'][$a] = $oldVals[$found_db]['praefix']; + $databases['command_before_dump'][$a] = $oldVals[$found_db]['command_before_dump']; + $databases['command_after_dump'][$a] = $oldVals[$found_db]['command_after_dump']; + } + $out .= $lang['L_SAVING_DB_FORM'].' '.$found_db.' '.$lang['L_ADDED']."$nl"; + ++$a; + } + } + if (!isset($databases['db_selected_index'])) { + $databases['db_selected_index'] = 0; + $databases['db_actual'] = $databases['Name'][0]; + } + WriteParams(1, $restore_values); + if (true === $load_default) { + WriteLog('default settings loaded.'); + } + + return $out; +} + +function WriteParams($as = 0, $restore_values = false) +{ + // wenn $as=1 wird versucht den aktuellen Index der Datenbank nach dem Einlesen wieder zu ermitteln + // auch wenn sich die Indexnummer durch Loeschaktionen geaendert hat + global $config, $databases, $config_dontsave; + $nl = "\n"; + // alte Werte retten + if ($as) { + if (is_array($restore_values)) { + if ($restore_values['cron_dbindex'] < 0) { + // Multidump oder "alle Datenbanken" war gewaehlt + $config['cron_dbindex'] = $restore_values['cron_dbindex']; + } else { + //den Index der konkreten Datenbank aus der alten Konfiguration ermitteln + $db_names = []; + $db_names = array_flip($databases['Name']); + if (isset($db_names[$restore_values['db_actual']])) { + // alte Db existiert noch -> Index uebernehmen + $databases['db_actual'] = $restore_values['db_actual']; + } else { + $databases['db_actual'] = $databases['Name'][0]; + } + + //Cron-Index wiederfinden + if (isset($db_names[$restore_values['cron_dbindex']])) { + $config['cron_dbindex'] = $db_names[$restore_values['cron_dbindex']]; + } else { + // DB wurde zwischenzeitlich geloescht - sicherheitshalber alle DBs sichern + $databases['cron_dbindex'] = -3; + } + } + } + } + FillMultiDBArrays(); + + //Parameter zusammensetzen + $config['multipartgroesse1'] = isset($config['multipartgroesse1']) ? $config['multipartgroesse1'] : 1; + $config['multipartgroesse2'] = isset($config['multipartgroesse2']) ? $config['multipartgroesse2'] : 1; + $config['multipart_groesse'] = $config['multipartgroesse1'] * ((1 == $config['multipartgroesse2']) ? 1024 : 1024 * 1024); + $param = $pars_all = ' $val) { + if (!in_array($var, $config_dontsave)) { + if (is_array($val)) { + $pars_all .= '$config[\''.$var.'\'] = [];'.$nl; + foreach ($val as $var2 => $val2) { + $pars_all .= '$config[\''.$var.'\']['.((is_int($var2)) ? $var2 : "'".$var2."'").'] = \''.my_addslashes($val2)."';$nl"; + } + } else { + if (!in_array($var, $config_dontsave)) { + $pars_all .= '$config[\''.$var.'\'] = \''.my_addslashes($val)."';$nl"; + } + } + } + } + foreach ($databases as $var => $val) { + if (is_array($val)) { + $pars_all .= '$databases[\''.$var.'\'] = [];'.$nl; + foreach ($val as $var2 => $val2) { + if (1 == $as) { + $pars_all .= '$databases[\''.$var.'\']['.((is_int($var2)) ? $var2 : "'".$var2."'").'] = \''.my_addslashes(stripslashes($val2))."';$nl"; + } else { + $pars_all .= '$databases[\''.$var.'\']['.((is_int($var2)) ? $var2 : "'".$var2."'").'] = \''.my_addslashes($val2)."';$nl"; + } + } + } else { + if (1 == $as) { + $pars_all .= '$databases[\''.$var.'\'] = \''.addslashes($val)."';$nl"; + } else { + $pars_all .= '$databases[\''.$var.'\'] = \''.$val."';$nl"; + } + } + } + + $param .= '?>'; + $pars_all .= '?>'; + + //Datei öffnen und schreiben + $ret = true; + $file = $config['paths']['config'].$config['config_file'].'.php'; + if ($fp = fopen($file, 'wb')) { + if (!fwrite($fp, $pars_all)) { + $ret = false; + } + if (!fclose($fp)) { + $ret = false; + } + @chmod($file, 0777); + } else { + $ret = false; + } + + $ret = WriteCronScript($restore_values); + return $ret; +} + +function escape_specialchars($text) +{ + $suchen = [ + '@', + '$', + '\\\\', + '"', + ]; + $ersetzen = [ + '\@', + '\$', + '\\', + '\"', + ]; + $text = str_replace($suchen, $ersetzen, $text); + return $text; +} + +// definiert einen String, der ein Array nach Perlsyntax aufbaut +function my_implode($arr, $mode = 0) // 0=String, 1=intval +{ + global $nl; + if (!is_array($arr)) { + return false; + } + foreach ($arr as $key => $val) { + if (0 == $mode) { + $arr[$key] = escape_specialchars($val); + } else { + $arr[$key] = intval($val); + } + } + if ($mode == 0) { + $ret='("' . implode('","', $arr) . '");' . $nl; + } else { + $ret='(' . implode(',', $arr) . ');' . $nl; + } + return $ret; +} + +function WriteCronScript($restore_values = false) +{ + global $nl, $config, $databases, $cron_db_array, $cron_dbpraefix_array, $cron_db_cbd_array, $cron_db_cad_array, $dontBackupDatabases; + + if (!isset($databases['db_selected_index'])) { + $databases['db_selected_index'] = 0; + } + if (!isset($databases['command_before_dump'])) { + $databases['command_before_dump'] = ''; + } + if (!isset($databases['command_after_dump'])) { + $databases['command_after_dump'] = ''; + } + if (!isset($databases['praefix'][$databases['db_selected_index']])) { + $databases['praefix'][$databases['db_selected_index']] = ''; + } + if (!isset($databases['db_actual_cronindex'])) { + $databases['db_actual_cronindex'] = $databases['db_selected_index']; + } + if (!isset($config['email_maxsize'])) { + $config['email_maxsize'] = $config['email_maxsize1'] * ((1 == $config['email_maxsize2']) ? 1024 : 1024 * 1024); + } + $cron_dbname = $databases['db_actual']; + + // -2 = Multidump configuration + // -3 = all databases - nothing to do + // get standard values for all databases + $cron_db_array = $databases['Name']; + $cron_dbpraefix_array = $databases['praefix']; + $cron_command_before_dump = $databases['command_before_dump']; + $cron_command_after_dump = $databases['command_after_dump']; + if (!isset($config['cron_dbindex'])) { + $config['cron_dbindex'] = -3; + } + if (-2 == intval($config['cron_dbindex'])) { + // get selected dbs from multidump-settings + $cron_db_array = $databases['multi']; + $cron_dbpraefix_array = $databases['multi_praefix']; + $cron_command_before_dump = $databases['multi_commandbeforedump']; + $cron_command_after_dump = $databases['multi_commandafterdump']; + } + + // we need to correct the index of the selected database after we cleaned + // the db-array from information_schema and mysql if it points to a db-name + if ($config['cron_dbindex'] >= 0) { + $cronDbIndexDbName = $databases['Name'][$config['cron_dbindex']]; + } else { + $cronDbIndex = $config['cron_dbindex']; + } + + $newDbNames = $databases['Name']; + //remove database we don't want to backup + // from newDbNames + foreach ($databases['Name'] as $k=>$v) { + if (in_array($v, $dontBackupDatabases)) { + unset($newDbNames[$k]); + } + } + // and from cron (cron_db_array has different length to newDbNames: at least mysql and information_schema are missing) + foreach ($cron_db_array as $k=>$v) { + if (in_array($v, $dontBackupDatabases)) { + unset($cron_db_array[$k], + $cron_dbpraefix_array[$k], + $cron_command_before_dump[$k], + $cron_command_after_dump[$k] + ); + } + } + + // find new index + if ($config['cron_dbindex'] >= 0) { + sort($newDbNames); + $dbNames = array_flip($newDbNames); + if (isset($dbNames[$cronDbIndexDbName])) { + $cronDbIndex = $dbNames[$cronDbIndexDbName]; + } else { + $cronDbIndex = 0; + } + } + $r = str_replace('\\\\', '/', $config['paths']['root']); + $r = str_replace('@', "\@", $r); + $p1 = $r.$config['paths']['backup']; + $p2 = $r.$config['files']['perllog'].((isset($config['logcompression']) && (1 == $config['logcompression'])) ? '.gz' : ''); + $p3 = $r.$config['files']['perllogcomplete'].((isset($config['logcompression']) && (1 == $config['logcompression'])) ? '.gz' : ''); + + // auf manchen Server wird statt 0 ein leerer String gespeichert -> fuehrt zu einem Syntax-Fehler + // hier die entsprechenden Ja/Nein-Variablen sicherheitshalber in intvalues aendern + $int_array = [ + 'dbport', + 'cron_compression', + 'cron_printout', + 'multi_part', + 'multipart_groesse', + 'email_maxsize', + 'auto_delete', + 'max_backup_files', + 'perlspeed', + 'optimize_tables_beforedump', + 'logcompression', + 'log_maxsize', + 'cron_completelog', + 'cron_use_sendmail', + 'cron_smtp_port', + ]; + foreach ($int_array as $i) { + if (is_array($i)) { + foreach ($i as $key => $val) { + $int_array[$key] = intval($val); + } + } else { + $config[$i] = isset($config[$i]) ? intval($config[$i]) : 0; + } + } + if (0 == $config['dbport']) { + $config['dbport'] = 3306; + } + + $config['cron_sendmail'] = isset($config['cron_sendmail']) ? $config['cron_sendmail'] : ''; + $config['cron_printout'] = isset($config['cron_printout']) ? $config['cron_printout'] : ''; + $config['send_mail'] = isset($config['send_mail']) ? $config['send_mail'] : ''; + $config['send_mail_dump'] = isset($config['send_mail_dump']) ? $config['send_mail_dump'] : ''; + $config['email_recipient'] = isset($config['email_recipient']) ? $config['email_recipient'] : ''; + $config['email_recipient_cc'] = isset($config['email_recipient_cc']) ? $config['email_recipient_cc'] : ''; + $config['email_sender'] = isset($config['email_sender']) ? $config['email_sender'] : ''; + $config['cron_smtp'] = isset($config['cron_smtp']) ? $config['cron_smtp'] : ''; + $config['ftp_server'] = isset($config['ftp_server']) ? $config['ftp_server'] : ''; + $config['ftp_port'] = isset($config['ftp_port']) ? $config['ftp_port'] : ''; + $config['ftp_mode'] = isset($config['ftp_mode']) ? $config['ftp_mode'] : ''; + $config['ftp_user'] = isset($config['ftp_user']) ? $config['ftp_user'] : ''; + $config['ftp_pass'] = isset($config['ftp_pass']) ? $config['ftp_pass'] : ''; + $config['ftp_dir'] = isset($config['ftp_dir']) ? $config['ftp_dir'] : ''; + $config['ftp_timeout'] = isset($config['ftp_timeout']) ? $config['ftp_timeout'] : ''; + $config['ftp_useSSL'] = isset($config['ftp_useSSL']) ? $config['ftp_useSSL'] : ''; + $config['ftp_transfer'] = isset($config['ftp_transfer']) ? $config['ftp_transfer'] : ''; + $config['sftp_server'] = isset($config['sftp_server']) ? $config['sftp_server'] : ''; + $config['sftp_port'] = isset($config['sftp_port']) ? $config['sftp_port'] : ''; + $config['sftp_user'] = isset($config['sftp_user']) ? $config['sftp_user'] : ''; + $config['sftp_pass'] = isset($config['sftp_pass']) ? $config['sftp_pass'] : ''; + $config['sftp_dir'] = isset($config['sftp_dir']) ? $config['sftp_dir'] : ''; + $config['sftp_path_to_private_key'] = isset($config['sftp_path_to_private_key']) ? $config['sftp_path_to_private_key'] : null; + $config['sftp_secret_passphrase_for_private_key'] = isset($config['sftp_secret_passphrase_for_private_key']) ? $config['sftp_secret_passphrase_for_private_key'] : null; + $config['sftp_fingerprint'] = isset($config['sftp_fingerprint']) ? $config['sftp_fingerprint'] : null; + $config['sftp_timeout'] = isset($config['sftp_timeout']) ? $config['sftp_timeout'] : ''; + $config['sftp_transfer'] = isset($config['sftp_transfer']) ? $config['sftp_transfer'] : ''; + $config['cron_comment'] = isset($config['cron_comment']) ? $config['cron_comment'] : ''; + + $cronscript = " restore it with the actual values + if (!file_exists($config['paths']['config'].'myoosdumper.conf.php')) { + $sfile = $config['paths']['config'].'myoosdumper.conf.php'; + if ($fp = fopen($sfile, 'wb')) { + if (!fwrite($fp, $cronscript)) { + $ret = false; + } + if (!fclose($fp)) { + $ret = false; + } + @chmod("$sfile", 0777); + } else { + $ret = false; + } + } + return $ret; +} + +function LogFileInfo($logcompression) +{ + global $config; + + $l = []; + $sum = $s + = $l['log_size'] = $l['perllog_size'] = $l['perllogcomplete_size'] = $l['errorlog_size'] = $l['log_totalsize'] = 0; + + if ((isset($config['logcompression']) && 1 == $config['logcompression'])) { + $l['log'] = $config['files']['log'].'.gz'; + $l['perllog'] = $config['files']['perllog'].'.gz'; + $l['perllogcomplete'] = $config['files']['perllogcomplete'].'.gz'; + $l['errorlog'] = $config['paths']['log'].'error.log.gz'; + } else { + $l['log'] = $config['files']['log']; + $l['perllog'] = $config['files']['perllog']; + $l['perllogcomplete'] = $config['files']['perllogcomplete']; + $l['errorlog'] = $config['paths']['log'].'error.log'; + } + $l['log_size'] += @filesize($l['log']); + $sum += $l['log_size']; + $l['perllog_size'] += @filesize($l['perllog']); + $sum += $l['perllog_size']; + $l['perllogcomplete_size'] += @filesize($l['perllogcomplete']); + $sum += $l['perllogcomplete_size']; + $l['errorlog_size'] += @filesize($l['errorlog']); + $sum += $l['errorlog_size']; + $l['log_totalsize'] += $sum; + + return $l; +} + +function DeleteLog() +{ + global $config; + //Datei öffnen und schreiben + $log = date('d.m.Y H:i:s')." Log created.\n"; + if (file_exists($config['files']['log'].'.gz')) { + @unlink($config['files']['log'].'.gz'); + } + if (file_exists($config['files']['log'].'.gz')) { + @unlink($config['files']['log']); + } + if ((isset($config['logcompression']) && 1 == $config['logcompression'])) { + $fp = @gzopen($config['files']['log'].'.gz', 'wb'); + @gzwrite($fp, $log); + @gzclose($fp); + @chmod($config['files']['log'].'.gz', 0777); + } else { + $fp = @fopen($config['files']['log'], 'wb'); + @fwrite($fp, $log); + @fclose($fp); + @chmod($config['files']['log'], 0777); + } +} + +function CreateDirsFTP() +{ + global $config, $lang, $install_ftp_server, $install_ftp_port, $install_ftp_user_name, $install_ftp_user_pass, $install_ftp_path; + // Herstellen der Basis-Verbindung + echo '
'.$lang['L_CONNECT_TO'].' `'.$install_ftp_server.'` Port '.$install_ftp_port.' ...
'; + $conn_id = ftp_connect($install_ftp_server); + // Einloggen mit Benutzername und Kennwort + $login_result = ftp_login($conn_id, $install_ftp_user_name, $install_ftp_user_pass); + // Verbindung überprüfen + if ((!$conn_id) || (!$login_result)) { + echo $lang['L_FTP_NOTCONNECTED']; + echo $lang['L_CONNWITH']." $install_ftp_server ".$lang['L_ASUSER']." $install_ftp_user_name ".$lang['L_NOTPOSSIBLE']; + return 0; + } else { + if (1 == $config['ftp_mode']) { + ftp_pasv($conn_id, true); + } + //Wechsel in betroffenes Verzeichnis + echo $lang['L_CHANGEDIR'].' `'.$install_ftp_path.'` ...
'; + ftp_chdir($conn_id, $install_ftp_path); + // Erstellen der Verzeichnisse + echo $lang['L_DIRCR1'].' ...
'; + ftp_mkdir($conn_id, 'work'); + ftp_site($conn_id, 'CHMOD 0777 work'); + echo $lang['L_CHANGEDIR'].' `work` ...
'; + ftp_chdir($conn_id, 'work'); + echo $lang['L_INDIR'].' `'.ftp_pwd($conn_id).'`
'; + echo $lang['L_DIRCR5'].' ...
'; + ftp_mkdir($conn_id, 'config'); + ftp_site($conn_id, 'CHMOD 0777 config'); + echo $lang['L_DIRCR2'].' ...
'; + ftp_mkdir($conn_id, 'backup'); + ftp_site($conn_id, 'CHMOD 0777 backup'); + echo $lang['L_DIRCR4'].' ...
'; + ftp_mkdir($conn_id, 'log'); + ftp_site($conn_id, 'CHMOD 0777 log'); + + // Schließen des FTP-Streams + ftp_quit($conn_id); + return 1; + } +} + +function ftp_mkdirs($config, $dirname) +{ + $path = ''; + $dir = preg_split('/\//', $dirname); + for ($i = 0; $i < count($dir) - 1; ++$i) { + $path .= $dir[$i].'/'; + @ftp_mkdir($config['dbconnection'], $path); + } + if (@ftp_mkdir($config['dbconnection'], $dirname)) { + return 1; + } +} + +function IsWritable($dir) +{ + $testfile = $dir.'/.writetest'; + if ($writable = @fopen($testfile, 'w')) { + @fclose($writable); + @unlink($testfile); + } + + return $writable; +} + +function IsAccessProtected() +{ + $rc = false; + $url = sprintf('%s://%s%s', $_SERVER['REQUEST_SCHEME'], $_SERVER['HTTP_HOST'], dirname($_SERVER['PHP_SELF'])); + $headers = @get_headers($url); + if (is_array($headers) && count($headers) > 0) { + $rc = (preg_match('/\s+(?:401|403)\s+/', $headers[0])) ? 1 : 0; + } + return $rc; +} + +function SearchDatabases($printout, $db = '') +{ + global $databases, $config, $lang; + + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + $db_list = []; + if ($db > '') { + $db_list[] = $db; // DB wurde manuell angegeben + } + // Datenbanken automatisch erkennen + $show_dbs = mysqli_query($config['dbconnection'], 'SHOW DATABASES'); + if (false === !$show_dbs) { + while ($row = mysqli_fetch_row($show_dbs)) { + if (trim($row[0]) > '') { + $db_list[] = $row[0]; + } + } + } + $db_list = array_unique($db_list); + sort($db_list); + if (sizeof($db_list) > 0) { + $databases['db_selected_index'] = 0; + for ($i = 0; $i < sizeof($db_list); ++$i) { + // Test-Select um zu sehen, ob Berechtigungen existieren + if (false === !@mysqli_query($config['dbconnection'], 'SHOW TABLES FROM `'.$db_list[$i].'`')) { + $databases['Name'][$i] = $db_list[$i]; + $databases['praefix'][$i] = ''; + $databases['command_before_dump'][$i] = ''; + $databases['command_after_dump'][$i] = ''; + if (1 == $printout) { + echo $lang['L_FOUND_DB'].' `'.$db_list[$i].'`
'; + } + } else { + if (1 == $printout) { + echo ''.sprintf($lang['L_DB_MANUAL_ERROR'], $db_list[$i]).'
'; + } + } + } + } + if (isset($databases['Name'][0])) { + $databases['db_actual'] = $databases['Name'][0]; + } +} + +// removes tags from inputs recursivly +function my_strip_tags($value) +{ + global $dont_strip; + if (is_array($value)) { + foreach ($value as $key => $val) { + if (!in_array($key, $dont_strip)) { + $ret[$key] = my_strip_tags($val); + } else { + $ret[$key] = $val; + } + } + } else { + $ret = trim(strip_tags($value)); + } + return $ret; +} + +/** + * Add a slashes only before '. + * + * Used for escaping strings in JS-alerts and config-files + * + * @param $string + * + * @return string + */ +function my_addslashes($string) +{ + if (is_string($string)) { + $string = str_replace("'", "\'", $string); + } + + return $string; +} + +/** + * Replaces quotes for outputting value in HTML-attributes. + * + * Replaces quotes for outputing value in HTML-attributes without breaking HTML + * + * @param string $value value to output + * + * @return string + */ +function my_quotes($value) +{ + return str_replace('"', '"', $value); +} + +// prepares a string for executing it as query +function db_escape($string) +{ + global $config; + if (function_exists('mysqli_real_escape_string')) { + $string = mysqli_real_escape_string($config['dbconnection'], $string); + } elseif (function_exists('mysqli_escape_string')) { + $string = mysqli_escape_string($config['dbconnection'], $string); + } else { + $string = addslashes($string); + } + + return $string; +} diff --git a/msd/inc/functions_dump.php b/msd/inc/functions_dump.php new file mode 100644 index 0000000..9347b45 --- /dev/null +++ b/msd/inc/functions_dump.php @@ -0,0 +1,568 @@ +'.$lang['L_STARTDUMP'].' `'.$databases['Name'][$dump['dbindex']].'`'.(('' != $databases['praefix'][$dump['dbindex']]) ? ' ('.$lang['L_WITHPRAEFIX'].' '.$databases['praefix'][$dump['dbindex']].')' : '').'... '; + if (1 == $dump['part']) { + $dump['table_offset'] = 0; + $dump['countdata'] = 0; + } + // Seitenerstaufruf -> Backupdatei anlegen + $dump['data'] = $statuszeile.$mysql_commentstring.' Dump created: '.$cur_time; + } else { + if (0 != $config['multi_part']) { + WriteLog('Continue Multipart-Dump with File '.($dump['part'] - $dump['part_offset']).' (last file was '.$last_groesse.' Bytes)'); + $dump['data'] = $statuszeile.$mysql_commentstring.' This is part '.($dump['part'] - $dump['part_offset']).' of the backup.'.$nl.$nl.$dump['data']; + } + } + WriteToDumpFile(); + ++$dump['part']; +} + +function GetStatusLine($kind = 'php') +{ + /*AUFBAU der Statuszeile: + -- Status:tabellenzahl:datensätze:Multipart:Datenbankname:script:scriptversion:Kommentar:MySQLVersion:Backupflags:SQLBefore:SQLAfter:Charset:CharsetEXTINFO + Aufbau Backupflags (1 Zeichen pro Flag, 0 oder 1, 2=unbekannt) + (complete inserts)(extended inserts)(ignore inserts)(delayed inserts)(downgrade)(lock tables)(optimize tables) + */ + + global $databases, $config, $lang, $dump, $mysql_commentstring; + + $t_array = explode('|', $databases['db_actual_tableselected']); + $t = 0; + $r = 0; + $t_zeile = "$mysql_commentstring\n$mysql_commentstring TABLE-INFO\r\n"; + mod_mysqli_connect(); + $res = mysqli_query($config['dbconnection'], 'SHOW TABLE STATUS FROM `'.$databases['Name'][$dump['dbindex']].'`'); + $numrows = intval(@mysqli_num_rows($res)); + for ($i = 0; $i < $numrows; ++$i) { + $erg = mysqli_fetch_array($res); + // Get nr of records -> need to do it this way because of incorrect returns when using InnoDBs + $sql_2 = 'SELECT count(*) as `count_records` FROM `'.$databases['Name'][$dump['dbindex']].'`.`'.$erg['Name'].'`'; + $res2 = mysqli_query($config['dbconnection'], $sql_2); + if (false === $res2) { + // error reading table definition + $read_create_error = sprintf($lang['L_FATAL_ERROR_DUMP'], $databases['Name'][$dump['dbindex']], $erg['Name']).': '.mysqli_error($config['dbconnection']); + Errorlog('DUMP', $databases['Name'][$dump['dbindex']], '', $read_create_error, 0); + WriteLog($read_create_error); + if ($config['stop_with_error'] > 0) { + exit($read_create_error); + } + ++$dump['errors']; + //$i++; // skip corrupted table + } else { + $row2 = mysqli_fetch_array($res2); + $erg['Rows'] = $row2['count_records']; + + if (('' == $databases['db_actual_tableselected'] || ('' != $databases['db_actual_tableselected'] && (in_array($erg[0], $t_array)))) && (substr($erg[0], 0, strlen($databases['praefix'][$dump['dbindex']])) == $databases['praefix'][$dump['dbindex']])) { + ++$t; + $r += $erg['Rows']; + if (isset($erg['Type'])) { + $erg['Engine'] = $erg['Type']; + } + $t_zeile .= "$mysql_commentstring TABLE|".$erg['Name'].'|'.$erg['Rows'].'|'.($erg['Data_length'] + $erg['Index_length']).'|'.$erg['Update_time'].'|'.$erg['Engine']."\n"; + } + } + } + //$dump['totalrecords'] = $r; + $flags = 1; + + $mp = (isset($config['multi_part']) && (1 == $config['multi_part'])) ? $mp = 'MP_'.($dump['part'] - $dump['part_offset']) : 'MP_0'; + $statusline = "$mysql_commentstring Status:$t:$r:$mp:".$databases['Name'][$dump['dbindex']].":$kind:".MOD_VERSION.':'.$dump['kommentar'].':'; + $statusline .= MOD_MYSQL_VERSION.":$flags:::".$dump['dump_encoding'].":EXTINFO\n".$t_zeile."$mysql_commentstring"." EOF TABLE-INFO\n$mysql_commentstring"; + return $statusline; +} + +// Liest die Eigenschaften der Tabelle aus der DB und baut die CREATE-Anweisung zusammen +function get_def($db, $table, $withdata = 1) +{ + global $config, $nl, $mysql_commentstring, $dump; + + $def = "\n\n$mysql_commentstring\n$mysql_commentstring Create Table `$table`\n$mysql_commentstring\n\n"; + if ('VIEW' == $dump['table_types'][getDBIndex($db, $table)]) { + $def .= "DROP VIEW IF EXISTS `$table`;\n"; + $withdata = 0; + } else { + $def .= "DROP TABLE IF EXISTS `$table`;\n"; + } + mysqli_select_db($config['dbconnection'], $db); + $result = mysqli_query($config['dbconnection'], 'SHOW CREATE TABLE `'.$table.'`'); + $row = mysqli_fetch_row($result); + if (false === $row) { + return false; + } + $def .= $row[1].';'."\n\n"; + if (1 == $withdata) { + $def .= "$mysql_commentstring\n$mysql_commentstring Data for Table `$table`\n$mysql_commentstring\n\n"; + $def .= "/*!40000 ALTER TABLE `$table` DISABLE KEYS */;".$nl; + } + return $def; +} + +// Liest die Daten aus der DB aus und baut die INSERT-Anweisung zusammen +function get_content($db, $table) +{ + global $config, $nl, $dump, $buffer; + + $content = ''; + $complete = Fieldlist($db, $table).' '; + + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + + $table_ready = 0; + $query = 'SELECT * FROM `'.$table.'` LIMIT '.$dump['zeilen_offset'].','.($dump['restzeilen'] + 1); + mysqli_select_db($config['dbconnection'], $db); + $result = mysqli_query($config['dbconnection'], $query); + $ergebnisse = mysqli_num_rows($result); + if (false !== $ergebnisse) { + // $num_felder=mysqli_field_count($result); + $num_felder = mysqli_field_count($config['dbconnection']); + + $first = 1; + + if ($ergebnisse > $dump['restzeilen']) { + $dump['zeilen_offset'] += $dump['restzeilen']; + --$ergebnisse; + $dump['restzeilen'] = 0; + } else { + ++$dump['table_offset']; + $dump['zeilen_offset'] = 0; + $dump['restzeilen'] = $dump['restzeilen'] - $ergebnisse; + $table_ready = 1; + } + $ax = 0; + for ($x = 0; $x < $ergebnisse; ++$x) { + $row = mysqli_fetch_row($result); + ++$ax; + + $insert = 'INSERT INTO `'.$table.'` '.$complete.'VALUES ('; + + for ($j = 0; $j < $num_felder; ++$j) { + if (!isset($row[$j])) { + $insert .= 'NULL,'; + } else { + $fieldinfo = mysqli_fetch_field_direct($result, $j); + if (($fieldinfo->flags & 128) == true && isset($config['use_binary_container']) && 1 == $config['use_binary_container']) { + if ('' != $row[$j]) { + $insert .= '_binary 0x'.bin2hex($row[$j]).','; + } else { + $insert .= '_binary \'\','; + } + } elseif ('' != $row[$j]) { + $insert .= '\''.mysqli_real_escape_string($config['dbconnection'], $row[$j]).'\','; + } else { + $insert .= '\'\','; + } + } + } + $insert = substr($insert, 0, -1).');'.$nl; + $dump['data'] .= $insert; + ++$dump['countdata']; + $config['memory_limit'] = isset($config['memory_limit']) ? $config['memory_limit'] : 0; + if (strlen($dump['data']) > $config['memory_limit'] || (1 == $config['multi_part'] && strlen($dump['data']) + $buffer > $config['multipart_groesse'])) { + WriteToDumpFile(); + } + } + if (1 == $table_ready && 'VIEW' != $dump['table_types'][getDBIndex($db, $table)]) { + $dump['data'] .= "/*!40000 ALTER TABLE `$table` ENABLE KEYS */;\n"; + } + } else { + // table corrupt -> skip it + ++$dump['table_offset']; + $dump['zeilen_offset'] = 0; + $dump['restzeilen'] = $dump['restzeilen'] - $ergebnisse; + $dump['data'] .= "/*!40000 ALTER TABLE `$table` ENABLE KEYS */;\n"; + if (strlen($dump['data']) > $config['memory_limit'] || (1 == $config['multi_part'] && strlen($dump['data']) + $buffer > $config['multipart_groesse'])) { + WriteToDumpFile(); + } + } + @mysqli_free_result($result); +} + +function WriteToDumpFile() +{ + global $config, $dump, $buffer; + $dump['filesize'] = 0; + + $df = $config['paths']['backup'].$dump['backupdatei']; + + if (isset($config['compression']) && (1 == $config['compression'])) { + if ('' != $dump['data']) { + $fp = gzopen($df, 'ab'); + gzwrite($fp, $dump['data']); + gzclose($fp); + } + } else { + if ('' != $dump['data']) { + $fp = fopen($df, 'ab'); + fwrite($fp, $dump['data']); + fclose($fp); + } + } + $dump['data'] = ''; + if (!isset($dump['fileoperations'])) { + $dump['fileoperations'] = 0; + } + ++$dump['fileoperations']; + + if (isset($config['multi_part']) && (1 == $config['multi_part'])) { + clearstatcache(); + } + $dump['filesize'] = filesize($df); + if ((isset($config['multi_part']) && 1 == $config['multi_part']) && ($dump['filesize'] + $buffer > $config['multipart_groesse'])) { + @chmod($df, 0777); + new_file($dump['filesize']); // Wenn maximale Dateigroesse erreicht -> neues File starten + } +} + +function ExecuteCommand($when) +{ + global $config, $databases, $dump, $out, $lang; + $lf = '
'; + if (!isset($dump['dbindex'])) { + return; + } + if ('b' == $when) { // before dump + $cd = $databases['command_before_dump'][$dump['dbindex']]; + //WriteLog('DbIndex: '.$dump['dbindex'].' Before: '.$databases['command_before_dump'][$dump['dbindex']]); + } else { + $cd = $databases['command_after_dump'][$dump['dbindex']]; + //WriteLog('DbIndex: '.$dump['dbindex'].' After: '.$databases['command_after_dump'][$dump['dbindex']]); + } + + if ('' != $cd) { + //jetzt ausführen + if ('system:' != substr(strtolower($cd), 0, 7)) { + $cad = []; + @mysqli_select_db($config['dbconnection'], $databases['Name'][$dump['dbindex']]); + if (strpos($cd, ';')) { + $cad = explode(';', $cd); + } else { + $cad[0] = $cd; + } + + for ($i = 0; $i < sizeof($cad); ++$i) { + if (trim($cad[$i]) > '') { + $result = mysqli_query($config['dbconnection'], $cad[$i]); + + if (false === $result) { + WriteLog("Error executing Query '$cad[$i]'! MySQL returns: ".trim(mysqli_error($config['dbconnection']))); + ErrorLog("Error executing Query '$cad[$i]'!", $databases['Name'][$dump['dbindex']], $cad[$i], mysqli_error($config['dbconnection']), 0); + ++$dump['errors']; + $out .= 'Error executing Query '.$cad[$i].''.$lf; + } else { + WriteLog("Successfully executed Query: '$cad[$i]'"); + $out .= 'Successfully executed Query: \''.$cad[$i].'\''.$lf; + } + } + } + } elseif ('system:' == substr(strtolower($cd), 0, 7)) { + $command = substr($cd, 7); + $result = @system($command, $returnval); + if (!$result) { + WriteLog("Error while executing System Command '$command'"); + ++$dump['errors']; + $out .= $lf.'ERROR executing System Command \''.$command.'\'
'; + } else { + WriteLog("Successfully executed System Command '$command'. [$returnval]"); + $out .= $lf.'Successfully executed System Command \''.$command.'.
'; + } + } + } +} + +function DoEmail() +{ + global $config, $dump, $databases, $email, $lang, $out, $REMOTE_ADDR; + + $header = ''; + if (1 == $config['cron_use_sendmail']) { + //sendmail + if (ini_get('sendmail_path') != $config['cron_sendmail']) { + @ini_set('SMTP', $config['cron_sendmail']); + } + if (ini_get('sendmail_from') != $config['email_sender']) { + @ini_set('SMTP', $config['email_sender']); + } + } else { + //SMTP + } + if (ini_get('SMTP') != $config['cron_smtp']) { + @ini_set('SMTP', $config['cron_smtp']); + } + if (25 != ini_get('smtp_port')) { + @ini_set('smtp_port', 25); + } + + if (0 == $config['multi_part']) { + $file = $dump['backupdatei']; + $file_name = (strpos('/', $file)) ? substr($file, strrpos('/', $file)) : $file; + $file_type = filetype($config['paths']['backup'].$file); + $file_size = filesize($config['paths']['backup'].$file); + if (($config['email_maxsize'] > 0 && $file_size > $config['email_maxsize']) || 0 == $config['send_mail_dump']) { + //anhang zu gross + $subject = "Backup '".$databases['Name'][$dump['dbindex']]."' - ".date("d\.m\.Y H:i", time()); + $header .= 'FROM:'.$config['email_sender']."\n"; + if (isset($config['email_recipient_cc']) && trim($config['email_recipient_cc']) > '') { + $header .= 'Cc: '.$config['email_recipient_cc']."\r\n"; + } + $header .= "MIME-version: 1.0\n"; + $header .= 'X-Mailer: PHP/'.phpversion()."\n"; + $header .= "X-Sender-IP: $REMOTE_ADDR\n"; + $header .= "Content-Type: text/html; charset=utf-8\n"; + if (0 != $config['send_mail_dump']) { + $msg_body = sprintf(addslashes($lang['L_EMAILBODY_TOOBIG']), byte_output($config['email_maxsize']), $databases['Name'][$dump['dbindex']], "$file (".byte_output(filesize($config['paths']['backup'].$file)).')
'); + } else { + $msg_body = sprintf(addslashes($lang['L_EMAILBODY_NOATTACH']), $databases['Name'][$dump['dbindex']], "$file (".byte_output(filesize($config['paths']['backup'].$file)).')'); + } + include_once './inc/functions.php'; + $msg_body .= ''.$file.''; + $email_log = "Email sent to '".$config['email_recipient']."'"; + $email_out = $lang['L_EMAIL_WAS_SEND'].'`'.$config['email_recipient'].'`
'; + } else { + //alles ok, anhang generieren + $msg_body = sprintf(addslashes($lang['L_EMAILBODY_ATTACH']), $databases['Name'][$dump['dbindex']], "$file (".byte_output(filesize($config['paths']['backup'].$file)).')'); + $subject = "Backup '".$databases['Name'][$dump['dbindex']]."' - ".date("d\.m\.Y", time()); + $fp = fopen($config['paths']['backup'].$file, 'r'); + $contents = fread($fp, $file_size); + $encoded_file = chunk_split(base64_encode($contents)); + fclose($fp); + $header .= 'FROM:'.$config['email_sender']."\n"; + if (isset($config['email_recipient_cc']) && trim($config['email_recipient_cc']) > '') { + $header .= 'Cc: '.$config['email_recipient_cc']."\r\n"; + } + $header .= "MIME-version: 1.0\n"; + $header .= 'Content-type: multipart/mixed; '; + $header .= "boundary=\"Message-Boundary\"\n"; + $header .= "Content-transfer-encoding: 7BIT\n"; + $header .= "X-attachments: $file_name"; + $body_top = "--Message-Boundary\n"; + $body_top .= "Content-type: text/html; charset=utf-8\n"; + $body_top .= "Content-transfer-encoding: 7BIT\n"; + $body_top .= "Content-description: Mail message body\n\n"; + $msg_body = $body_top.$msg_body; + $msg_body .= "\n\n--Message-Boundary\n"; + $msg_body .= "Content-type: $file_type; name=\"$file\"\n"; + $msg_body .= "Content-Transfer-Encoding: BASE64\n"; + $msg_body .= "Content-disposition: attachment; filename=\"$file\"\n\n"; + $msg_body .= "$encoded_file\n"; + $msg_body .= "--Message-Boundary--\n"; + $email_log = "Email was sent to '".$config['email_recipient']."' with '".$dump['backupdatei']."'."; + $email_out = $lang['L_EMAIL_WAS_SEND'].'`'.$config['email_recipient'].'`'.$lang['L_WITH'].'`'.$dump['backupdatei'].'`.
'; + } + } else { + //Multipart + $mp_sub = "Backup '".$databases['Name'][$dump['dbindex']]."' - ".date("d\.m\.Y", time()); + $subject = $mp_sub; + $header .= 'FROM:'.$config['email_sender']."\n"; + if (isset($config['email_recipient_cc']) && trim($config['email_recipient_cc']) > '') { + $header .= 'Cc: '.$config['email_recipient_cc']."\r\n"; + } + $header .= "MIME-version: 1.0\n"; + $header .= 'X-Mailer: PHP/'.phpversion()."\n"; + $header .= "X-Sender-IP: $REMOTE_ADDR\n"; + $header .= 'Content-Type: text/html; charset=utf-8'; + $dateistamm = substr($dump['backupdatei'], 0, strrpos($dump['backupdatei'], 'part_')).'part_'; + $dateiendung = (1 == $config['compression']) ? '.sql.gz' : '.sql'; + $mpdatei = []; + $mpfiles = ''; + for ($i = 1; $i < ($dump['part'] - $dump['part_offset']); ++$i) { + $mpdatei[$i - 1] = $dateistamm.$i.$dateiendung; + $sz = byte_output(@filesize($config['paths']['backup'].$mpdatei[$i - 1])); + $mpfiles .= $mpdatei[$i - 1].' ('.$sz.')
'; + } + $msg_body = (1 == $config['send_mail_dump']) ? sprintf(addslashes($lang['L_EMAILBODY_MP_ATTACH']), $databases['Name'][$dump['dbindex']], $mpfiles) : sprintf(addslashes($lang['L_EMAILBODY_MP_NOATTACH']), $databases['Name'][$dump['dbindex']], $mpfiles); + $email_log = "Email was sent to '".$config['email_recipient']."'"; + $email_out = $lang['L_EMAIL_WAS_SEND'].'`'.$config['email_recipient'].'`
'; + } + if (@mail($config['email_recipient'], stripslashes($subject), $msg_body, $header)) { + $out .= ''.$email_out.''; + WriteLog("$email_log"); + } else { + $out .= ''.$lang['L_MAILERROR'].'
'; + WriteLog("Email to '".$config['email_recipient']."' failed !"); + ErrorLog('Email ', $databases['Name'][$dump['dbindex']], 'Subject: '.stripslashes($subject), $lang['L_MAILERROR']); + ++$dump['errors']; + } + + if (isset($mpdatei) && 1 == $config['send_mail_dump']) { // && ($config['email_maxsize'] ==0 || ($config['email_maxsize']>0 && $config['multipartgroesse2']<= $config['email_maxsize']))) { + for ($i = 0; $i < count($mpdatei); ++$i) { + $file_name = $mpdatei[$i]; + $file_type = filetype($config['paths']['backup'].$mpdatei[$i]); + $file_size = filesize($config['paths']['backup'].$mpdatei[$i]); + $fp = fopen($config['paths']['backup'].$mpdatei[$i], 'r'); + $contents = fread($fp, $file_size); + $encoded_file = chunk_split(base64_encode($contents)); + fclose($fp); + $subject = $mp_sub.' [Part '.($i + 1).' / '.count($mpdatei).']'; + $header = 'FROM:'.$config['email_sender']."\n"; + if (isset($config['email_recipient_cc']) && trim($config['email_recipient_cc']) > '') { + $header .= 'Cc: '.$config['email_recipient_cc']."\r\n"; + } + $header .= "MIME-version: 1.0\n"; + $header .= 'Content-type: multipart/mixed; '; + $header .= "boundary=\"Message-Boundary\"\n"; + $header .= "Content-transfer-encoding: 7BIT\n"; + $header .= "X-attachments: $file_name"; + $body_top = "--Message-Boundary\n"; + $body_top .= "Content-type: text/html; charset=utf-8\n"; + $body_top .= "Content-transfer-encoding: 7BIT\n"; + $body_top .= "Content-description: Mail message body\n\n"; + $msg_body = $body_top.addslashes($lang['L_EMAIL_ONLY_ATTACHMENT'].$lang['L_EMAILBODY_FOOTER']); + $msg_body .= "\n\n--Message-Boundary\n"; + $msg_body .= "Content-type: $file_type; name=\"".$mpdatei[$i]."\"\n"; + $msg_body .= "Content-Transfer-Encoding: BASE64\n"; + $msg_body .= 'Content-disposition: attachment; filename="'.$mpdatei[$i]."\"\n\n"; + $msg_body .= "$encoded_file\n"; + $msg_body .= "--Message-Boundary--\n"; + $email_log = "Email with $mpdatei[$i] was sent to '".$config['email_recipient']."'"; + $email_out = $lang['L_EMAIL_WAS_SEND'].'`'.$config['email_recipient'].'`'.$lang['L_WITH'].'`'.$mpdatei[$i].'`.
'; + + if (@mail($config['email_recipient'], stripslashes($subject), $msg_body, $header)) { + $out .= ''.$email_out.''; + WriteLog("$email_log"); + } else { + $out .= ''.$lang['L_MAILERROR'].'
'; + WriteLog("Email to '".$config['email_recipient']."' failed !"); + ErrorLog('Email ', $databases['Name'][$dump['dbindex']], 'Subject: '.stripslashes($subject), $lang['L_MAILERROR']); + ++$dump['errors']; + } + } + } +} + +function DoFTP($i) +{ + global $config, $dump, $out; + + if (0 == $config['multi_part']) { + SendViaFTP($i, $dump['backupdatei'], 1); + } else { + $dateistamm = substr($dump['backupdatei'], 0, strrpos($dump['backupdatei'], 'part_')).'part_'; + $dateiendung = (1 == $config['compression']) ? '.sql.gz' : '.sql'; + for ($a = 1; $a < ($dump['part'] - $dump['part_offset']); ++$a) { + $mpdatei = $dateistamm.$a.$dateiendung; + SendViaFTP($i, $mpdatei, $a); + } + } +} + +function SendViaFTP($i, $source_file, $conn_msg = 1) +{ + global $config, $out, $lang; + flush(); + if (1 == $conn_msg) { + $out .= ''.$lang['L_FILESENDFTP'].'('.$config['ftp_server'][$i].' - '.$config['ftp_user'][$i].')
'; + } + // Herstellen der Basis-Verbindung + if (0 == $config['ftp_useSSL'][$i]) { + $conn_id = @ftp_connect($config['ftp_server'][$i], $config['ftp_port'][$i], $config['ftp_timeout'][$i]); + } else { + $conn_id = @ftp_ssl_connect($config['ftp_server'][$i], $config['ftp_port'][$i], $config['ftp_timeout'][$i]); + } + // Einloggen mit Benutzername und Kennwort + $login_result = @ftp_login($conn_id, $config['ftp_user'][$i], $config['ftp_pass'][$i]); + if (1 == $config['ftp_mode'][$i]) { + ftp_pasv($conn_id, true); + } + + // Verbindung überprüfen + if ((!$conn_id) || (!$login_result)) { + $out .= ''.$lang['L_FTPCONNERROR'].$config['ftp_server'][$i].$lang['L_FTPCONNERROR1'].$config['ftp_user'][$i].$lang['L_FTPCONNERROR2'].'
'; + } else { + if (1 == $conn_msg) { + $out .= ''.$lang['L_FTPCONNECTED1'].$config['ftp_server'][$i].$lang['L_FTPCONNERROR1'].$config['ftp_user'][$i].'
'; + } + } + + // Upload der Datei + $dest = $config['ftp_dir'][$i].$source_file; + $source = $config['paths']['backup'].$source_file; + $upload = @ftp_put($conn_id, $dest, $source, FTP_BINARY); + + // Upload-Status überprüfen + if (!$upload) { + $out .= ''.$lang['L_FTPCONNERROR3']."
($source -> $dest)

"; + } else { + $out .= ''.$lang['L_FILE'].' '.$source_file.''.$lang['L_FTPCONNECTED2'].$config['ftp_server'][$i].$lang['L_FTPCONNECTED3'].'
'; + WriteLog("'$source_file' sent via FTP."); + } + + // Schließen des FTP-Streams + @ftp_quit($conn_id); +} + +function DoSFTP($i) +{ + global $config, $dump, $out; + + if (0 == $config['multi_part']) { + SendViaSFTP($i, $dump['backupdatei'], 1); + } else { + $dateistamm = substr($dump['backupdatei'], 0, strrpos($dump['backupdatei'], 'part_')).'part_'; + $dateiendung = (1 == $config['compression']) ? '.sql.gz' : '.sql'; + for ($a = 1; $a < ($dump['part'] - $dump['part_offset']); ++$a) { + $mpdatei = $dateistamm.$a.$dateiendung; + SendViaSFTP($i, $mpdatei, $a); + } + } +} diff --git a/msd/inc/functions_files.php b/msd/inc/functions_files.php new file mode 100644 index 0000000..0fb8a5e --- /dev/null +++ b/msd/inc/functions_files.php @@ -0,0 +1,486 @@ +'; + $r .= ''; + + $dh = opendir($fpath); + while (false !== ($filename = readdir($dh))) { + if ('.' != $filename && '..' != $filename && !is_dir($fpath.$filename)) { + $r .= ''.$end."\n"; + } elseif ('radio' == $k) { + $r .= $start.''; + $r .= ''.$end."\n"; + } + ++$i; + } + return $r; +} + +// detect language subdirs and add them to the global definition of $lang +function GetLanguageArray() +{ + global $config, $lang; + $dh = opendir($config['paths']['root'].'language/'); + unset($lang['languages']); + $lang['languages'] = []; + while (false !== ($filename = readdir($dh))) { + if ('.' != $filename && '.svn' != $filename && '..' != $filename && 'flags' != $filename && is_dir($config['paths']['root'].'language/'.$filename)) { + $lang['languages'][] = $filename; + } + } +} + +function headline($title, $mainframe = 1) +{ + global $config, $lang; + $s = ''; + if ((isset($config['interface_server_caption'])) && (1 == $config['interface_server_caption'])) { + if ($config['interface_server_caption_position'] == $mainframe) { + $s .= '
'.$lang['L_SERVER'].': '.$_SERVER['SERVER_NAME'].'
'; + } + } + if (1 == $mainframe) { + $s .= '
'.$title.'
'; + $s .= '
'; + } + return $s; +} + +function PicCache($rpath = './') +{ + global $BrowserIcon, $config; + + $t = '
'; + + $dh = opendir($config['files']['iconpath']); + while (false !== ($filename = readdir($dh))) { + if ('.' != $filename && '..' != $filename && !is_dir($config['files']['iconpath'].$filename)) { + $t .= ''."\n"; + } + } + $t .= '
'; + return $t; +} + +function MODHeader($kind = 0) +{ + global $config; + header('Pragma: no-cache'); + header('Cache-Control: no-cache, must-revalidate'); // HTTP/1.1 + header('Expires: -1'); // Datum in der Vergangenheit + header('Content-Type: text/html; charset=UTF-8'); + + //kind 0=main 1=menu + $r = ''."\n\n\n"; + $r .= ''."\n"; + $r .= ''."\n"; + $r .= ''."\n"; + $r .= ''."\n"; + $r .= ''."\n"; + $r .= ''."\n"; + $r .= 'MyOOS [Dumper]'."\n"; + $r .= ''."\n"; + $r .= ''."\n"; + $r .= "\n'; + return $r; +} + +function MODFooter($rfoot = '', $enddiv = 1) +{ + $f = ''; + if (1 == $enddiv) { + $f .= '
'; + } + + $f .= $rfoot.''; + + return $f; +} + +function save_bracket($str) +{ + // Wenn Klammer zu am Ende steht, diese behalten + $str = trim($str); + if (')' == substr($str, -1)) { + $str = ')'; + } else { + $str = ''; + } + return $str; +} + +function DownGrade($s, $show = true) +{ + $tmp = explode(',', $s); + //echo "
";print_r($tmp);echo "
"; + + for ($i = 0; $i < count($tmp); ++$i) { + $t = strtolower($tmp[$i]); + + if (strpos($t, 'collate ')) { + $tmp2 = explode(' ', $tmp[$i]); + for ($j = 0; $j < count($tmp2); ++$j) { + if ('collate' == strtolower($tmp2[$j])) { + $tmp2[$j] = ''; + $tmp2[$j + 1] = save_bracket($tmp2[$j + 1]); + ++$j; + } + } + $tmp[$i] = implode(' ', $tmp2); + } + + if (strpos($t, 'engine=')) { + $tmp2 = explode(' ', $tmp[$i]); + for ($j = 0; $j < count($tmp2); ++$j) { + if ('ENGINE=' == substr(strtoupper($tmp2[$j]), 0, 7)) { + $tmp2[$j] = 'TYPE='.substr($tmp2[$j], 7, strlen($tmp2[$j]) - 7); + } + if ('CHARSET=' == substr(strtoupper($tmp2[$j]), 0, 8)) { + $tmp2[$j] = ''; + $tmp2[$j - 1] = save_bracket($tmp2[$j - 1]); + } + if ('COLLATE=' == substr(strtoupper($tmp2[$j]), 0, 8)) { + $tmp2[$j] = save_bracket($tmp2[$j]); + $tmp2[$j - 1] = ''; + } + } + $tmp[$i] = implode(' ', $tmp2); + } + + // character Set sprache entfernen + if (strpos($t, 'character set')) { + $tmp2 = explode(' ', $tmp[$i]); + $end = false; + + for ($j = 0; $j < count($tmp2); ++$j) { + if ('character' == strtolower($tmp2[$j])) { + $tmp2[$j] = ''; + $tmp2[$j + 1] = save_bracket($tmp2[$j + 1]); + $tmp2[$j + 2] = save_bracket($tmp2[$j + 2]); + } + } + $tmp[$i] = implode(' ', $tmp2); + } + + if (strpos($t, 'timestamp')) { + $tmp2 = explode(' ', $tmp[$i]); + $end = false; + + for ($j = 0; $j < count($tmp2); ++$j) { + if ($end) { + $tmp2[$j] = ''; + } + if ('timestamp' == strtolower($tmp2[$j])) { + $tmp2[$j] = 'TIMESTAMP(14)'; + $end = true; + } + } + $tmp[$i] = implode(' ', $tmp2); + } + } + $t = implode(',', $tmp); + if (';' != substr(rtrim($t), -1)) { + $t = rtrim($t).';'; + } + return $t; +} + +function MySQLi_Ticks($s) +{ + $klammerstart = $lastklammerstart = $end = 0; + $inner_s_start = strpos($s, '('); + $inner_s_end = strrpos($s, ')'); + $inner_s = substr($s, $inner_s_start + 1, $inner_s_end - (1 + $inner_s_start)); + $pieces = explode(',', $inner_s); + for ($i = 0; $i < count($pieces); ++$i) { + $r = trim($pieces[$i]); + $klammerstart += substr_count($r, '(') - substr_count($r, ')'); + if ($i == count($pieces) - 1) { + ++$klammerstart; + } + if ('KEY ' == substr(strtoupper($r), 0, 4) || 'UNIQUE ' == substr(strtoupper($r), 0, 7) || 'PRIMARY KEY ' == substr(strtoupper($r), 0, 12) || 'FULLTEXT KEY ' == substr(strtoupper($r), 0, 13)) { + //nur ein Key + $end = 1; + } else { + if ('`' != substr($r, 0, 1) && '\'' != substr($r, 0, 1) && 0 == $klammerstart && 0 == $end && 0 == $lastklammerstart) { + $pos = strpos($r, ' '); + $r = '`'.substr($r, 0, $pos).'`'.substr($r, $pos); + } + } + $pieces[$i] = $r; + $lastklammerstart = $klammerstart; + } + $back = substr($s, 0, $inner_s_start + 1).implode(',', $pieces).');'; + return $back; +} + +/** + * Convert all array elements to UTF-8. + * + * @param $array + * + * @return array + */ +function convert_to_utf8($obj) +{ + global $config; + $ret = $obj; + // wenn die Verbindung zur Datenbank nicht auf utf8 steht, dann muessen die Rückgaben in utf8 gewandelt werden, + // da die Webseite utf8-kodiert ist + if (!isset($config['mysql_can_change_encoding'])) { + get_sql_encodings(); + } + + if (false == $config['mysql_can_change_encoding'] && 'utf8' != $config['mysql_standard_character_set']) { + if (is_array($obj)) { + foreach ($obj as $key => $val) { + //echo "
Wandle " . $val . " nach "; + $obj[$key] = utf8_encode($val); + //echo $obj[$key]; + } + } + if (is_string($obj)) { + $obj = utf8_encode($obj); + } + $ret = $obj; + } + return $ret; +} + +/** + * Convert all array elements to Latin1. + * + * @param $array + * + * @return array + */ +function convert_to_latin1($obj) +{ + global $config; + $ret = $obj; + // wenn die Verbindung zur Datenbank nicht auf utf8 steht, dann muessen die Rückgaben in utf8 gewandelt werden, + // da die Webseite utf8-kodiert ist + if (false == $config['mysql_can_change_encoding'] && 'utf8' != $config['mysql_standard_character_set']) { + if (is_array($obj)) { + foreach ($obj as $key => $val) { + $obj[$key] = utf8_decode($val); + } + } + if (is_string($obj)) { + $obj = utf8_decode($obj); + } + $ret = $obj; + } + return $ret; +} + +// returns the index of the selected val in an optionlist +function get_index($arr, $selected) +{ + $ret = false; // return false if not found + foreach ($arr as $key => $val) { + if (strtolower(substr($val, 0, strlen($selected))) == strtolower($selected)) { + $ret = $key; + break; + } + } + return $ret; +} + +/** + * Check if config is readable. + * + * @param $file + * + * @return bool + */ +function read_config($file = false) +{ + global $config, $databases; + $ret = false; + if (!$file) { + $file = $config['config_file']; + } + // protect from including external files + $search = [':', 'http', 'ftp', ' ']; + $replace = ['', '', '', '']; + $file = str_replace($search, $replace, $file); + + if (is_readable($config['paths']['config'].$file.'.php')) { + // to prevent modern server from caching the new configuration we need to evaluate it this way + clearstatcache(); + $f = implode('', file($config['paths']['config'].$file.'.php')); + $f = str_replace('', '', $f); + eval($f); + $config['config_file'] = $file; + $_SESSION['config_file'] = $config['config_file']; + $ret = true; + } + return $ret; +} + +/** + * Get all work configurations from /work/config directory. + * + * @return array + */ +function get_config_filenames() +{ + global $config; + $configs = []; + $dh = opendir($config['paths']['config'].'/'); + while (false !== ($filename = readdir($dh))) { + if ('.php' == substr($filename, -4) && '.conf.php' != substr($filename, -9) && 'dbs_manual.php' != $filename) { + $configs[] = substr($filename, 0, -4); + } + } + asort($configs); + return $configs; +} + +function table_output($text, $val, $small = false, $colspan = 1) +{ + $ret = ''; + $ret .= ' 1) { + $ret .= ' colspan="'.$colspan.'"'; + } + $ret .= '>'.$text; + if (1 == $colspan) { + $ret .= ': '; + } else { + $ret .= ' '; + } + if (1 == $colspan) { + $ret .= ''; + } + if ($small) { + $ret .= ''.$val.''; + } else { + $ret .= ''.$val.''; + } + return $ret; +} + +/** + * Receive all possible MySQL character sets and save standard to $config['mysql_standard_charset']. + */ +function get_sql_encodings() +{ + global $config; + + unset($config['mysql_possible_character_sets']); + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + $erg = false; + $config['mysql_standard_character_set'] = ''; + $config['mysql_possible_character_sets'] = []; + + // MySQL-Version >= 4.1 + $config['mysql_can_change_encoding'] = true; + $sqlt = 'SHOW CHARACTER SET'; + $res = mod_query($sqlt) or exit(SQLError($sqlt, mysqli_error($config['dbconnection']))); + + if ($res) { + while ($row = mysqli_fetch_row($res)) { + $config['mysql_possible_character_sets'][] = $row[0].' - '.$row[1]; + } + sort($config['mysql_possible_character_sets']); + } + + $sqlt = 'SHOW VARIABLES LIKE \'character_set_connection\''; + $res = mod_query($sqlt) or exit(SQLError($sqlt, mysqli_error($config['dbconnection']))); + + if ($res) { + while ($row = mysqli_fetch_row($res)) { + $config['mysql_standard_character_set'] = $row[1]; + } + } +} + +/** + * Un-quotes a quoted string/array. + * + * @param $value + * + * @return string/array + */ +function stripslashes_deep($value) +{ + $value = is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value); + return $value; +} + +/** + * Remove whitespaces before and after an string or array. + * + * @param $value + * + * @return string/array + */ +function trim_deep($value) +{ + $value = is_array($value) ? array_map('trim_deep', $value) : trim($value); + return $value; +} + +/** + * load external source from given URL and save content locally. + * + * loads content from an external URL and saves it locally in $path with the name $local_file + * return false on failure or true on success + * + * @param $url + * @param $file + * @param local_file + * @param $path + * + * @return bool + */ +function fetchFileFromURL($url, $file, $local_file, $local_path = './data/') +{ + $data = fetchFileDataFromURL($url.$file); + if ($data) { + $d = fopen($local_path.$local_file, 'wb'); + $ret = fwrite($d, $data); + fclose($d); + return $ret; + } + return false; +} + +/** + * Loads data from an external source via HTTP-socket. + * + * Loads data from an external source $url given as URL + * and returns the content as a binary string or an empty string on failure + * + * @param $url + * + * @return string file data + */ +function fetchFileDataFromURL($url) +{ + $url_parsed = parse_url($url); + $in = ''; + + $host = $url_parsed['host']; + $port = isset($url_parsed['port']) ? intval($url_parsed['port']) : 80; + if (0 == $port) { + $port = 80; + } + $path = $url_parsed['path']; + if (isset($url_parsed['query']) && '' != $url_parsed['query']) { + $path .= '?'.$url_parsed['query']; + } + + $fp = fsockopen($host, $port, $errno, $errstr, 3); + if ($fp) { + $out = "GET $path HTTP/1.1\r\nHost: $host\r\n"; + $out .= "Connection: close\r\n\r\n"; + fwrite($fp, $out); + $body = false; + while (!feof($fp)) { + $s = fgets($fp, 1024); + if ($body) { + $in .= $s; + } + if ("\r\n" == $s) { + $body = true; + } + } + + fclose($fp); + } + return $in; +} diff --git a/msd/inc/functions_imexport.php b/msd/inc/functions_imexport.php new file mode 100644 index 0000000..506efc1 --- /dev/null +++ b/msd/inc/functions_imexport.php @@ -0,0 +1,418 @@ + $config['memory_limit']) { + CSVOutput($t); + $t = ''; + } + $time_now = time(); + if ($time_start >= $time_now + 30) { + $time_start = $time_now; + header('X-MODPing: Pong'); + } + } + } + } + CSVOutput($t, 1); +} + +function CSVOutput($str, $last = 0) +{ + global $sql, $config; + if (0 == $sql['export']['sendfile']) { + //Display + echo $str; + } else { + if ('' == $sql['export']['header_sent']) { + if (1 == $sql['export']['compressed'] & !function_exists('gzencode')) { + $sql['export']['compressed'] = 0; + } + if ($sql['export']['format'] < 4) { + $file = $sql['export']['db'].((1 == $sql['export']['compressed']) ? '.csv.gz' : '.csv'); + } elseif (4 == $sql['export']['format']) { + $file = $sql['export']['db'].((1 == $sql['export']['compressed']) ? '.xml.gz' : '.xml'); + } elseif (5 == $sql['export']['format']) { + $file = $sql['export']['db'].((1 == $sql['export']['compressed']) ? '.html.gz' : '.html'); + } + $mime = (0 == $sql['export']['compressed']) ? 'x-type/subtype' : 'application/x-gzip'; + + header('Content-Disposition: attachment; filename="'.$file.'"'); + header('Pragma: no-cache'); + header('Content-Type: '.$mime); + header('Expires: '.gmdate('D, d M Y H:i:s').' GMT'); + $sql['export']['header_sent'] = 1; + } + if (1 == $sql['export']['compressed']) { + echo gzencode($str); + } else { + echo $str; + } + } +} + +function DoImport() +{ + global $sql, $lang; + $r = ''; + $zeilen = count($sql['import']['csv']) - $sql['import']['namefirstline']; + $sql['import']['first_zeile'] = explode($sql['import']['trenn'], $sql['import']['csv'][0]); + $importfelder = count($sql['import']['first_zeile']); + + if (0 == $sql['import']['tablecreate']) { + $res = mod_query('show fields FROM '.$sql['import']['table']); + $tabellenfelder = mysqli_num_rows($res); + if ($importfelder != $tabellenfelder) { + $r .= '
'.sprintf($lang['L_CSV_FIELDCOUNT_NOMATCH'], $tabellenfelder, $importfelder); + } else { + $ok = 1; + } + } else { + $ok = ImportCreateTable(); + if (0 == $ok) { + $r .= '
'.sprintf($lang['L_CSV_ERRORCREATETABLE'], $sql['import']['table']); + } + } + if (1 == $ok) { + $insert = ''; + if (1 == $sql['import']['emptydb'] && 0 == $sql['import']['tablecreate']) { + MOD_DoSQL('TRUNCATE '.$sql['import']['table'].';'); + } + $sql['import']['lines_imported'] = 0; + $enc = ('' == $sql['import']['enc']) ? "'" : ''; + $zc = ''; + for ($i = $sql['import']['namefirstline']; $i < $zeilen + $sql['import']['namefirstline']; ++$i) { + //Importieren + $insert = 'INSERT INTO '.$sql['import']['table'].' VALUES('; + if (1 == $sql['import']['createindex']) { + $insert .= "'', "; + } + $zc .= trim(rtrim($sql['import']['csv'][$i])); + //echo "Zeile $i: $zc
"; + if ('' != $zc) { // && substr($zc,-1)== $enc) { + $zeile = explode($sql['import']['trenn'], $zc); + for ($j = 0; $j < $importfelder; ++$j) { + $a = ('' == $zeile[$j] && '' == $enc) ? "''" : $zeile[$j]; + $insert .= $enc.$a.$enc.(($j == $importfelder - 1) ? ");\n" : ','); + } + MOD_DoSQL($insert); + ++$sql['import']['lines_imported']; + $zc = ''; + } + } + $r .= sprintf($lang['L_CSV_FIELDSLINES'], $importfelder, $sql['import']['lines_imported']); + } + + $r .= '
'; + return $r; +} + +function ImportCreateTable() +{ + global $sql, $lang, $db, $config; + $tbl = []; + $sql = "SHOW TABLES FROM $db"; + $tabellen = mod_query($sql); + // while ($row = mysqli_fetch_row($num_tables)) + while ($row = mysqli_fetch_row($tabellen)) { + $tbl[] = strtolower($row[0]); + } + $i = 0; + $sql['import']['table'] = $sql['import']['table'].$i; + while (in_array($sql['import']['table'], $tbl)) { + $sql['import']['table'] = substr($sql['import']['table'], 0, strlen($sql['import']['table']) - 1).++$i; + } + $create = 'CREATE TABLE `'.$sql['import']['table'].'` ('.((1 == $sql['import']['createindex']) ? '`import_id` int(11) unsigned NOT NULL auto_increment, ' : ''); + if ($sql['import']['namefirstline']) { + for ($i = 0; $i < count($sql['import']['first_zeile']); ++$i) { + $create .= '`'.$sql['import']['first_zeile'][$i].'` VARCHAR(250) NOT NULL, '; + } + } else { + for ($i = 0; $i < count($sql['import']['first_zeile']); ++$i) { + $create .= '`FIELD_'.$i.'` VARCHAR(250) NOT NULL, '; + } + } + if (1 == $sql['import']['createindex']) { + $create .= 'PRIMARY KEY (`import_id`) '; + } else { + $create = substr($create, 0, strlen($create) - 2); + } + + $create .= ') '.((MOD_NEW_VERSION) ? 'ENGINE' : 'TYPE')."=MyISAM COMMENT='imported at ".date('l dS of F Y H:i:s A')."'"; + $res = mysqli_query($config['dbconnection'], $create) || exit(SQLError($create, mysqli_error($config['dbconnection']))); + return 1; +} + +function ExportXML() +{ + global $sql, $config; + $tab = "\t"; + $level = 0; + $t = ''."\n".''."\n"; + ++$level; + $time_start = time(); + + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + for ($table = 0; $table < count($sql['export']['tables']); ++$table) { + $t .= str_repeat($tab, $level++).''."\n"; + $sqlt = 'SHOW Fields FROM `'.$sql['export']['db'].'`.`'.$sql['export']['tables'][$table].'`;'; + $res = mod_query($sqlt); + if ($res) { + $numfields = mysqli_num_rows($res); + if (1 == $sql['export']['xmlstructure']) { + $t .= str_repeat($tab, $level++).''."\n"; + for ($feld = 0; $feld < $numfields; ++$feld) { + $row = mysqli_fetch_array($res); + $t .= str_repeat($tab, $level++).''."\n"; + $t .= str_repeat($tab, $level).''.$row['Field'].''."\n"; + $t .= str_repeat($tab, $level).''.$row['Type'].''."\n"; + $t .= str_repeat($tab, $level).''.$row['Null'].''."\n"; + $t .= str_repeat($tab, $level).''.$row['Key'].''."\n"; + $t .= str_repeat($tab, $level).''.$row['Default'].''."\n"; + $t .= str_repeat($tab, $level).''.$row['Extra'].''."\n"; + $t .= str_repeat($tab, --$level).''."\n"; + } + $t .= str_repeat($tab, --$level).''."\n"; + } + } + $t .= str_repeat($tab, $level++).''."\n"; + $sqlt = 'SELECT * FROM `'.$sql['export']['db'].'`.`'.$sql['export']['tables'][$table].'`;'; + $res = mod_query($sqlt); + if ($res) { + $numrows = mysqli_num_rows($res); + for ($data = 0; $data < $numrows; ++$data) { + $t .= str_repeat($tab, $level)."\n"; + ++$level; + $row = mysqli_fetch_row($res); + for ($feld = 0; $feld < $numfields; ++$feld) { + $t .= str_repeat($tab, $level).''.$row[$feld].''."\n"; + } + $t .= str_repeat($tab, --$level)."\n"; + ++$sql['export']['lines']; + if ('' == $config['memory_limit']) { + $config['memory_limit'] = 0; + } + if (strlen($t) > $config['memory_limit']) { + CSVOutput($t); + $t = ''; + } + $time_now = time(); + if ($time_start >= $time_now + 30) { + $time_start = $time_now; + header('X-MODPing: Pong'); + } + } + } + $t .= str_repeat($tab, --$level).''."\n"; + $t .= str_repeat($tab, --$level).'
'."\n"; + } + $t .= str_repeat($tab, --$level).'
'."\n"; + CSVOutput($t, 1); +} + +function ExportHTML() +{ + global $sql, $config, $lang; + $header = 'MOD Export'; + $footer = "\n\n\n"; + $content = ''; + $content .= '

'.$lang['L_DB'].' '.$sql['export']['db'].'

'; + + $time_start = time(); + + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + for ($table = 0; $table < count($sql['export']['tables']); ++$table) { + $content .= '

Tabelle '.$sql['export']['tables'][$table].'

'."\n"; + $fsql = 'show fields from `'.$sql['export']['tables'][$table].'`'; + $dsql = 'select * from `'.$sql['export']['tables'][$table].'`'; + //Struktur + $res = mod_query($fsql); + if ($res) { + $field = $fieldname = $fieldtyp = []; + $structure = "\n"; + $numfields = mysqli_num_rows($res); + for ($feld = 0; $feld < $numfields; ++$feld) { + $row = mysqli_fetch_row($res); + $field[$feld] = $row[0]; + + if (0 == $feld) { + $structure .= "\n"; + for ($i = 0; $i < count($row); ++$i) { + $str = mysqli_fetch_field($res, $i); + $fieldname[$i] = $str->name; + $fieldtyp[$i] = $str->type; + $structure .= '\n"; + } + $structure .= "\n\n"; + } + for ($i = 0; $i < count($row); ++$i) { + $structure .= '\n"; + } + $structure .= "\n"; + } + $structure .= "
'.$str->name."
'.(('' != $row[$i]) ? $row[$i] : ' ')."
\n"; + } + if (1 == $sql['export']['htmlstructure']) { + $content .= "

Struktur

\n".$structure; + } + //Daten + + $res = mod_query($dsql); + if ($res) { + $anz = mysqli_num_rows($res); + $content .= "

Daten ($anz Datensätze)

\n"; + $content .= "\n"; + for ($feld = 0; $feld < count($field); ++$feld) { + if (0 == $feld) { + $content .= "\n"; + for ($i = 0; $i < count($field); ++$i) { + $content .= '\n"; + } + $content .= "\n"; + } + } + for ($d = 0; $d < $anz; ++$d) { + $row = mysqli_fetch_row($res); + $content .= "\n"; + for ($i = 0; $i < count($row); ++$i) { + $content .= '\n"; + } + $content .= "\n"; + } + } + $content .= '
'.$field[$i]."
'.(('' != $row[$i]) ? $row[$i] : ' ')."
'; + } + CSVOutput($header.$content.$footer); +} diff --git a/msd/inc/functions_restore.php b/msd/inc/functions_restore.php new file mode 100644 index 0000000..a6da1a3 --- /dev/null +++ b/msd/inc/functions_restore.php @@ -0,0 +1,420 @@ +
Zeile: '.htmlspecialchars($zeile); + } + /******************* Setzen des Parserstatus *******************/ + // herausfinden um was für einen Befehl es sich handelt + if (0 == $sqlparser_status) { + //Vergleichszeile, um nicht bei jedem Vergleich strtoupper ausführen zu müssen + $zeile2 = strtoupper(trim($zeile)); + // pre-built compare strings - so we need the CPU power only once :) + $sub9 = substr($zeile2, 0, 9); + $sub7 = substr($sub9, 0, 7); + $sub6 = substr($sub7, 0, 6); + $sub4 = substr($sub6, 0, 4); + $sub3 = substr($sub4, 0, 3); + $sub2 = substr($sub3, 0, 2); + $sub1 = substr($sub2, 0, 1); + + if ('INSERT ' == $sub7) { + $sqlparser_status = 3; //Datensatzaktion + $restore['actual_table'] = get_tablename($zeile); + } + + //Einfache Anweisung finden die mit Semikolon beendet werden + elseif ('LOCK TA' == $sub7) { + $sqlparser_status = 4; + } elseif ('COMMIT' == $sub6) { + $sqlparser_status = 7; + } elseif ('BEGIN' == substr($sub6, 0, 5)) { + $sqlparser_status = 7; + } elseif ('UNLOCK TA' == $sub9) { + $sqlparser_status = 4; + } elseif ('SET' == $sub3) { + $sqlparser_status = 4; + } elseif ('START ' == $sub6) { + $sqlparser_status = 4; + } elseif ('/*!' == $sub3) { + $sqlparser_status = 5; + } //MySQL-Condition oder Kommentar + elseif ('ALTER TAB' == $sub9) { + $sqlparser_status = 4; + } // Alter Table + elseif ('CREATE TA' == $sub9) { + $sqlparser_status = 2; + } //Create Table + elseif ('CREATE AL' == $sub9) { + $sqlparser_status = 2; + } //Create View + elseif ('CREATE IN' == $sub9) { + $sqlparser_status = 4; + } //Indexaktion + + //Condition? + elseif ((5 != $sqlparser_status) && ('/*' == substr($zeile2, 0, 2))) { + $sqlparser_status = 6; + } + + // Delete actions + elseif ('DROP TABL' == $sub9) { + $sqlparser_status = 1; + } elseif ('DROP VIEW' == $sub9) { + $sqlparser_status = 1; + } + + // Befehle, die nicht ausgeführt werden sollen + elseif ('CREATE DA' == $sub9) { + $sqlparser_status = 7; + } elseif ('DROP DATA ' == $sub9) { + $sqlparser_status = 7; + } elseif ('USE' == $sub3) { + $sqlparser_status = 7; + } + + // Am Ende eines MySQLDumper-Backups angelangt? + elseif ('-- EOB' == $sub6 || '# EO' == $sub4) { + $restore['EOB'] = true; + $restore['fileEOF'] = true; + $zeile = ''; + $zeile2 = ''; + $sqlparser_status = 100; + } + + // Kommentar? + elseif ('--' == $sub2 || '#' == $sub1) { + $zeile = ''; + $zeile2 = ''; + $sqlparser_status = 0; + } + + // Fortsetzung von erweiterten Inserts + if (1 == $restore['flag']) { + $sqlparser_status = 3; + } + + if ((0 == $sqlparser_status) && (trim($complete_sql) > '') && (-1 == $restore['flag'])) { + // Unbekannten Befehl entdeckt + v($restore); + echo '
Sql: '.htmlspecialchars($complete_sql); + echo '
Erweiterte Inserts: '.$restore['erweiterte_inserts']; + exit('
'.$lang['L_UNKNOWN_SQLCOMMAND'].': '.$zeile.'

'.$complete_sql); + } + /******************* Ende von Setzen des Parserstatus *******************/ + } + + $last_char = substr(rtrim($zeile), -1); + // Zeilenumbrüche erhalten - sonst werden Schlüsselwörter zusammengefügt + // z.B. 'null' und in der nächsten Zeile 'check' wird zu 'nullcheck' + $complete_sql .= $zeile."\n"; + + if (3 == $sqlparser_status) { + //INSERT + if (SQL_Is_Complete($complete_sql)) { + $sqlparser_status = 100; + $complete_sql = trim($complete_sql); + if ('*/' == substr($complete_sql, -2)) { + $complete_sql = remove_comment_at_eol($complete_sql); + } + + // letzter Ausdruck des erweiterten Inserts erreicht? + if (');' == substr($complete_sql, -2)) { + $restore['flag'] = -1; + } + + // Wenn am Ende der Zeile ein Klammer Komma -> erweiterter Insert-Modus -> Steuerflag setzen + elseif ('),' == substr($complete_sql, -2)) { + // letztes Komme gegen Semikolon tauschen + $complete_sql = substr($complete_sql, 0, -1).';'; + $restore['erweiterte_inserts'] = 1; + $restore['flag'] = 1; + } + + if ('INSERT ' != substr(strtoupper($complete_sql), 0, 7)) { + // wenn der Syntax aufgrund eines Reloads verloren ging - neu ermitteln + if (!isset($restore['insert_syntax'])) { + $restore['insert_syntax'] = get_insert_syntax($restore['actual_table']); + } + $complete_sql = $restore['insert_syntax'].' VALUES '.$complete_sql.';'; + } else { + // INSERT Syntax ermitteln und merken + $ipos = strpos(strtoupper($complete_sql), ' VALUES'); + if (false === !$ipos) { + $restore['insert_syntax'] = substr($complete_sql, 0, $ipos); + } else { + $restore['insert_syntax'] = 'INSERT INTO `'.$restore['actual_table'].'`'; + } + } + } + } elseif (1 == $sqlparser_status) { + //Löschaktion + if (';' == $last_char) { + $sqlparser_status = 100; + } //Befehl komplett + $restore['actual_table'] = get_tablename($complete_sql); + } elseif (2 == $sqlparser_status) { + // Createanweisung ist beim Finden eines ; beendet + if (';' == $last_char) { + if ($config['minspeed'] > 0) { + $restore['anzahl_zeilen'] = $config['minspeed']; + } + // Soll die Tabelle hergestellt werden? + $do_it = true; + if (is_array($restore['tables_to_restore'])) { + $do_it = false; + if (in_array($restore['actual_table'], $restore['tables_to_restore'])) { + $do_it = true; + } + } + if ($do_it) { + $tablename = submit_create_action($complete_sql); + $restore['actual_table'] = $tablename; + ++$restore['table_ready']; + } + // Zeile verwerfen, da CREATE jetzt bereits ausgefuehrt wurde und naechsten Befehl suchen + $complete_sql = ''; + $sqlparser_status = 0; + } + } + + // Index + elseif (4 == $sqlparser_status) { //Createindex + if (';' == $last_char) { + if ($config['minspeed'] > 0) { + $restore['anzahl_zeilen'] = $config['minspeed']; + } + $complete_sql = del_inline_comments($complete_sql); + $sqlparser_status = 100; + } + } + + // Kommentar oder Condition + elseif (5 == $sqlparser_status) { //Anweisung + $t = strrpos($zeile, '*/;'); + if (false === !$t) { + $restore['anzahl_zeilen'] = $config['minspeed']; + $sqlparser_status = 100; + if ($config['ignore_enable_keys'] && + false !== strrpos($zeile, 'ENABLE KEYS ')) { + $sqlparser_status = 100; + $complete_sql = ''; + } + } + } + + // Mehrzeiliger oder Inline-Kommentar + elseif (6 == $sqlparser_status) { + $t = strrpos($zeile, '*/'); + if (false === !$t) { + $complete_sql = ''; + $sqlparser_status = 0; + } + } + + // Befehle, die verworfen werden sollen + elseif (7 == $sqlparser_status) { //Anweisung + if (';' == $last_char) { + if ($config['minspeed'] > 0) { + $restore['anzahl_zeilen'] = $config['minspeed']; + } + $complete_sql = ''; + $sqlparser_status = 0; + } + } + + if (($restore['compressed']) && (gzeof($restore['filehandle']))) { + $restore['fileEOF'] = true; + } + if ((!$restore['compressed']) && (feof($restore['filehandle']))) { + $restore['fileEOF'] = true; + } + } + // wenn bestimmte Tabellen wiederhergestellt werden sollen -> pruefen + if (is_array($restore['tables_to_restore']) && !(in_array($restore['actual_table'], $restore['tables_to_restore']))) { + $complete_sql = ''; + } + return trim($complete_sql); +} + +function submit_create_action($sql) +{ + global $config; + + //executes a create command + $tablename = get_tablename($sql); + if ('CREATE ALGORITHM' == strtoupper(substr($sql, 0, 16))) { + // It`s a VIEW. We need to substitute the original DEFINER with the actual MySQL-User + $parts = explode(' ', $sql); + for ($i = 0, $count = sizeof($parts); $i < $count; ++$i) { + if ('DEFINER=' == strtoupper(substr($parts[$i], 0, 8))) { + $parts[$i] = 'DEFINER=`'.$config['dbuser'].'`@`'.$config['dbhost'].'`'; + $sql = implode(' ', $parts); + $i = $count; + } + } + } + + $res = mysqli_query($config['dbconnection'], $sql); + if (false === $res) { + // erster Versuch fehlgeschlagen -> zweiter Versuch - vielleicht versteht der Server die Inline-Kommentare nicht? + $sql = del_inline_comments($sql); + $res = mysqli_query($config['dbconnection'], downgrade($sql)); + } + if (false === $res) { + // wenn wir hier angekommen sind hat nichts geklappt -> Fehler ausgeben und abbrechen + SQLError($sql, mysqli_error($config['dbconnection'])); + exit("
Fatal error: Couldn't create table or view `".$tablename.'´'); + } + return $tablename; +} + +function get_insert_syntax($table) +{ + global $config; + + $insert = ''; + $sql = 'SHOW COLUMNS FROM `'.$table.'`'; + $res = mysqli_query($config['dbconnection'], $sql); + if ($res) { + $insert = 'INSERT INTO `'.$table.'` ('; + while ($row = mysqli_fetch_object($res)) { + $insert .= '`'.$row->Field.'`,'; + } + $insert = substr($insert, 0, strlen($insert) - 1).') '; + } else { + global $restore; + v($restore); + SQLError($sql, mysqli_error($config['dbconnection'])); + } + return $insert; +} + +function del_inline_comments($sql) +{ + //$sql=str_replace("\n",'
', $sql); + $array = []; + preg_match_all("/(\/\*(.+)\*\/)/U", $sql, $array); + if (is_array($array[0])) { + $sql = str_replace($array[0], '', $sql); + if (DEBUG) { + echo 'Nachher: :
'.$sql.'

'; + } + } + //$sql=trim(str_replace('
',"\n", $sql)); + //Wenn nach dem Entfernen nur noch ein ; übrigbleibt -> entfernen + if (';' == $sql) { + $sql = ''; + } + return $sql; +} + +// extrahiert auf einfache Art den Tabellennamen aus dem "Create",Drop"-Befehl +function get_tablename($t) +{ + // alle Schluesselbegriffe entfernen, bis der Tabellenname am Anfang steht + $t = substr($t, 0, 150); // verkuerzen, um Speicher zu sparen - wir brauchenhier nur den Tabellennamen + $t = str_ireplace('DROP TABLE', '', $t); + $t = str_ireplace('DROP VIEW', '', $t); + $t = str_ireplace('CREATE TABLE', '', $t); + $t = str_ireplace('INSERT INTO', '', $t); + $t = str_ireplace('REPLACE INTO', '', $t); + $t = str_ireplace('IF NOT EXISTS', '', $t); + $t = str_ireplace('IF EXISTS', '', $t); + if ('CREATE ALGORITHM' == substr(strtoupper($t), 0, 16)) { + $pos = strpos($t, 'DEFINER VIEW '); + $t = substr($t, $pos, strlen($t) - $pos); + } + $t = str_ireplace(';', ' ;', $t); // tricky -> insert space as delimiter + $t = trim($t); + + // jetzt einfach nach dem ersten Leerzeichen suchen + $delimiter = substr($t, 0, 1); + if ('`' != $delimiter) { + $delimiter = ' '; + } + $found = false; + $position = 1; + while (!$found) { + if (substr($t, $position, 1) == $delimiter) { + $found = true; + } + if ($position >= strlen($t)) { + $found = true; + } + ++$position; + } + $t = substr($t, 0, $position); + $t = trim(str_replace('`', '', $t)); + return $t; +} + +// decide if an INSERT-Command is complete - simply count quotes and look for ); at the end of line +function SQL_Is_Complete($string) +{ + $string = str_replace('\\\\', '', trim($string)); // trim and remove escaped backslashes + $string = trim($string); + $quotes = substr_count($string, '\''); + $escaped_quotes = substr_count($string, '\\\''); + if (($quotes - $escaped_quotes) % 2 == 0) { + $compare = substr($string, -2); + if ('*/' == $compare) { + $compare = substr(trim(remove_comment_at_eol($string)), -2); + } + if (');' == $compare) { + return true; + } + if ('),' == $compare) { + return true; + } + } + return false; +} + +function remove_comment_at_eol($string) +{ + // check for Inline-Comments at the end of the line + if ('*/' == substr(trim($string), -2)) { + $pos = strrpos($string, '/*'); + if ($pos > 0) { + $string = trim(substr($string, 0, $pos)); + } + } + return $string; +} diff --git a/msd/inc/functions_sql.php b/msd/inc/functions_sql.php new file mode 100644 index 0000000..9cf19a0 --- /dev/null +++ b/msd/inc/functions_sql.php @@ -0,0 +1,1268 @@ + 0) { + $SQL_ARRAY = file($sf); + } +} + +function WriteSQL() +{ + global $SQL_ARRAY, $config; + $sf = $config['paths']['config'].'sql_statements'; + $str = ''; + for ($i = 0; $i < count($SQL_ARRAY); ++$i) { + $str .= $SQL_ARRAY[$i]; + if ("\n" != substr($str, -1) && $i != (count($SQL_ARRAY) - 1)) { + $str .= "\n"; + } + } + + $fp = fopen($sf, 'wb'); + fwrite($fp, $str); + fclose($fp); +} + +function SQL_Name($index) +{ + global $SQL_ARRAY; + $s = explode('|', $SQL_ARRAY[$index]); + return $s[0]; +} + +function SQL_String($index) +{ + global $SQL_ARRAY; + if (isset($SQL_ARRAY[$index]) && !empty($SQL_ARRAY[$index])) { + $s = explode('|', $SQL_ARRAY[$index], 2); + return (isset($s[1])) ? $s[1] : ''; + } +} + +function SQL_ComboBox() +{ + global $SQL_ARRAY, $tablename, $nl; + $s = ''; + if (is_array($SQL_ARRAY) && count($SQL_ARRAY) > 0) { + $s = $nl.$nl.''.$nl.$nl; + } + return $s; +} + +function Table_ComboBox() +{ + global $db, $config, $lang, $nl; + $tabellen = mysqli_query($config['dbconnection'], 'SHOW TABLES FROM `'.$db.'`'); + $num_tables = 0; + if (is_resource($tabellen)) { + $num_tables = mysqli_num_rows($tabellen); + } + $s = $nl.$nl.''.$nl.$nl; + return $s; +} + +function TableComboBox($default = '') +{ + global $db, $config, $lang, $nl; + + $sql = "SHOW TABLES FROM $db"; + $tabellen = mod_query($sql); + $s = ''.$nl; + while ($row = mysqli_fetch_row($tabellen)) { + $t = $row[0]; + $s .= ''.$nl; + } + return $s; +} + +function DB_Exists($db) +{ + global $config; + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + $erg = false; + + $dbs = mod_query('SHOW DATABASES'); + while ($row = mysqli_fetch_assoc($dbs)) { + if (strtolower($row['Database']) == strtolower($db)) { + $erg = true; + break; + } + } + return $erg; +} + +function Table_Exists($db, $table) +{ + global $config; + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + $sqlt = "SHOW TABLES FROM `$db`"; + $res = mod_query($sqlt); + if ($res) { + $tables = []; + while ($row = mysqli_fetch_row($res)) { + $tables[] = $row[0]; + } + if (in_array($table, $tables)) { + return true; + } + } + return false; +} + +function DB_Empty($dbn) +{ + $r = "DROP DATABASE `$dbn`;\nCREATE DATABASE `$dbn`;"; + return MOD_DoSQL($r); +} + +function sqlReturnsRecords($sql) +{ + global $mysql_SQLhasRecords; + $s = explode(' ', $sql); + return in_array(strtoupper($s[0]), $mysql_SQLhasRecords) ? 1 : 0; +} + +function getCountSQLStatements($sql) +{ + $z = 0; + $l = strlen($sql); + $inQuotes = false; + for ($i = 0; $i < $l; ++$i) { + if ("'" == $sql[$i] || '"' == $sql[$i]) { + $inQuotes = !$inQuotes; + } + if ((';' == $sql[$i] && false == $inQuotes) || $i == $l - 1) { + ++$z; + } + } + return $z; +} + +function splitSQLStatements2Array($sql) +{ + $z = 0; + $sqlArr = []; + $tmp = ''; + $sql = str_replace("\n", '', $sql); + $l = strlen($sql); + $inQuotes = false; + for ($i = 0; $i < $l; ++$i) { + $tmp .= $sql[$i]; + if ("'" == $sql[$i] || '"' == $sql[$i]) { + $inQuotes = !$inQuotes; + } + if (';' == $sql[$i] && false == $inQuotes) { + ++$z; + $sqlArr[] = $tmp; + $tmp = ''; + } + } + if ('' != trim($tmp)) { + $sqlArr[] = $tmp; + } + return $sqlArr; +} + +function DB_Copy($source, $destination, $drop_source = 0, $insert_data = 1) +{ + global $config; + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + $SQL_Array = $t = ''; + if (!DB_Exists($destination)) { + $res = MOD_DoSQL("CREATE DATABASE `$destination`;"); + if (!$res) { + return false; + } + } + $SQL_Array .= "USE `$destination` ;\n"; + $sql = "SHOW TABLES FROM $source"; + $tabellen = mod_query($sql); + while ($row = mysqli_fetch_row($tabellen)) { + $table = strtolower($row[0]); + $sqlt = "SHOW CREATE TABLE `$source`.`$table`"; + $res = mod_query($sqlt); + if ($res) { + $row = mysqli_fetch_row($res); + $c = $row[1]; + if (';' == substr($c, -1)) { + $c = substr($c, 0, strlen($c) - 1); + } + $SQL_Array .= (1 == $insert_data) ? "$c SELECT * FROM `$source`.`$table` ;\n" : "$c ;\n"; + } else { + return false; + } + } + mysqli_select_db($config['dbconnection'], $destination); + $res = MOD_DoSQL($SQL_Array); + if (1 == $drop_source && $res) { + mod_query("DROP DATABASE `$source`;"); + } + return $res; +} + +function Table_Copy($source, $destination, $insert_data, $destinationdb = '') +{ + global $config; + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + $SQL_Array = $t = ''; + $sqlc = "SHOW CREATE TABLE $source"; + $res = mod_query($sqlc); + $row = mysqli_fetch_row($res); + $c = $row[1]; + $a1 = strpos($c, '`'); + $a2 = strpos($c, '`', $a1 + 1); + $c = substr($c, 0, $a1 + 1).$destination.substr($c, $a2); + if (';' == substr($c, -1)) { + $c = substr($c, 0, strlen($c) - 1); + } + $SQL_Array .= (1 == $insert_data) ? "$c SELECT * FROM $source ;\n" : "$c ;\n"; + //echo "
$SQL_Array
"; + MOD_DoSQL($SQL_Array); +} + +function MOD_DoSQL($sqlcommands, $limit = '') +{ + global $config, $out, $numrowsabs, $numrows, $num_befehle, $time_used, $sql; + + if (!isset($sql['parser']['sql_commands'])) { + $sql['parser']['sql_commands'] = 0; + } + if (!isset($sql['parser']['sql_errors'])) { + $sql['parser']['sql_errors'] = 0; + } + + $sql['parser']['time_used'] = getmicrotime(); + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + $out = $sqlcommand = ''; + $allSQL = splitSQLStatements2Array($sqlcommands); //explode(';',preg_replace('/\r\n|\n/', '', $sqlcommands)); + $sql_queries = count($allSQL); + + if (!isset($allSQL[$sql_queries - 1])) { + --$sql_queries; + } + if (1 == $sql_queries) { + SQLParser($allSQL[0]); + ++$sql['parser']['sql_commands']; + $out .= Stringformat(($sql['parser']['sql_commands']), 4).': '.$allSQL[0]."\n"; + $result = mod_query($allSQL[0]); + } else { + $result = true; + for ($i = 0; $i < $sql_queries; ++$i) { + $allSQL[$i] = trim(rtrim($allSQL[$i])); + + if ('' != $allSQL[$i]) { + $sqlcommand .= $allSQL[$i]; + $sqlcommand = SQLParser($sqlcommand); + if (0 == $sql['parser']['start'] && 0 == $sql['parser']['end'] && '' != $sqlcommand) { + //sql complete + ++$sql['parser']['sql_commands']; + $out .= Stringformat(($sql['parser']['sql_commands']), 4).': '.$sqlcommand."\n"; + $result = $result && mod_query($sqlcommand); + $sqlcommand = ''; + } + } + } + } + $sql['parser']['time_used'] = getmicrotime() - $sql['parser']['time_used']; + return $result; +} + +function SQLParser($command, $debug = 0) +{ + global $sql; + $sql['parser']['start'] = $sql['parser']['end'] = 0; + $sql['parser']['sqlparts'] = 0; + if (!isset($sql['parser']['drop'])) { + $sql['parser']['drop'] = 0; + } + if (!isset($sql['parser']['create'])) { + $sql['parser']['create'] = 0; + } + if (!isset($sql['parser']['insert'])) { + $sql['parser']['insert'] = 0; + } + if (!isset($sql['parser']['update'])) { + $sql['parser']['update'] = 0; + } + if (!isset($sql['parser']['comment'])) { + $sql['parser']['comment'] = 0; + } + $Backslash = chr(92); + $s = rtrim(trim(($command))); + + //Was ist das für eine Anfrage ? + if ('#' == substr($s, 0, 1) || '--' == substr($s, 0, 2)) { + ++$sql['parser']['comment']; + $s = ''; + } elseif ('DROP ' == strtoupper(substr($s, 0, 5))) { + ++$sql['parser']['drop']; + } elseif ('CREATE ' == strtoupper(substr($s, 0, 7))) { + //Hier nur die Anzahl der Klammern zählen + $sql['parser']['start'] = 1; + $kl1 = substr_count($s, '('); + $kl2 = substr_count($s, ')'); + if (0 == $kl2 - $kl1) { + $sql['parser']['start'] = 0; + ++$sql['parser']['create']; + } + } elseif ('INSERT ' == strtoupper(substr($s, 0, 7)) || 'UPDATE ' == strtoupper(substr($s, 0, 7))) { + if ('INSERT ' == strtoupper(substr($s, 0, 7))) { + ++$sql['parser']['insert']; + } else { + ++$sql['parser']['update']; + } + $i = strpos(strtoupper($s), ' VALUES') + 7; + $st = substr($s, $i); + $i = strpos($st, '(') + 1; + $st = substr($st, $i); + $st = substr($st, 0, strlen($st) - 2); + + $tb = explode(',', $st); + for ($i = 0; $i < count($tb); ++$i) { + $first = $B_Esc = $B_Ticks = $B_Dashes = 0; + $v = trim($tb[$i]); + //Ticks + Dashes zählen + for ($cpos = 2; $cpos <= strlen($v); ++$cpos) { + if ("'" == substr($v, (-1 * $cpos), 1)) { + ++$B_Ticks; + } else { + break; + } + } + for ($cpos = 2; $cpos <= strlen($v); ++$cpos) { + if ('"' == substr($v, (-1 * $cpos), 1)) { + ++$B_Dashes; + } else { + break; + } + } + + //Backslashes zählen + for ($cpos = 2 + $B_Ticks; $cpos <= strlen($v); ++$cpos) { + if ('\\' == substr($v, (-1 * $cpos), 1)) { + ++$B_Esc; + } else { + break; + } + } + + if ('NULL' == $v && 0 == $sql['parser']['start']) { + $sql['parser']['start'] = 1; + $sql['parser']['end'] = 1; + } + if (0 == $sql['parser']['start'] && is_numeric($v)) { + $sql['parser']['start'] = 1; + $sql['parser']['end'] = 1; + } + if (0 == $sql['parser']['start'] && '0X' == substr($v, 0, 2) && false == strpos($v, ' ')) { + $sql['parser']['start'] = 1; + $sql['parser']['end'] = 1; + } + if (0 == $sql['parser']['start'] && is_object($v)) { + $sql['parser']['start'] = 1; + $sql['parser']['end'] = 1; + } + + if ("'" == substr($v, 0, 1) && 0 == $sql['parser']['start']) { + $sql['parser']['start'] = 1; + if (1 == strlen($v)) { + $first = 1; + } + $DELIMITER = "'"; + } + if ('"' == substr($v, 0, 1) && 0 == $sql['parser']['start']) { + $sql['parser']['start'] = 1; + if (1 == strlen($v)) { + $first = 1; + } + $DELIMITER = '"'; + } + if (1 == $sql['parser']['start'] && 1 != $sql['parser']['end'] && 0 == $first) { + if (substr($v, -1) == $DELIMITER) { + $B_Delimiter = ("'" == $DELIMITER) ? $B_Ticks : $B_Dashes; + //ist Delimiter maskiert? + if (($B_Esc % 2) == 1 && ($B_Delimiter % 2) == 1 && strlen($v) > 2) { + $sql['parser']['end'] = 1; + } elseif (($B_Delimiter % 2) == 1 && strlen($v) > 2) { + //ist mit `'` maskiert + $sql['parser']['end'] = 0; + } elseif (($B_Esc % 2) == 1) { + //ist mit Backslash maskiert + $sql['parser']['end'] = 0; + } else { + $sql['parser']['end'] = 1; + } + } + } + if (1 == $debug) { + echo "".$sql['parser']['start'].'/'.$sql['parser']['end']." Feld $i: ".htmlspecialchars($tb[$i]).'- '.$sql['parser']['sqlparts']." ($B_Ticks / $B_Esc)
"; + } + if (1 == $sql['parser']['start'] && 1 == $sql['parser']['end']) { + ++$sql['parser']['sqlparts']; + $sql['parser']['start'] = $sql['parser']['end'] = 0; + } + } + } + return $s; +} + +function SQLOutput($sqlcommand, $meldung = '') +{ + global $sql, $lang; + $s = '
'.$lang['L_SQL_OUTPUT'].'
'; + if ('' != $meldung) { + $s .= trim($meldung); + } + + if (isset($sql['parser']['sql_commands'])) { + $s .= ' '.$sql['parser']['sql_commands'].''.$lang['L_SQL_COMMANDS_IN'].round($sql['parser']['time_used'], 4).$lang['L_SQL_COMMANDS_IN2'].'

'; + $s .= $lang['L_SQL_OUT1'].''.$sql['parser']['drop'].' DROP-, '; + $s .= ''.$sql['parser']['create'].' CREATE-, '; + $s .= ''.$sql['parser']['insert'].' INSERT-, '; + $s .= ''.$sql['parser']['update'].' UPDATE-'.$lang['L_SQL_OUT2'].'
'; + $s .= $lang['L_SQL_OUT3'].''.$sql['parser']['comment'].' '.$lang['L_SQL_OUT4'].'
'; + if ($sql['parser']['sql_commands'] < 50) { + $s .= '
'.Highlight_SQL($sqlcommand).'
'; + } else { + $s .= $lang['L_SQL_OUT5']; + } + } elseif ('' != $sqlcommand) { + $s .= '
'.$lang['L_SQL_OUTPUT'].'
'.Highlight_SQL($sqlcommand).'
'; + } + return $s.'
'; +} + +function GetCreateTable($db, $tabelle) +{ + global $config; + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + $res = mysqli_query($config['dbconnection'], "SHOW CREATE TABLE `$db`.`$tabelle`"); + if ($res) { + $row = mysqli_fetch_array($res); + if (isset($row['Create Table'])) { + return $row['Create Table']; + } elseif (isset($row['Create View'])) { + return $row['Create View']; + } else { + return false; + } + } else { + return mysqli_error($config['dbconnection']); + } +} + +function KindSQL($sql) +{ + if (preg_match('@^((-- |#)[^\n]*\n|/\*.*?\*/)*(DROP|CREATE)[[:space:]]+(IF EXISTS[[:space:]]+)?(TABLE|DATABASE)[[:space:]]+(.+)@im', $sql)) { + return 2; + } elseif (preg_match('@^((-- |#)[^\n]*\n|/\*.*?\*/)*(DROP|CREATE)[[:space:]]+(IF EXISTS[[:space:]]+)?(TABLE|DATABASE)[[:space:]]+(.+)@im', $sql)) { + return 1; + } +} + +function GetPostParams() +{ + global $db, $dbid, $tablename, $context, $limitstart, $order, $orderdir, $sql; + $db = $_POST['db']; + $dbid = $_POST['dbid']; + $tablename = $_POST['tablename']; + $context = $_POST['context']; + $limitstart = $_POST['limitstart']; + $order = $_POST['order']; + $orderdir = $_POST['orderdir']; + $sql['sql_statement'] = (isset($_POST['sql_statement'])) ? $_POST['sql_statement'] : "SELECT * FROM `$tablename`"; +} + +// when fieldnames contain spaces or dots they are replaced with underscores +// we need to built the same index to get the postet values for inserts and updates +function correct_post_index($index) +{ + $index = str_replace(' ', '_', $index); + $index = str_replace('.', '_', $index); + return $index; +} +function ComboCommandDump($when, $index, $disabled = '') +{ + global $SQL_ARRAY, $nl, $databases, $lang; + if ((is_array($SQL_ARRAY) && 0 == count($SQL_ARRAY)) || !is_array($SQL_ARRAY)) { + $r = ''.$lang['L_SQL_BEFEHLE'].''; + if (0 == $when) { + $r .= ''; + } else { + $r .= ''; + } + } else { + if (0 == $when) { + $r = ''; + $csql = $databases['command_after_dump'][$index]; + } + + $r .= ''."\n"; + if (is_array($SQL_ARRAY) && count($SQL_ARRAY) > 0) { + for ($i = 0; $i < count($SQL_ARRAY); ++$i) { + $s = trim(SQL_String($i)); + $r .= ''."\n"; + } + } + $r .= ''; + } + return $r; +} + +function EngineCombo($default = '') +{ + global $config; + if (!$config['dbconnection']) { + mod_mysqli_connect(); + } + + $r = ''; + if (!MOD_NEW_VERSION) { + //BDB | HEAP | ISAM | InnoDB | MERGE | MRG_MYISAM | MYISAM + $r .= ''; + $r .= ''; + $r .= ''; + $r .= ''; + $r .= ''; + $r .= ''; + $r .= ''; + } else { + $res = mysqli_query($config['dbconnection'], 'SHOW ENGINES'); + $num = mysqli_num_rows($res); + for ($i = 0; $i < $num; ++$i) { + $row = mysqli_fetch_array($res); + $r .= ''; + } + } + return $r; +} + +function CharsetCombo($default = '') +{ + global $config; + if (!MOD_NEW_VERSION) { + return ''; + } else { + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + $res = mysqli_query($config['dbconnection'], 'SHOW Charset'); + $num = mysqli_num_rows($res); + $r = ''; + $charsets = []; + for ($i = 0; $i < $num; ++$i) { + $charsets[] = mysqli_fetch_array($res); + } + + if (is_array($charsets)) { + $charsets = mu_sort($charsets, 'Charset'); + foreach ($charsets as $row) { + $r .= ''; + } + } + return $r; + } +} + +function GetCollationArray() +{ + global $config; + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + + $res = mysqli_query($config['dbconnection'], 'SHOW Collation'); + $num = mysqli_num_rows($res); + $r = []; + if (is_array($r)) { + for ($i = 0; $i < $num; ++$i) { + $row = mysqli_fetch_array($res); + $r[$i]['Collation'] = isset($row['Collation']) ? $row['Collation'] : ''; + $r[$i]['Charset'] = isset($row['Charset']) ? $row['Charset'] : ''; + $r[$i]['Id'] = isset($row['Id']) ? $row['Id'] : ''; + $r[$i]['Default'] = isset($row['Default']) ? $row['Default'] : ''; + $r[$i]['Compiled'] = isset($row['Compiled']) ? $row['Compiled'] : ''; + $r[$i]['Sortlen'] = isset($row['Sortlen']) ? $row['Sortlen'] : ''; + } + } + return $r; +} + +function CollationCombo($default = '', $withcharset = 0) +{ + if (!MOD_NEW_VERSION) { + return ''; + } else { + $r = GetCollationArray(); + sort($r); + $s = ''; + $s = ''; + $group = ''; + for ($i = 0; $i < count($r); ++$i) { + $gc = $r[$i]['Charset']; + if ($gc != $group) { + $group = $gc; + if ($i > 0) { + $s .= ''; + } + $s .= ''; + } + $s .= ''; + } + return $s.''; + } +} + +function AttributeCombo($default = '') +{ + $s = ''; + $s .= ''; + $s .= ''; + return $s; +} + +function simple_bbcode_conversion($a) +{ + global $config; + $tag_start = ''; + $tag_end = ''; + + //replacements + $a = nl2br($a); + $a = str_replace('
', '
', $a); + $a = str_replace('
', '
', $a); + + $a = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/si", '$2', $a); + $a = preg_replace("/\[urltargetself=(.*?)\](.*?)\[\/urltargetself\]/si", '$2', $a); + $a = preg_replace("/\[url\](.*?)\[\/url\]/si", '$1', $a); + $a = preg_replace("/\[ed2k=\+(.*?)\](.*?)\[\/ed2k\]/si", '$2', $a); + $a = preg_replace("/\[ed2k=(.*?)\](.*?)\[\/ed2k\]/si", '$2', $a); + + $a = preg_replace("/\[center\](.*?)\[\/center\]/si", '
$1
', $a); + $a = preg_replace("/\[size=([1-2]?[0-9])\](.*?)\[\/size\]/si", '$2', $a); + $a = preg_replace("/\[size=([1-2]?[0-9]):(.*?)\](.*?)\[\/size(.*?)\]/si", '$3', $a); + $a = preg_replace("/\[font=(.*?)\](.*?)\[\/font\]/si", '$2', $a); + $a = preg_replace("/\[color=(.*?)\](.*?)\[\/color\]/si", '$2', $a); + $a = preg_replace("/\[color=(.*?):(.*?)\](.*?)\[\/color(.*?)\]/si", '$3', $a); + $a = preg_replace("/\[img\](.*?)\[\/img\]/si", '', $a); + //$a=preg_replace("/\[b\](.*?)\[\/b\]/si", "$1", $a); + $a = preg_replace("/\[b(.*?)\](.*?)\[\/b(.*?)\]/si", '$2', $a); + //$a=preg_replace("/\[u\](.*?)\[\/u\]/si", "$1", $a); + $a = preg_replace("/\[u(.*?)\](.*?)\[\/u(.*?)\]/si", '$2', $a); + //$a=preg_replace("/\[i\](.*?)\[\/i\]/si", "$1", $a); + $a = preg_replace("/\[i(.*?)\](.*?)\[\/i(.*?)\]/si", '$2', $a); + //$a=preg_replace("/\[quote\](.*?)\[\/quote\]/si", "

$1

", $a); + $a = preg_replace("/\[quote(.*?)\](.*?)\[\/quote(.*?)\]/si", '

$2

', $a); + $a = preg_replace("/\[code(.*?)\](.*?)\[\/code(.*?)\]/si", '

$2

', $a); + $a = preg_replace("/\[hide\](.*?)\[\/hide\]/si", '
$1
', $a); + $a = preg_replace("/(^|\s)+((http:\/\/)|(www.))(.+)(\s|$)+/Uis", ' http://$4$5 ', $a); + return $tag_start.$a.$tag_end; +} + +function ExtractTablenameFromSQL($q) +{ + global $databases, $db, $dbid; + $tablename = ''; + if (strlen($q) > 100) { + $q = substr($q, 0, 100); + } + $p = trim($q); + // if we get a list of tables - no current table is selected -> return '' + if ('SHOW TABLE STATUS' == strtoupper(substr($p, 0, 17))) { + return ''; + } + // check for SELECT-Statement to extract tablename after FROM + if ('SELECT ' == strtoupper(substr($p, 0, 7))) { + $parts = []; + $p = substr($p, strpos(strtoupper($p), 'FROM') + 5); + $parts = explode(' ', $p); + $p = $parts[0]; + } + // remove keyword DATABASES and the database name after that + $p = preg_replace('/DATABASE [`]*\w+[`]*/i', '', $p); + // remove other keywords + $suchen = [ + 'SHOW DATABASES', + 'SHOW ', + 'SELECT', + 'DROP', + 'INSERT', + 'UPDATE', + 'DELETE', + 'CREATE', + 'TABLE', + 'STATUS', + 'FROM', + '*', + ]; + $ersetzen = [ + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + ]; + $cleaned = trim(str_ireplace($suchen, $ersetzen, $p)); + $tablename = $cleaned; + if (strpos($cleaned, ' ')) { + $tablename = substr($cleaned, 0, strpos($cleaned, ' ')); + } + $tablename = str_replace('`', '', $tablename); // remove backticks + // take care of db-name.tablename + if (strpos($tablename, '.')) { + $p = explode('.', $tablename); + $databases['db_actual'] = $p[0]; + // if database is changed in Query we need to get the index of the actual db + $db_temp = array_flip($databases['Name']); + if (isset($db_temp[$databases['db_actual']])) { + $databases['db_selected_index'] = $db_temp[$databases['db_actual']]; + $dbid = $databases['db_selected_index']; + } + if (isset($_GET['tablename'])) { + unset($_GET['tablename']); + } + //echo "
" . $db; + $tablename = $p[1]; + } + // if (Table_Exists($databases['db_actual'], $tablename)) return $tablename; + // else return ''; + return $tablename; +} + +function GetOptionsCombo($arr, $default) +{ + global $feldtypen, $feldattribute, $feldnull, $feldextras, $feldkeys, $feldrowformat; + $r = ''; + foreach ($arr as $s) { + $r .= ''."\n"; + } + return $r; +} + +function make_options($arr, $selected) +{ + $r = ''; + foreach ($arr as $key => $val) { + $r .= '
'; +ob_end_flush(); +exit(); diff --git a/msd/inc/home/system.php b/msd/inc/home/system.php new file mode 100644 index 0000000..6c6a774 --- /dev/null +++ b/msd/inc/home/system.php @@ -0,0 +1,101 @@ +'; + $res = mysqli_query($config['dbconnection'], 'FLUSH PRIVILEGES'); + $meldung = mysqli_error($config['dbconnection']); + if ('' != $meldung) { + $msg .= '> MySQL-Error: '.$meldung; + } else { + $msg .= '> Privileges were reloaded.'; + } + break; + case 2: //FLUSH STATUS + $msg = '> operating FLUSH STATUS
'; + $res = mysqli_query($config['dbconnection'], 'FLUSH STATUS'); + $meldung = mysqli_error($config['dbconnection']); + if ('' != $meldung) { + $msg .= '> MySQL-Error: '.$meldung; + } else { + $msg .= '> Status was reset.'; + } + break; + case 3: //FLUSH HOSTS + $msg = '> operating FLUSH HOSTS
'; + $res = mysqli_query($config['dbconnection'], 'FLUSH HOSTS'); + $meldung = mysqli_error($config['dbconnection']); + if ('' != $meldung) { + $msg .= '> MySQL-Error: '.$meldung; + } else { + $msg .= '> Hosts were reloaded.'; + } + break; + case 4: //SHOW MASTER LOGS + $msg = '> operating SHOW MASTER LOGS
'; + $res = mysqli_query($config['dbconnection'], 'SHOW MASTER LOGS'); + $meldung = mysqli_error($config['dbconnection']); + if ('' != $meldung) { + $msg .= '> MySQL-Error: '.$meldung; + } else { + $numrows = mysqli_num_rows($res); + if (0 == $numrows || false === $numrows) { + $msg .= '> there are no master log-files'; + } else { + $msg .= '> there are '.$numrows.' logfiles
'; + for ($i = 0; $i < $numrows; ++$i) { + $row = mysqli_fetch_row($res); + $msg .= '> '.$row[0].'   '.(($data_dir) ? byte_output(@filesize($data_dir.$row[0])) : '').'
'; + } + } + } + break; + case 5: //RESET MASTER + $msg = '> operating RESET MASTER
'; + $res = mysqli_query($config['dbconnection'], 'RESET MASTER'); + $meldung = mysqli_error($config['dbconnection']); + if ('' != $meldung) { + $msg .= '> MySQL-Error: '.$meldung; + } else { + $msg .= '> All Masterlogs were deleted.'; + } + break; +} +echo '
'.$lang['L_MYSQLSYS'].'
'; +echo ''; +echo '
'; +echo '> MySQL Dumper v'.MOD_VERSION.' - Output Console

'; +echo ('' != $msg) ? $msg : '> waiting for operation ...
'; +echo '
'; diff --git a/msd/inc/home/update.php b/msd/inc/home/update.php new file mode 100644 index 0000000..42ed9bf --- /dev/null +++ b/msd/inc/home/update.php @@ -0,0 +1,87 @@ +newVersionAvailable() && $check_update === true) { + // Install new update + echo '

<< Home

'; + + echo $lang['L_NEW_MOD_VERSION'] . ': ' . $update->getLatestVersion() . '
'; + echo $lang['L_INSTALLING_UPDATES'] . ':
'; + /* + echo '
';
+        var_dump(array_map(function ($version) {
+            return (string) $version;
+        }, $update->getVersionsToUpdate()));
+        echo '
'; + */ + // Optional - empty log file + $f = fopen($config['paths']['log'] . 'update.log', 'rb+'); + if ($f !== false) { + ftruncate($f, 0); + fclose($f); + } + + /* + // Optional Callback function - on each version update + function eachUpdateFinishCallback($updatedVersion) + { + echo '

CALLBACK for version ' . $updatedVersion . '

'; + } + $update->onEachUpdateFinish('eachUpdateFinishCallback'); + + // Optional Callback function - on each version update + function onAllUpdateFinishCallbacks($updatedVersions) + { + echo '

CALLBACK for all updated versions:

'; + echo '
    '; + foreach ($updatedVersions as $v) { + echo '
  • ' . $v . '
  • '; + } + echo '
'; + } + $update->setOnAllUpdateFinishCallbacks('onAllUpdateFinishCallbacks'); + */ + + // This call will only simulate an update. + // Set the first argument (simulate) to "false" to install the update + // i.e. $update->update(false); + $result = $update->update(false); + + if ($result === true) { + echo $lang['L_UPDATE_SUCCESSFUL'] . '
'; + } else { + echo $lang['L_UPDATE_FAILED'] . ': ' . $result . '!
'; + + if ($result = AutoUpdate::ERROR_SIMULATE) { + echo '
';
+            var_dump($update->getSimulationResults());
+            echo '
'; + } + } +} else { + echo $lang['L_UP_TO_DATE']. '
'; +} + +echo 'Log:
'; +echo nl2br(file_get_contents($config['paths']['log'] . '/update.log')); + +echo '

<< Home

'; diff --git a/msd/inc/mysqli.php b/msd/inc/mysqli.php new file mode 100644 index 0000000..d12963c --- /dev/null +++ b/msd/inc/mysqli.php @@ -0,0 +1,543 @@ + 'http://dev.mysql.com/doc/mysql/de/Column_types.html', +]; +$mysql_string_types = [ + 'char', + 'varchar', + 'tinytext', + 'text', + 'mediumtext', + 'longtext', + 'binary', + 'varbinary', + 'tinyblob', + 'mediumblob', + 'blob', + 'longblob', + 'enum', + 'set', +]; +$mysql_SQLhasRecords = [ + 'SELECT', + 'SHOW', + 'EXPLAIN', + 'DESCRIBE', + 'DESC', +]; + +function mod_mysqli_connect($encoding = 'utf8mb4', $keycheck_off = false, $actual_table = '') +{ + global $config, $databases; + + if (isset($config['dbconnection']) && is_resource($config['dbconnection'])) { + return $config['dbconnection']; + } + + $port = (isset($config['dbport']) && !empty($config['dbport'])) ? ':'.$config['dbport'] : ''; + $socket = (isset($config['dbsocket']) && !empty($config['dbsocket'])) ? ':'.$config['dbsocket'] : ''; + + // Forcing error reporting mode to OFF, which is no longer the default + // starting with PHP 8.1 + @mysqli_report(MYSQLI_REPORT_OFF); + + $config['dbconnection'] = @mysqli_connect($config['dbhost'].$port.$socket, $config['dbuser'], $config['dbpass']); + + if (!$config['dbconnection']) { + exit(SQLError('Error establishing a database connection!', mysqli_connect_error())); + } + if (!defined('MOD_MYSQL_VERSION')) { + GetMySQLVersion(); + } + + if (!isset($config['mysql_standard_character_set']) || '' == $config['mysql_standard_character_set']) { + get_sql_encodings(); + } + + if ($config['mysql_standard_character_set'] != $encoding) { + $set_encoding = mysqli_query($config['dbconnection'], 'SET NAMES \''.$encoding.'\''); + if (false === $set_encoding) { + $config['mysql_can_change_encoding'] = false; + } else { + $config['mysql_can_change_encoding'] = true; + } + } + if ($keycheck_off) { + // only called with this param when restoring + mysqli_query($config['dbconnection'], 'SET FOREIGN_KEY_CHECKS=0'); + // also set SQL-Mode NO_AUTO_VALUE_ON_ZERO for magento users + mysqli_query($config['dbconnection'], 'SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"'); + } + + return $config['dbconnection']; +} + +function GetMySQLVersion() +{ + global $config; + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + + $res = mod_query('SELECT VERSION()'); + $row = mysqli_fetch_array($res); + $str = $row[0]; + $version = str_replace(':', '--', $str); + if (!defined('MOD_MYSQL_VERSION')) { + define('MOD_MYSQL_VERSION', $version); + } + $versions = explode('.', $version); + $new = false; + if (4 == $versions[0] && $versions[1] >= 1) { + $new = true; + } + if ($versions[0] > 4) { + $new = true; + } + if (!defined('MOD_NEW_VERSION')) { + define('MOD_NEW_VERSION', $new); + } + + return $version; +} + +function mod_query($query, $error_output = true) +{ + global $config; + // print_mem(); + if (!isset($config['dbconnection'])) { + mod_mysqli_connect(); + } + // echo "
Query: ".htmlspecialchars($query).'
'; + $res = mysqli_query($config['dbconnection'], $query); + // print_mem(); + if (false === $res && $error_output) { + SQLError($query, mysqli_error($config['dbconnection'])); + } + return $res; +} + +function print_mem() +{ + /* Currently used memory */ + $mem_usage = memory_get_usage(); + + /* Peak memory usage */ + $mem_peak = memory_get_peak_usage(); + + echo 'The script is now using: '.round($mem_usage / 1024).' KB of memory.
'; + echo 'Peak usage: '.round($mem_peak / 1024).' KB of memory.

'; +} + +function SQLError($sql, $error, $return_output = false) +{ + global $lang; + + $ret = '
+ + + +
MySQL-ERROR
'.$lang['L_SQL_ERROR2'].'
'.$error.'

'.$lang['L_SQL_ERROR1'].'
'.Highlight_SQL($sql).'

'; + if ($return_output) { + return $ret; + } else { + echo $ret; + } +} + +function Highlight_SQL($sql) +{ + global $sql_keywords; + + $end = ''; + $tickstart = false; + if (function_exists('token_get_all')) { + $a = @token_get_all(""); + } else { + return $sql; + } + foreach ($a as $token) { + if (!is_array($token)) { + if ('`' == $token) { + $tickstart = !$tickstart; + } + $end .= $token; + } else { + if ($tickstart) { + $end .= $token[1]; + } else { + switch (token_name($token[0])) { + case 'T_STRING': + case 'T_AS': + case 'T_FOR': + $end .= (in_array(strtoupper($token[1]), $sql_keywords)) ? ''.$token[1].'' : $token[1]; + break; + case 'T_IF': + case 'T_LOGICAL_AND': + case 'T_LOGICAL_OR': + case 'T_LOGICAL_XOR': + $end .= (in_array(strtoupper($token[1]), $sql_keywords)) ? ''.$token[1].'' : $token[1]; + break; + case 'T_CLOSE_TAG': + case 'T_OPEN_TAG': + break; + default: + $end .= $token[1]; + } + } + } + } + $end = preg_replace('/`(.*?)`/si', '`$1`', $end); + return $end; +} + +function Fieldlist($db, $tbl) +{ + $fl = ''; + $res = mod_query("SHOW FIELDS FROM `$db`.`$tbl`;"); + if ($res) { + $fl = '('; + for ($i = 0; $i < mysqli_num_rows($res); ++$i) { + $row = mysqli_fetch_row($res); + $fl .= '`'.$row[0].'`,'; + } + $fl = substr($fl, 0, strlen($fl) - 1).')'; + } + return $fl; +} + +// reads all Tableinfos and place them in $dump-Array +function getDBInfos() +{ + global $databases, $dump, $config, $tbl_sel, $flipped; + for ($ii = 0; $ii < count($databases['multi']); ++$ii) { + $dump['dbindex'] = $flipped[$databases['multi'][$ii]]; + $tabellen = mysqli_query($config['dbconnection'], 'SHOW TABLE STATUS FROM `'.$databases['Name'][$dump['dbindex']].'`') or exit('getDBInfos: '.mysqli_error($config['dbconnection'])); + $num_tables = mysqli_num_rows($tabellen); + // Array mit den gewünschten Tabellen zusammenstellen... wenn Präfix angegeben, werden die anderen einfach nicht übernommen + if ($num_tables > 0) { + for ($i = 0; $i < $num_tables; ++$i) { + $row = mysqli_fetch_array($tabellen); + if (isset($row['Type'])) { + $row['Engine'] = $row['Type']; + } + if (isset($row['Comment']) && 'VIEW' == substr(strtoupper($row['Comment']), 0, 4)) { + $dump['table_types'][] = 'VIEW'; + } else { + $dump['table_types'][] = strtoupper($row['Engine']); + } + // check if data needs to be backed up + if ('VIEW' == strtoupper($row['Comment']) || (isset($row['Engine']) && in_array(strtoupper($row['Engine']), [ + 'MEMORY', + ]))) { + $dump['skip_data'][] = $databases['Name'][$dump['dbindex']].'|'.$row['Name']; + } + if ((isset($config['optimize_tables_beforedump']) && (1 == $config['optimize_tables_beforedump'])) && -1 == $dump['table_offset'] + && 'information_schema' != $databases['Name'][$dump['dbindex']]) { + mysqli_select_db($config['dbconnection'], $databases['Name'][$dump['dbindex']]); + $opt = 'OPTIMIZE TABLE `'.$row['Name'].'`'; + $res = mysqli_query($config['dbconnection'], 'OPTIMIZE TABLE `'.$row['Name'].'`'); + if (false === $res) { + exit('Error in '.$opt.' -> '.mysqli_error($config['dbconnection'])); + } + } + + if (isset($tbl_sel)) { + if (in_array($row['Name'], $dump['tblArray'])) { + $dump['tables'][] = $databases['Name'][$dump['dbindex']].'|'.$row['Name']; + $dump['records'][] = $databases['Name'][$dump['dbindex']].'|'.$row['Rows']; + $dump['totalrecords'] += $row['Rows']; + } + } elseif ('' != $databases['praefix'][$dump['dbindex']] && !isset($tbl_sel)) { + if (substr($row['Name'], 0, strlen($databases['praefix'][$dump['dbindex']])) == $databases['praefix'][$dump['dbindex']]) { + $dump['tables'][] = $databases['Name'][$dump['dbindex']].'|'.$row['Name']; + $dump['records'][] = $databases['Name'][$dump['dbindex']].'|'.$row['Rows']; + $dump['totalrecords'] += $row['Rows']; + } + } else { + $dump['tables'][] = $databases['Name'][$dump['dbindex']].'|'.$row['Name']; + $dump['records'][] = $databases['Name'][$dump['dbindex']].'|'.$row['Rows']; + + // Get nr of records -> need to do it this way because of incorrect returns when using InnoDBs + $sql_2 = 'SELECT count(*) as `count_records` FROM `'.$databases['Name'][$dump['dbindex']].'`.`'.$row['Name'].'`'; + $res2 = mysqli_query($config['dbconnection'], $sql_2); + if (false === $res2) { + $read_error = mysqli_error($config['dbconnection']); + SQLError($read_error, $sql_2); + WriteLog($read_error); + if ($config['stop_with_error'] > 0) { + exit($read_error); + } + } else { + $row2 = mysqli_fetch_array($res2); + $row['Rows'] = $row2['count_records']; + $dump['totalrecords'] += $row['Rows']; + } + } + } + // Correct total number of records; substract skipped data + foreach ($dump['skip_data'] as $skip_data) { + $index = false; + $records_to_skip = 0; + //find index of table to get the nr of records + $count = sizeof($dump['tables']); + for ($a = 0; $a < $count; ++$a) { + if ($dump['tables'][$a] == $skip_data) { + $index = $a; + $t = explode('|', $dump['records'][$a]); + $rekords_to_skip = $t[1]; + break; + } + } + if ($index) { + $dump['totalrecords'] -= intval($rekords_to_skip); + } + } + } + } +} + +// gets the numeric index in dump-array and returns it +function getDBIndex($db, $table) +{ + global $dump; + $index = array_keys($dump['tables'], $db.'|'.$table); + return $index[0]; +} diff --git a/msd/inc/runtime.php b/msd/inc/runtime.php new file mode 100644 index 0000000..bc5ea16 --- /dev/null +++ b/msd/inc/runtime.php @@ -0,0 +1,150 @@ + 30) { + $config['max_execution_time'] = 30; +} +$config['upload_max_filesize'] = get_cfg_var('upload_max_filesize'); +$config['disabled'] = get_cfg_var('disable_functions'); +$config['phpextensions'] = implode(', ', get_loaded_extensions()); +$m = trim(str_replace('M', '', ini_get('memory_limit'))); +// fallback if ini_get doesn't work +if (0 == intval($m)) { + $m = trim(str_replace('M', '', get_cfg_var('memory_limit'))); +} +$config['php_ram'] = $m; + +//Ist zlib moeglich? +$p1 = explode(', ', $config['phpextensions']); +$p2 = explode(',', str_replace(' ', '', $config['disabled'])); + +$config['zlib'] = (in_array('zlib', $p1) && (!in_array('gzopen', $p2) || !in_array('gzwrite', $p2) || !in_array('gzgets', $p2) || !in_array('gzseek', $p2) || !in_array('gztell', $p2))); + +//Tuning-Ecke +$config['tuning_add'] = 1.1; +$config['tuning_sub'] = 0.9; +$config['time_buffer'] = 0.75; //max_zeit= $config['max_execution_time']*$config['time_buffer'] +$config['perlspeed'] = 10000; //Anzahl der Datensaetze, die in einem Rutsch gelesen werden +$config['ignore_enable_keys'] = 0; + +//Bausteine +$config['homepage'] = 'http://foren.myoos.de/viewforum.php?f=40'; + +$nl = "\n"; +$mysql_commentstring = '--'; + +//config-Variablen, die nicht gesichert werden sollen +$config_dontsave = [ + 'homepage', + 'max_execution_time', + 'disabled', + 'phpextensions', + 'php_ram', + 'zlib', + 'tuning_add', + 'tuning_sub', + 'time_buffer', + 'perlspeed', + 'cron_configurationfile', + 'dbconnection', + 'version', + 'mysql_possible_character_sets', + 'mysql_standard_character_set', + 'config_file', + 'upload_max_filesize', + 'mysql_can_change_encoding', + 'cron_samedb', + 'paths', + 'files', +]; + +$dontBackupDatabases = ['mysql', 'information_schema']; + +// Automatisches entfernen von Slashes und Leerzeichen vorn und hinten abschneiden +$_POST = trim_deep($_POST); +$_GET = trim_deep($_GET); + +function v($t) +{ + echo '
'; + if (is_array($t) || is_object($t)) { + echo '
';
+        print_r($t);
+        echo '
'; + } else { + echo $t; + } +} + +function getServerProtocol() +{ + return (isset($_SERVER['HTTPS']) && 'on' == strtolower($_SERVER['HTTPS'])) ? 'https://' : 'http://'; +} diff --git a/msd/inc/sql_importexport.php b/msd/inc/sql_importexport.php new file mode 100644 index 0000000..1e5cb96 --- /dev/null +++ b/msd/inc/sql_importexport.php @@ -0,0 +1,290 @@ +'.$lang['L_IMPORT_NOTABLE'].''; + } else { + if (0 == $_POST['import_source']) { + //Import aus textbox + $sql['import']['csv'] = explode($sql['import']['endline'], $sql['import']['text']); + } else { + if (!isset($_FILES['upfile']['name']) || empty($_FILES['upfile']['name'])) { + $aus .= ''.$lang['L_FM_UPLOADFILEREQUEST'].''; + } else { + $fn = $_FILES['upfile']['tmp_name']; + + $sql['import']['csv'] = ('.gz' == substr($_FILES['upfile']['name'], -3)) ? gzfile($fn) : file($fn); + $sql['import']['text'] = implode('', $sql['import']['csv']); + $aus .= ''.$lang['L_SQL_UPLOADEDFILE'].''.$_FILES['upfile']['name'].'   '.byte_output(filesize($_FILES['upfile']['tmp_name'])).''; + } + } + if (is_array($sql['import']['csv'])) { + $aus .= DoImport(); + } else { + $aus .= '
'.$lang['L_CSV_NODATA'].''; + } + } + } + $impaus = $aus; + + $impaus .= '
'.$nl; + $impaus .= ''; + $impaus .= ''.$lang['L_EXPORT'].''; + $impaus .= '
'.sprintf($lang['L_SQL_IMPORT'], $databases['Name'][$dbid]).'
'; + $impaus .= ''.$nl; + + $impaus .= ''; + + $impaus .= ''; + + $impaus .= ''; + + $impaus .= '
'.$nl; + $impaus .= $lang['L_IMPORTOPTIONS'].''.$lang['L_CSVOPTIONS'].'
'.$nl; + $impaus .= ''.$nl; + $impaus .= ''.$nl; + $impaus .= ''.$nl; + $impaus .= ''.$nl; + $impaus .= ''.$nl; + $impaus .= ''.$nl; + $impaus .= '
'.$lang['L_IMPORTTABLE'].'
'.$lang['L_IMPORTSOURCE'].''.$lang['L_FROMTEXTBOX'].'
'.$nl; + $impaus .= ''.$lang['L_FROMFILE'].'
'.$lang['L_EMPTYTABLEBEFORE'].'
'.$lang['L_CREATEAUTOINDEX'].'
'.$nl; + + $impaus .= '
'.$nl; + + $impaus .= ''.$nl; + $impaus .= ''.$nl; + $impaus .= ''.$nl; + $impaus .= ''.$nl; + $impaus .= ''.$nl; + $impaus .= ''.$nl; + $impaus .= ''.$nl; + $impaus .= '
'.$lang['L_CSV_NAMEFIRSTLINE'].'
'.$lang['L_CSV_FIELDSEPERATE'].'
'.$lang['L_CSV_FIELDSENCLOSED'].'
'.$lang['L_CSV_FIELDSESCAPE'].'
'.$lang['L_CSV_EOL'].'
'.$lang['L_CSV_NULL'].'
'.$nl; + + $impaus .= '
'.$lang['L_CSV_FILEOPEN'].':   + '; + $impaus .= '
'.$nl; + + $impaus .= '

 

'.$lang['L_IMPORT'].':
'.$nl; + + $impaus .= ''.$nl; + + echo $impaus.$nl; +} else { + //EXPORT + $tables = 0; + $tblstr = ''; + $sql['export']['db'] = $db; + + if (isset($_POST['f_export_submit'])) { + //echo '
'.print_r($_POST,true).'

'; + $sql['export']['header_sent'] = ''; + $sql['export']['lines'] = 0; + $sql['export']['format'] = $_POST['f_export_format']; + $sql['export']['ztrenn'] = $_POST['f_export_csvztrenn']; + $sql['endline']['ztrenn'] = $sql['export']['ztrenn']; + if (0 == $sql['export']['format']) { + //CSV + $format = 0; + $sql['export']['trenn'] = $_POST['f_export_csvtrenn']; + $sql['export']['enc'] = $_POST['f_export_csvenc']; + $sql['export']['esc'] = $_POST['f_export_csvesc']; + if (empty($sql['export']['endline'])) { + $sql['export']['endline'] = $nl; + } else { + $sql['export']['endline'] = str_replace('\\r', "\015", $sql['export']['endline']); + $sql['export']['endline'] = str_replace('\\n', "\012", $sql['export']['endline']); + $sql['export']['endline'] = str_replace('\\t', "\011", $sql['export']['endline']); + } + $sql['export']['endline'] = str_replace('\\t', "\011", $sql['export']['endline']); + } elseif (1 == $sql['export']['format']) { + //EXCEL + $format = 1; + $sql['export']['trenn'] = ','; + $sql['export']['enc'] = '"'; + $sql['export']['esc'] = '"'; + $sql['export']['endline'] = "\015\012"; + } elseif (3 == $sql['export']['format']) { + //EXCEL 2003 + $format = 1; + $sql['export']['trenn'] = ';'; + $sql['export']['enc'] = '"'; + $sql['export']['esc'] = '"'; + $sql['export']['endline'] = "\015\012"; + } elseif (4 == $sql['export']['format']) { + //XML + $format = 4; + CheckcsvOptions(); + } elseif (5 == $sql['export']['format']) { + //HTML + $format = 5; + CheckcsvOptions(); + } + if ($format < 3) { + $sql['export']['null'] = $_POST['f_export_csvnull'.$format]; + } + $sql['export']['namefirstline'] = (isset($_POST['f_export_namefirstline'.$format])) ? $_POST['f_export_namefirstline'.$format] : 0; + + $sql['export']['sendfile'] = $_POST['f_export_sendresult']; + $sql['export']['compressed'] = (isset($_POST['f_export_compressed'])) ? $_POST['f_export_compressed'] : 0; + + $sql['export']['exportfile'] = ''; + $sql['export']['xmlstructure'] = (isset($_POST['f_export_xmlstructure'])) ? $_POST['f_export_xmlstructure'] : 0; + $sql['export']['htmlstructure'] = (isset($_POST['f_export_htmlstructure'])) ? $_POST['f_export_htmlstructure'] : 0; + + //ausgewählte Tabellen + if (isset($_POST['f_export_tables'])) { + $sql['export']['tables'] = $_POST['f_export_tables']; + } + } else { + CheckcsvOptions(); + } + + //Tabellenliste + $sqlt = "SHOW TABLE STATUS FROM `$db`"; + $res = mod_query($sqlt); + if ($res) { + $sql['export']['tablecount'] = mysqli_num_rows($res); + $sql['export']['recordcount'] = 0; + for ($i = 0; $i < $sql['export']['tablecount']; ++$i) { + $row = mysqli_fetch_array($res); + $tblstr .= ''."\n"; + $sql['export']['recordcount'] += $row['Rows']; + } + } + + $exaus = $aus.'

'.sprintf($lang['L_SQL_EXPORT'], $databases['Name'][$dbid]).'

'; + + $exaus .= '
'.$nl; + $exaus .= ''.$lang['L_IMPORT'].''; + $exaus .= '
'.sprintf($lang['L_SQL_EXPORT'], $databases['Name'][$dbid]).'
'; + $exaus .= ''.$nl; + $exaus .= ''; + $exaus .= ''; + $exaus .= ''; + + $exaus .= '
'.$lang['L_TABLES'].''.$lang['L_EXPORTOPTIONS'].''.$lang['L_EXPORT'].'
'.$sql['export']['tablecount'].' '.$lang['L_TABLES'].', '.$sql['export']['recordcount'].' '.$lang['L_RECORDS'].''; + $exaus .= '   '.$lang['L_ALL'].'  '.$lang['L_NONE'].''.$nl; + + $exaus .= '

'.$nl; + $exaus .= '
'.$nl; + $exaus .= ''.$nl; + $exaus .= ''.'CSV'.'   '.$nl; + $exaus .= ''.'Excel'.'   '.$nl; + $exaus .= ''.$lang['L_EXCEL2003'].'
'.$nl; + $exaus .= ''.'XML'.'   '.$nl; + $exaus .= ''.'HTML'.'

'.$nl; + $exaus .= '
CSV-Optionen'.$nl; + $exaus .= ''.$nl; + $exaus .= ''.$nl; + $exaus .= ''.$nl; + $exaus .= ''.$nl; + $exaus .= ''.$nl; + $exaus .= '
'.$nl; + $exaus .= ''.$lang['L_CSV_NAMEFIRSTLINE'].'
'.$lang['L_CSV_FIELDSEPERATE'].'
'.$lang['L_CSV_FIELDSENCLOSED'].'
'.$lang['L_CSV_FIELDSESCAPE'].'
'.$lang['L_CSV_EOL'].'
'.$lang['L_CSV_NULL'].'
'.$nl; + + $exaus .= '
Excel-Optionen'.$nl; + $exaus .= ''.$nl; + $exaus .= '
'; + $exaus .= ''.$lang['L_CSV_NAMEFIRSTLINE'].'
'.$lang['L_CSV_NULL'].'
'.$nl; + + $exaus .= '
XML-Optionen'; + $exaus .= ''; + $exaus .= '
mit Struktur
'.$nl; + + $exaus .= '
HTML-Optionen'; + $exaus .= ''; + $exaus .= '
mit Struktur
'.$nl; + + $exaus .= '
'.$nl; + $exaus .= ''.$lang['L_SHOWRESULT'].'
'.$nl; + $exaus .= ''.$lang['L_SENDRESULTASFILE'].'
'.$nl; + $exaus .= '
'.$lang['L_COMPRESSED'].'

'.$nl; + + $exaus .= '
'.$nl; + $exaus .= '
'.$nl; + + $exaus .= ''.$nl; + + if (!$download) { + echo $exaus.$nl; + } + if (isset($_POST['f_export_submit']) && isset($sql['export']['tables'])) { + if (!$download) { + echo '

'.$lang['L_EXPORT'].':zeige in neuem Fenster

'.$nl; + echo ''.$lang['L_EXPORTFINISHED'].'  '.sprintf($lang['L_EXPORTLINES'], $sql['export']['lines']).$nl; + } else { + exit(); + } + } +} diff --git a/msd/inc/sql_tools.php b/msd/inc/sql_tools.php new file mode 100644 index 0000000..b301863 --- /dev/null +++ b/msd/inc/sql_tools.php @@ -0,0 +1,197 @@ +'.$lang['L_TOOLS'].''; +if (isset($_POST['dbdosubmit'])) { + $newname = $_POST['newname']; + $db_index = $_POST['db_index']; + $db_action = $_POST['db_action']; + $changed = false; + $ausgabe = $out = ''; + switch ($db_action) { + case 'drop': + if (MOD_DoSQL('DROP DATABASE `'.$databases['Name'][$db_index].'`')) { + echo SQLOutput($out, '

'.$lang['L_DB'].' `'.$databases['Name'][$db_index].'` '.$lang['L_SQL_DELETED'].'

'); + $changed = true; + } + break; + case 'empty': + EmptyDB($databases['Name'][$db_index]); + echo SQLOutput($out, '

'.$lang['L_DB'].' `'.$databases['Name'][$db_index].'` '.$lang['L_SQL_WASEMPTIED'].'.

'); + break; + case 'rename': + $dbold = $databases['Name'][$db_index]; + if (DB_Copy($dbold, $newname, 1)) { + echo SQLOutput($out, '

'.$lang['L_DB'].' `'.$dbold.'` '.$lang['L_SQL_RENAMEDTO'].' `'.$newname.'`.

'); + $changed = true; + } + break; + case 'copy': + $dbold = $databases['Name'][$db_index]; + if (DB_Copy($dbold, $newname)) { + $changed = true; + echo SQLOutput($out, '

'.sprintf($lang['L_SQL_DBCOPY'], $dbold, $newname).'

'); + } + break; + case 'structure': + if (DB_Copy($databases['Name'][$db_index], $newname, 0, 0)) { + $changed = true; + echo SQLOutput($out, '

'.sprintf($lang['L_SQL_DBSCOPY'], $databases['Name'][$db_index], $newname).'

'); + } + break; + case 'rights': + break; + } + + if (true == $changed) { + SetDefault(); + include $config['files']['parameter']; + echo ''; + } +} +if (isset($_POST['dbwantaction'])) { + if (isset($_POST['db_createnew'])) { + $newname = trim($_POST['db_create']); + if (!empty($newname)) { + $sqlc = "CREATE DATABASE `$newname`"; + $col = (MOD_NEW_VERSION) ? $_POST['db_collate'] : ''; + if (isset($_POST['db_default_charset']) && intval(substr(MOD_NEW_VERSION, 0, 1)) > 3) { + $db_default_charset_string = $config['mysql_possible_character_sets'][$_POST['db_default_charset']]; + $db_default_charset = explode(' ', $db_default_charset_string); + if (isset($db_default_charset[0])) { + $sqlc .= ' DEFAULT CHARACTER SET `'.$db_default_charset[0].'`'; + } + } + $db_default_collation = @explode('|', $col); + if (isset($db_default_collation[1])) { + $sqlc .= ' COLLATE `'.$db_default_collation[1].'`'; + } + + if (mod_query($sqlc)) { + echo $lang['L_DB']." `$newname` ".$lang['L_SQL_WASCREATED'].'.
'; + SetDefault(); + include $config['files']['parameter']; + echo ''; + } + } + } + $db_action = $newname = ''; + $db_index = -1; + for ($i = 0; $i < count($databases['Name']); ++$i) { + if (isset($_POST['db_do_'.$i])) { + $newname = $_POST['db_rename'.$i]; + $db_index = $i; + $db_action = $_POST['db_do_action_'.$i]; + break; + } + } + if ('' != $db_action) { + echo '
'; + echo '
+ + + '; + switch ($db_action) { + case 'drop': + echo ''.sprintf($lang['L_ASKDBDELETE'], $databases['Name'][$i]).'

'; + echo ''; + break; + case 'empty': + echo ''.sprintf($lang['L_ASKDBEMPTY'], $databases['Name'][$i]).'

'; + echo ''; + break; + case 'rename': + echo ''.$lang['L_SQL_RENAMEDB'].' `'.$databases['Name'][$db_index].'` '.$lang['L_IN'].' `'.$newname.'`

'; + if ('' == $newname) { + echo '

'.$lang['L_SQL_NAMEDEST_MISSING'].'

'; + } else { + echo ''; + } + break; + case 'copy': + echo ''.sprintf($lang['L_ASKDBCOPY'], $databases['Name'][$db_index], $newname).'

'; + if ('' == $newname) { + echo '

'.$lang['L_SQL_NAMEDEST_MISSING'].'

'; + } else { + echo ''; + } + break; + case 'structure': + echo ''.$lang['L_FM_ASKDBCOPY1'].'`'.$databases['Name'][$db_index].'`'.$lang['L_FM_ASKDBCOPY2'].'`'.$newname.'`'.$lang['L_FM_ASKDBCOPY3'].'

'; + if ('' == $newname) { + echo '

'.$lang['L_SQL_NAMEDEST_MISSING'].'

'; + } else { + echo ''; + } + break; + case 'rights': + break; + } + echo '

'; + } +} + +echo '
'; +echo '
'; +echo ''; +echo ''; + +echo ''; + +echo ''; +echo ''; +echo '
'.$lang['L_CREATE_DATABASE'].'
Name:
'.$lang['L_DEFAULT_CHARSET'].':
'.$lang['L_COLLATION'].'
'; + +echo '
'; +echo ''; +for ($i = 0; $i < count($databases['Name']); ++$i) { + $cl = ($i % 2) ? 'dbrow' : 'dbrow1'; + echo ($i == $databases['db_selected_index']) ? '' : ''; + echo ''; + echo ''; +} + +echo '
'.$lang['L_DBS'].''.$lang['L_SQL_ACTIONS'].'
'.$databases['Name'][$i].''; + echo '  '; + echo "\n\n".'  '; + + echo '  
'; diff --git a/msd/inc/sqlbrowser/mysql_search.php b/msd/inc/sqlbrowser/mysql_search.php new file mode 100644 index 0000000..348ab43 --- /dev/null +++ b/msd/inc/sqlbrowser/mysql_search.php @@ -0,0 +1,387 @@ + count($tables) - 1) { + $table_selected = 0; +} + +$offset = (isset($_POST['offset'])) ? intval($_POST['offset']) : 0; + +$tablename = isset($_GET['tablename']) ? urldecode($_GET['tablename']) : ''; + +// Delete +if (isset($_GET['mode']) && 'kill' == $_GET['mode'] && $rk > '') { + // echo "
RK ist: ".$rk."

"; + $sqlk = "DELETE FROM `$tablename` WHERE ".$rk.' LIMIT 1'; + // echo $sqlk; + $res = mod_query($sqlk); + // echo "
".$res; + $aus .= '

'.$lang['L_SQL_RECORDDELETED'].'

'; +} + +function mysqli_search($db, $tabelle, $suchbegriffe, $suchart, $offset = 0, $anzahl_ergebnisse = 20, $auszuschliessende_tabellen = '') +{ + global $tables, $config, $lang; + + $ret = false; + $link = mod_mysqli_connect(); + if (sizeof($tables) > 0) { + $suchbegriffe = trim(str_replace('*', '', $suchbegriffe)); + $suchworte = explode(' ', $suchbegriffe); + if (($suchbegriffe > '') && (is_array($suchworte))) { + // Remove empty entries (due to double spaces) + $anzahl_suchworte = sizeof($suchworte); + for ($i = 0; $i < $anzahl_suchworte; ++$i) { + if ('' == trim($suchworte[$i])) { + unset($suchworte[$i]); + } + } + + $bedingung = []; + $where = ''; + $felder = []; + + // Determine fields + $sql = 'SHOW COLUMNS FROM `'.$db.'`.`'.$tables[$tabelle].'`'; + $res = mysqli_query($link, $sql); + if (false === !$res) { + // Determine fields of the table + while ($row = mysqli_fetch_object($res)) { + $felder[] = $row->Field; + } + } + + $feldbedingung = ''; + if ('CONCAT' == $suchart) { + if (count($felder) > 0) { + // Build Concat-String + $concat = implode('`),LOWER(`', $felder); + $concat = 'CONCAT_WS(\'\',LOWER(`'.$concat.'`))'; + $where = ''; + foreach ($suchworte as $suchbegriff) { + $where .= $concat.' LIKE \'%'.strtolower($suchbegriff).'%\' AND '; + } + $where = substr($where, 0, -4); // Remove last AND + $sql = 'SELECT * FROM `'.$db.'`.`'.$tables[$tabelle].'` WHERE '.$where.' LIMIT '.$offset.','.$anzahl_ergebnisse; + } else { + $_SESSION['mysql_search']['suchbegriffe'] = ''; + exit(sprintf($lang['L_ERROR_NO_FIELDS'], $tabelle)); + } + } else { + $pattern = '`{FELD}` LIKE \'%{SUCHBEGRIFF}%\''; + + if (count($felder) > 0) { + foreach ($felder as $feld) { + unset($feldbedingung); + foreach ($suchworte as $suchbegriff) { + $suchen = [ + '{FELD}', + '{SUCHBEGRIFF}', + ]; + $ersetzen = [ + $feld, + $suchbegriff, + ]; + $feldbedingung[] = str_replace($suchen, $ersetzen, $pattern); + } + $bedingung[] = '('.implode(' '.$suchart.' ', $feldbedingung).') '; + } + } else { + $_SESSION['mysql_search']['suchbegriffe'] = ''; + exit(sprintf($lang['L_ERROR_NO_FIELDS'], $tabelle)); + } + $where = implode(' OR ', $bedingung); + $sql = 'SELECT * FROM `'.$db.'`.`'.$tables[$tabelle].'` WHERE ('.$where.') LIMIT '.$offset.','.$anzahl_ergebnisse; + } + } else { + $sql = 'SELECT * FROM `'.$db.'`.`'.$tables[$tabelle].'` LIMIT '.$offset.','.$anzahl_ergebnisse; + } + + $res = mysqli_query($link, $sql); + if (false === !$res) { + while ($row = mysqli_fetch_array($res, MYSQLI_ASSOC)) { + // Mark hits + foreach ($row as $key => $val) { + foreach ($suchworte as $suchbegriff) { + $row[$key] = markiere_suchtreffer($suchbegriff, $row[$key]); + } + $row[$key] = ersetze_suchtreffer($row[$key]); + } + $ret[] = $row; + } + } + } + return $ret; +} + +// Marks the search string with a code (ASCII 01/02) +// - if not found : returns the original string +function markiere_suchtreffer($suchbegriff, $suchstring) +{ + $str = strtolower($suchstring); + $suchbegriff = strtolower($suchbegriff); + if ((strlen($str) > 0) && (strlen($suchbegriff) > 0)) { + // Determine hit position + $offset = 0; + $trefferpos = 0; + while (($offset <= strlen($str))) { + // If only the first hit is to be marked, the line must read as follow + // while ( ($offset<=strlen($str)) || ($in_html==false) ) + for ($offset = $trefferpos; $offset <= strlen($str); ++$offset) { + $start = strpos($str, $suchbegriff, $offset); + if (false === $start) { + $offset = strlen($str) + 1; + } else { + if ($offset <= strlen($str)) { + //Treffer überprüfen + $in_html = false; + // Steht die Fundstelle zwischen < und > (also im HTML-Tag) ? + for ($position = $start; $position >= 0; --$position) { + if ('>' == substr($str, $position, 1)) { + $in_html = false; + $position = -1; // Schleife verlassen + } + if ('<' == substr($str, $position, 1)) { + $in_html = true; + $position = -1; // Schleife verlassen + } + } + if ($in_html) { + for ($position2 = $start; $position2 < strlen($str); ++$position2) { + if ('<' == substr($str, $position2, 1)) { + $position2 = strlen($str) + 1; + } + if ('>' == substr($str, $position2, 1)) { + $in_html = true; + $position2 = strlen($str) + 1; + $offset = strlen($str) + 1; + } + } + } + if (!$in_html) { + $ersetzen = substr($suchstring, $start, strlen($suchbegriff)); + $str = substr($suchstring, 0, $start); + $str .= chr(1).$ersetzen.chr(2); + $str .= substr($suchstring, ($start + strlen($ersetzen)), (strlen($suchstring) - strlen($ersetzen))); + $suchstring = $str; + } + if ($in_html) { + $trefferpos = $start + 1; + $offset = $trefferpos; + } + } + $offset = $start + 1; + } + } + } + } + return $suchstring; +} + +// Ersetzt die Codes letztlich durch die Fontangabe +function ersetze_suchtreffer($text) +{ + $such = [ + chr(1), + chr(2), ]; + $ersetzen = [ + '', + '', ]; + return str_replace($such, $ersetzen, htmlspecialchars($text)); +} + +$suchbegriffe = trim($suchbegriffe); // Leerzeichen vorne und hinten wegschneiden +if (isset($_POST['reset'])) { + $suchbegriffe = ''; + $_SESSION['mysql_search']['suchbegriffe'] = ''; + $suchart = 'AND'; + $_SESSION['mysql_search']['suchart'] = 'AND'; + $table_selected = 0; + $_SESSION['mysql_search']['table_selected'] = 0; +} +$showtables = 0; // Anzeige der Tabellendaten im restlichen SQL-Browser ausschalten + +// Fix bis zur kompletten Umstellung auf Templates +echo $aus; +$aus = ''; + +$anzahl_tabellen = sizeof($tables); +$table_options = ''; +if ($anzahl_tabellen > 0) { + for ($i = 0; $i < $anzahl_tabellen; ++$i) { + if (isset($tables[$i])) { + $table_options .= '
` '; +$lang['L_DUMP_ENDERGEBNIS'] = 'محتويات الملف %s مع الجداول %s السجلات.
'; +$lang['L_MAILERROR'] = 'فشل ارسال البريد الالكتروني!'; +$lang['L_EMAILBODY_ATTACH'] = 'المرفق يحتوي على ملف النسخ الاحتياطي لقاعدة البيانات MySQL.
نسخ احتياطي لقاعدة البيانات `%s` +

تم انشاء الملف التالي:

%s

حظا موفقا

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_MP_NOATTACH'] = 'تم انشاء النسخ الاحتياطي المتعدد.
ملفات النسخ الاحتياطي لا يمكن ارسالها البريد الالكتروني!
النسخ الاحتياطي لقاعدة البيانات `%s` +

الملفات التالية تم انشئت:

%s +

حظا موفقا

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_MP_ATTACH'] = 'تم انشاء النسخ الاحتياطي المتعدد.
فواصل بين ملفات النسخ الاحتياطي عند ارسالها بالبريد الالكتروني.
النسخ الاحتياطي لقاعدة البيانات `%s` +

الملفات التالية انشئت:

%s

حظا موفقا

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_FOOTER'] = '`

حظا موفقا

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_TOOBIG'] = 'الحجم الاقصى للملف تجاوز الحد المسموح به %s لا يمكن ارسال المرفقات الى البريد الالكتروني .
النسخ الاحتياطي لقاعدة البيانات `%s` +

الملفات التالية انشئت:

%s +

حظا موفقا

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_NOATTACH'] = 'الملفات لايمكن ارسالها بالبريد الالكتروني!
النسخ الاحتياطي لقاعدة البيانات `%s` +

الملفات التالية انشئت:

%s +

حظا موفقا

MyOOS [Dumper]
'; +$lang['L_EMAIL_ONLY_ATTACHMENT'] = ' ... المرفقات فقط.'; +$lang['L_TABLESELECTION'] = 'تحديد جدول'; +$lang['L_SELECTALL'] = 'تحديد الكل'; +$lang['L_DESELECTALL'] = 'الغاء تحديد الكل'; +$lang['L_STARTDUMP'] = 'بدء النسخ الاحتياطي'; +$lang['L_LASTBUFROM'] = 'من اخر تحديث'; +$lang['L_NOT_SUPPORTED'] = 'النسخ الاحتياطي هذا لا يدعم هذه الوظيفة.'; +$lang['L_MULTIDUMP'] = 'متعدد dump: النسخ الاحتياطي لـ %d لقاعدة البيانات انتهى.'; +$lang['L_FILESENDFTP'] = 'ارسال الملف عن طريق via FTP... رجاء كن صبورا. '; +$lang['L_FTPCONNERROR'] = ' لم يتم تأسيسه FTP اتصال مع ! الاتصال مع '; +$lang['L_FTPCONNERROR1'] = ' اسم مستخدم '; +$lang['L_FTPCONNERROR2'] = ' غير ممكن'; +$lang['L_FTPCONNERROR3'] = 'FTP الارسال فشل! '; +$lang['L_FTPCONNECTED1'] = 'الربط مع '; +$lang['L_FTPCONNECTED2'] = ' مفتوح '; +$lang['L_FTPCONNECTED3'] = ' تمت عملية النقل بنجاح'; +$lang['L_FILESENDSFTP'] = 'ارسال الملف عن طريق via FTP... رجاء كن صبورا. '; +$lang['L_SFTPCONNERROR'] = ' لم يتم تأسيسه FTP اتصال مع ! الاتصال مع '; +$lang['L_NR_TABLES_SELECTED'] = '- مع %s اختر الجداول'; +$lang['L_NR_TABLES_OPTIMIZED'] = '%s تم اصلاح وتحسين الجداول.'; +$lang['L_DUMP_ERRORS'] = '

%s حدثت اخطاء: عرض

+ + +'; +$lang['L_FATAL_ERROR_DUMP'] = "خطأ فادح: انشاء - بيانات من الجدول '%s' في قاعدة البيانات '%s' لا يمكن القراءة!
+قم بفحص المشاكل في هذا الجدول."; diff --git a/msd/language/ar/lang_filemanagement.php b/msd/language/ar/lang_filemanagement.php new file mode 100644 index 0000000..204d8cc --- /dev/null +++ b/msd/language/ar/lang_filemanagement.php @@ -0,0 +1,80 @@ +%s"'; +$lang['L_DELETE_FILE_ERROR'] = 'Error deleting file "%s"!'; +$lang['L_FM_DUMP_HEADER'] = 'نسخ احتياطي'; +$lang['L_DOCRONBUTTON'] = 'تشغيل Perl Cron في المخطوطه'; +$lang['L_DOPERLTEST'] = 'فحص وحدات Perl '; +$lang['L_DOSIMPLETEST'] = 'فحص Perl'; +$lang['L_PERLOUTPUT1'] = 'الدخول في crondump.pl مسموح_عن طريق_التهيئة في الدليل'; +$lang['L_PERLOUTPUT2'] = 'عنوان المتصفح او المشغل الخارجي لوظائف Cron '; +$lang['L_PERLOUTPUT3'] = 'سطر الاوامر في الشل او علامة التبويب Cron'; +$lang['L_RESTORE_OF_TABLES'] = 'اختر الجداول التي تريد استعادتها'; +$lang['L_CONVERTER'] = 'تحويل النسخ الاحتياطي'; +$lang['L_CONVERT_FILE'] = 'الملف المحول'; +$lang['L_CONVERT_FILENAME'] = 'اسم الملف الوجهة (بدون الامتداد)'; +$lang['L_CONVERTING'] = 'تحويل'; +$lang['L_CONVERT_FILEREAD'] = "قراءة الملف '%s'"; +$lang['L_CONVERT_FINISHED'] = "التحويل انتهى, '%s' تم التحويل بنجاح."; +$lang['L_NO_MOD_BACKUPFILE'] = 'النسخ الاحتياطي لمخطوطات اخرى'; +$lang['L_MAX_UPLOAD_SIZE'] = 'الحد الاقصى لحجم الملف'; +$lang['L_MAX_UPLOAD_SIZE_INFO'] = 'إذا كان ملف النسخ الاحتياطي هو اكبر من الحد المسموح به أعلاه ، يجب عليك ارساله عبر برامج بروتوكول نقل الملفات اف تي بي إلى دليل "work/backup". +بعد ذلك يمكنك تحديد ملف الاستعادة ومشاهدة بداية التقدم. '; +$lang['L_ENCODING'] = 'الترميز'; +$lang['L_FM_CHOOSE_ENCODING'] = 'اختر ترميز لملف النسخ الاحتياطي'; +$lang['L_CHOOSE_CHARSET'] = 'MyOOS [Dumper] لا يمكن الكشف عن الترميز تلقائيالملف النسخ الاحتياطي. +
يجب عليك اختيار الاحرف التي تم حفظ النسخ الاحتياطي بها. +
اذا واجهت اي مشكلة مع بعض الاحرف في الاستعادة يمكنك تكرار عملية النسخ الاحتياطي واختيار مجموعة اخرى. +
حظا موفقا. ;)'; +$lang['L_DOWNLOAD_FILE'] = 'Download file'; +$lang['L_BACKUP_NOT_POSSIBLE'] = 'A backup of the system database `%s` is not possible!'; diff --git a/msd/language/ar/lang_help.php b/msd/language/ar/lang_help.php new file mode 100644 index 0000000..5fbcc1c --- /dev/null +++ b/msd/language/ar/lang_help.php @@ -0,0 +1,37 @@ +تم اكتمال التركيب --> ابدأ MyOOS [Dumper]
'; +$lang['L_INSTALL_TOMENU'] = 'عوده الى القائمة الرئيسة'; +$lang['L_INSTALLMENU'] = 'القائمة الرئيسة'; +$lang['L_STEP'] = 'خطوة '; +$lang['L_INSTALL'] = 'التركيب'; +$lang['L_UNINSTALL'] = 'مسح'; +$lang['L_TOOLS'] = 'الادوات'; +$lang['L_EDITCONF'] = 'تعديل الدليل'; +$lang['L_OSWEITER'] = 'مواصلة بدون حفظ'; +$lang['L_ERRORMAN'] = 'خطأ اثناء عملية حفظ البيانات!
الرجاء تعديل الملف '; +$lang['L_MANUELL'] = 'يدوي'; +$lang['L_CREATEDIRS'] = 'انشاء الادلة'; +$lang['L_INSTALL_CONTINUE'] = 'استمر بعملية التركيب'; +$lang['L_CONNECTTOMYSQL'] = 'ربط الى MySQL '; +$lang['L_DBPARAMETER'] = 'قاعدة البيانات Parameters'; +$lang['L_CONFIGNOTWRITABLE'] = 'لا يمكن الكتابة للملف "config.php". +الرجاء استخدام برامج بروتوكول نقل الملفات الخاصة بك واعط الملف التصريح0777.'; +$lang['L_DBCONNECTION'] = 'الاتصال بقاعدة البيانات'; +$lang['L_CONNECTIONERROR'] = 'خطأ غير قادر على الربط.'; +$lang['L_CONNECTION_OK'] = 'تم تأسيس الاتصال بقاعدة البيانات.'; +$lang['L_SAVEANDCONTINUE'] = 'حفظ ومتابعة التركيب'; +$lang['L_CONFBASIC'] = 'اساسي Parameter'; +$lang['L_INSTALL_STEP2FINISHED'] = 'تم الوصول الى قاعدة البيانات بنجاح.'; +$lang['L_INSTALL_STEP2_1'] = 'متابعة التركيب بالإعدادات الافتراضية'; +$lang['L_LASTSTEP'] = 'انتهى التركيب'; +$lang['L_FTPMODE'] = 'انشاء ادلة ضرورية في النمط الآمن'; +$lang['L_IDOMANUAL'] = 'انشاء الدليل الخاص بي'; +$lang['L_DOFROM'] = 'بداية من'; +$lang['L_FTPMODE2'] = 'تهيئة مع بروتوكول نقل الملفات FTP:'; +$lang['L_CONNECT'] = 'ربط'; +$lang['L_DIRS_CREATED'] = 'انشاء الادلة بشكل صحيح.'; +$lang['L_CONNECT_TO'] = 'ربط الى'; +$lang['L_CHANGEDIR'] = 'تغيير مياشر'; +$lang['L_CHANGEDIRERROR'] = ' لا يمكن التغيير مباشرة '; +$lang['L_FTP_OK'] = 'FTPيفضل برتكول '; +$lang['L_SFTP_OK'] = 'FTPيفضل برتكول '; +$lang['L_CREATEDIRS2'] = 'انشاء الادلة'; +$lang['L_FTP_NOTCONNECTED'] = 'FTP لم يتم انشاء برتكول نقل الملفات!'; +$lang['L_CONNWITH'] = 'اتصال مع'; +$lang['L_ASUSER'] = 'اسم المستخدم'; +$lang['L_NOTPOSSIBLE'] = 'غير ممكن'; +$lang['L_DIRCR1'] = 'انشاء في دليل العمل'; +$lang['L_DIRCR2'] = 'انشاء في دليل الاسناد'; +$lang['L_DIRCR4'] = 'انشاء في السجل'; +$lang['L_DIRCR5'] = 'انشاء في configurationdir'; +$lang['L_INDIR'] = 'الآن في '; +$lang['L_CHECK_DIRS'] = 'فحص الادلة'; +$lang['L_DISABLEDFUNCTIONS'] = 'الوظائف المعطلة'; +$lang['L_NOFTPPOSSIBLE'] = 'لا يجد لديك وظائف بروتوكول نقل الملفات FTP !'; +$lang['L_NOGZPOSSIBLE'] = 'لا يوجد لديك وظائف ضغط الملفات !'; +$lang['L_UI1'] = 'جميع أدلة العمل التي يمكن أن تحتوي النسخ الاحتياطي سيتم حذفها.'; +$lang['L_UI2'] = 'هل انت موافق على عمل ذلك?'; +$lang['L_UI3'] = 'لا, الغاء العملية'; +$lang['L_UI4'] = 'نعم ، من فضلك تابع'; +$lang['L_UI5'] = 'حذف ادلة العمل'; +$lang['L_UI6'] = 'تم حذف الجميع بنجاح.'; +$lang['L_UI7'] = 'رجاء احذف الدليل من المخطوطه'; +$lang['L_UI8'] = 'المستوى الاول الاعلى'; +$lang['L_UI9'] = 'حدث خطأ ، لم يكن من الممكن الحذف

خطأ بالدليل'; +$lang['L_IMPORT'] = 'استيراد الوظائف'; +$lang['L_IMPORT3'] = 'تحميل القوائم ...'; +$lang['L_IMPORT4'] = 'تم حفظ القوائم.'; +$lang['L_IMPORT5'] = 'ابدأ MyOOS [Dumper]'; +$lang['L_IMPORT6'] = 'تركيب القائمة'; +$lang['L_IMPORT7'] = 'ارسال القوائم'; +$lang['L_IMPORT8'] = 'عودة الى الرفع'; +$lang['L_IMPORT9'] = 'هذا ليس دليل اسناد !'; +$lang['L_IMPORT10'] = 'تم رفع القوائم بنجاح ...'; +$lang['L_IMPORT11'] = 'خطا: كانت هناك مشكلة في الكتابة الى sql_statements'; +$lang['L_IMPORT12'] = 'خطأ: كانت هناك مشكلة في الكتابة الى config.php'; +$lang['L_INSTALL_HELP_PORT'] = '(empty = Default Port)'; +$lang['L_INSTALL_HELP_SOCKET'] = '(empty = Default Socket)'; +$lang['L_TRYAGAIN'] = 'حاول مرة اخرى'; +$lang['L_SOCKET'] = 'توصيل'; +$lang['L_PORT'] = 'منفذ'; +$lang['L_FOUND_DB'] = 'العثور على قاعدة بيانات'; +$lang['L_FM_FILEUPLOAD'] = 'رفع ملف'; +$lang['L_PASS'] = 'كلمة المرور'; +$lang['L_NO_DB_FOUND_INFO'] = 'تم الاتصال بقاعدة البيانات بنجاح.
+تم قبول بيانات المستخدم الخصة بك لـ MySQL-Server.
+لم يستطيع العثور على اية قاعدة بيانات.
+البحث التلقائي لا يمكن من قبل الخادم.
+انت يجب أن تدخل اسم قاعدة البيانات يدويا بعد الانتهاء من التركيب. +اضغط هنا "configuration" "Connection Parameter - display" وتدخل اسم قاعدة البيانات هناك. + +'; +$lang['L_SAFEMODEDESC'] = 'السبب PHP يعمل في الوضع الآمن فأنت تحتاج لإنشاء الادلة التالية يدويا بأستخدام برامج نقل الملفات FTP-Programms: + + +'; +$lang['L_ENTER_DB_INFO'] = 'First click the button "Connect to MySQL". Only if no database could be detected you need to provide a database name here.'; diff --git a/msd/language/ar/lang_log.php b/msd/language/ar/lang_log.php new file mode 100644 index 0000000..d72716c --- /dev/null +++ b/msd/language/ar/lang_log.php @@ -0,0 +1,9 @@ +يرجى كتابة وتهيئة الملفات التالية يدويا في المحتوى'; +$lang['L_HTACC_CHECK_ERROR'] = 'It could not be checked whether the program is protected!
The simulated external access could not be carried out.'; +$lang['L_HTACC_NOT_NEEDED'] = 'The program is protected by higher-level authorizations; local directory protection is not required.'; +$lang['L_HTACC_COMPLETE'] = 'The program is protected, the directory protection is complete.'; +$lang['L_HTACC_INCOMPLETE'] = 'The program is not protected, the directory protection is incomplete!'; +$lang['L_HTACC_PROPOSED'] = 'موصي به بشدة وللأهمية القصوي'; +$lang['L_HTACC_EDIT'] = 'تحرير .htaccess'; +$lang['L_HTACCESS18'] = 'انشاء في .htaccess '; +$lang['L_HTACCESS19'] = 'اعد التحميل '; +$lang['L_HTACCESS20'] = 'تنفيذ البرنامج النصي'; +$lang['L_HTACCESS21'] = 'اضف عملية'; +$lang['L_HTACCESS22'] = 'اجعلها قابله للتنفيذ'; +$lang['L_HTACCESS23'] = 'اداراجها في الدليل'; +$lang['L_HTACCESS24'] = 'انشاء وثيقة للخطأ'; +$lang['L_HTACCESS25'] = 'تفعيل اعادة الكتابة'; +$lang['L_HTACCESS26'] = 'تعطيل / سماح'; +$lang['L_HTACCESS27'] = 'اعادة توجيه'; +$lang['L_HTACCESS28'] = 'خطا السجل'; +$lang['L_HTACCESS29'] = 'امثلة وتفاصيل اكثر'; +$lang['L_HTACCESS30'] = 'مقدمه'; +$lang['L_HTACCESS31'] = 'عام'; +$lang['L_HTACCESS32'] = 'انتبه! فإن. htaccess يؤثر بشكل مباشر على المتصفح .
مع وجود محتوى غير صحيح ، هذه الصفحات قد لا تكون متاحة.'; +$lang['L_DISABLEDFUNCTIONS'] = 'الوظائف المعطلة'; +$lang['L_NOGZPOSSIBLE'] = 'Zlib لم يتم تركيبها ، لا يمكنك استخدام - GZip وظائف!'; +$lang['L_DELETE_HTACCESS'] = 'إزالة الحماية من الدليل (delete .htaccess)'; +$lang['L_WRONG_RIGHTS'] = "الملف او الدليل '%s' لايمكن الكتابة فيه.
+الحقوق (chmod)ليست المجموعة او ان المكلية غير صحيحة.
+استخدم برامج بروتكول FTP-لنقل ملفاتك.
+الملف او الدليل من الضروري ان يستعدا %s.
"; +$lang['L_CANT_CREATE_DIR'] = "Couldn' لا يستطيع انشاء '%s'. +من فضلك استخدم برامج برتكول FTP-لنقل الملفات."; +$lang['L_TABLE_TYPE'] = 'Type'; +$lang['L_CHECK'] = 'check'; +$lang['L_OS'] = 'Operating system'; +$lang['L_MOD_VERSION'] = 'MyOOS [Dumper] - Version'; +$lang['L_NEW_MOD_VERSION'] = 'New Version'; +$lang['L_NEW_MOD_VERSION_INFO'] = 'There is a new version of MyOOS [Dumper] available.'; +$lang['L_UPDATED_IMPORTANT'] = 'Important: Before updating, please backup your files.'; +$lang['L_UPDATE'] = 'Update now'; +$lang['L_MYSQL_VERSION'] = 'MySQL-Version'; +$lang['L_PHP_VERSION'] = 'PHP-Version'; +$lang['L_MAX_EXECUTION_TIME'] = 'Max execution time'; +$lang['L_PHP_EXTENSIONS'] = 'PHP-Extensions'; +$lang['L_MEMORY'] = 'Memory'; +$lang['L_FILE_MISSING'] = 'لا يستطيع ايجاد الملف'; +$lang['L_INSTALLING_UPDATES'] = 'Installing Updates'; +$lang['L_UPDATE_SUCCESSFUL'] = 'Update successful'; +$lang['L_UPDATE_FAILED'] = 'Update failed'; +$lang['L_UP_TO_DATE'] = 'Current Version is up to date'; diff --git a/msd/language/ar/lang_restore.php b/msd/language/ar/lang_restore.php new file mode 100644 index 0000000..00788a8 --- /dev/null +++ b/msd/language/ar/lang_restore.php @@ -0,0 +1,21 @@ +%d تم انشاء الجداول.'; +$lang['L_FILE_MISSING'] = 'لا يستطيع ايجاد الملف'; +$lang['L_RESTORE_DB'] = "قاعدة البيانات '%s' على '%s'."; +$lang['L_RESTORE_COMPLETE'] = '%s انشاء الجداول.'; +$lang['L_RESTORE_RUN1'] = '
البدء لآن %s of %s تمت الاضافة بنجاح.'; +$lang['L_RESTORE_RUN2'] = "
ابدأ الجداول '%s'اعادة.

"; +$lang['L_RESTORE_COMPLETE2'] = '%s ادراج سجلات.'; +$lang['L_RESTORE_TABLES_COMPLETED'] = 'البدء لآن %d of %d تم انشاء الجداول.'; +$lang['L_RESTORE_TOTAL_COMPLETE'] = '
تهانينا.

تمت الان استعادة قاعدة البيانات.
تم استعادة جميع ملفات النسخ الاحتياطي.

انتهت العملية. :-)'; +$lang['L_DB_SELECT_ERROR'] = '
مشكلة:
حدد قاعدة البيانات '; +$lang['L_DB_SELECT_ERROR2'] = ' فشل!'; +$lang['L_FILE_OPEN_ERROR'] = 'خطأ: لا يمكن فتح الملف.'; +$lang['L_PROGRESS_OVER_ALL'] = 'التقدم الشامل'; +$lang['L_BACK_TO_OVERVIEW'] = 'تفاصيل عامة لقاعدة البيانات'; +$lang['L_RESTORE_RUN0'] = '
البدء لآن%s تمت اضافة السجلات بنجاح.'; +$lang['L_UNKNOWN_SQLCOMMAND'] = 'مجهول SQL-امر'; +$lang['L_NOTICES'] = 'ملاحظات + +'; diff --git a/msd/language/ar/lang_sql.php b/msd/language/ar/lang_sql.php new file mode 100644 index 0000000..69dbf97 --- /dev/null +++ b/msd/language/ar/lang_sql.php @@ -0,0 +1,190 @@ +%s تم تصدير الخطوط'; +$lang['L_CSV_FIELDCOUNT_NOMATCH'] = 'احصاء الحقول لا يتطابق مع بيانات الاستيراد (%d بدلا من %d).'; +$lang['L_CSV_FIELDSLINES'] = '%d الحقول معروفه, كليا %d خطوط'; +$lang['L_CSV_ERRORCREATETABLE'] = ' `%s` حدث خطأ عند القيام بإنشاء الجدول !'; +$lang['L_FM_UPLOADFILEREQUEST'] = 'رجاء اختر الملف.'; +$lang['L_CSV_NODATA'] = 'لا توجد بيانات للاستيراد!'; +$lang['L_SQLLIB_GENERALFUNCTIONS'] = 'الوظائف العامة'; +$lang['L_SQLLIB_RESETAUTO'] = 'اعادة تلقائية-للزيادة'; +$lang['L_SQLLIB_BOARDS'] = 'القوائم'; +$lang['L_SQLLIB_DEACTIVATEBOARD'] = 'تعطيل قائمة'; +$lang['L_SQLLIB_ACTIVATEBOARD'] = 'تمكين قائمة'; +$lang['L_SQL_NOTABLESSELECTED'] = 'لم يتم اختيار اي جداول !'; +$lang['L_TOOLS'] = 'اداوات'; +$lang['L_TOOLS_TOOLBOX'] = ' اختيار قاعدة البيانات / مهام / استيراد - تصدير قاعدة بيانات '; +$lang['L_SQL_OPENFILE'] = 'فتح SQL-ملف'; +$lang['L_SQL_OPENFILE_BUTTON'] = 'ارسال'; +$lang['L_MAX_UPLOAD_SIZE'] = 'الحجم الاقصى للملف'; +$lang['L_SQL_SEARCH'] = 'بحث'; +$lang['L_SQL_SEARCHWORDS'] = 'كلمة البحث(s)'; +$lang['L_START_SQL_SEARCH'] = 'بداية البحث'; +$lang['L_RESET_SEARCHWORDS'] = 'اعد كتابة كلمات البحث'; +$lang['L_SEARCH_OPTIONS'] = 'خيارات البحث'; +$lang['L_SEARCH_RESULTS'] = 'عملية البحث "%s" في الجدول "%s" جلبت النتائج التالية'; +$lang['L_SEARCH_NO_RESULTS'] = 'عملية البحث "%s" في الجدول "%s" لم تجلب اي نتائج!'; +$lang['L_NO_ENTRIES'] = 'الجدول "%s" فارغ او انه لايمكن الوصل اليه.'; +$lang['L_SEARCH_ACCESS_KEYS'] = ' استعرض واستخدم المفاتيح التالية : forwardللامام =ALT+V, backwardsالتراجع للخلف=ALT+C'; +$lang['L_SEARCH_OPTIONS_OR'] = 'يجب ان يحتوي العمود على احدى كلمات البحث (او-بحث)'; +$lang['L_SEARCH_OPTIONS_CONCAT'] = 'الصف يجب ان يحتوي على كل كلمات البحث ولكن يمكن ان يكونوا في اي عمود(يمكن ان يستغرق بعض الوقت)'; +$lang['L_SEARCH_OPTIONS_AND'] = 'العمود يجب ان يحتوي على جميع كلمات البحث (و-البحث)'; +$lang['L_SEARCH_IN_TABLE'] = 'بحث في الجدول'; +$lang['L_ERROR_NO_FIELDS'] = 'Search error: it could not be determined which fields the table "%s" has!'; +$lang['L_SQL_EDIT_TABLESTRUCTURE'] = 'تحرير اعمدة الجدول'; +$lang['L_DEFAULT_CHARSET'] = 'الاعدادات الاصلية'; +$lang['L_TITLE_KEY_PRIMARY'] = 'المفتاح الاساسي'; +$lang['L_TITLE_KEY_UNIQUE'] = 'مفتاح فريد'; +$lang['L_TITLE_INDEX'] = 'الرئيسة'; +$lang['L_TITLE_KEY_FULLTEXT'] = 'مفتاح النص الكامل'; +$lang['L_TITLE_NOKEY'] = 'لا مفتاح'; +$lang['L_TITLE_SEARCH'] = 'بحث'; +$lang['L_TITLE_MYSQL_HELP'] = 'MySQl وثائق'; +$lang['L_TITLE_UPLOAD'] = 'ارسال ملف SQL'; +$lang['L_PRIMARYKEY_DELETED'] = 'Primary key deleted'; +$lang['L_PRIMARYKEY_NOTFOUND'] = 'Primary key not found'; +$lang['L_PRIMARYKEYS_CHANGED'] = 'Primary keys changed'; +$lang['L_PRIMARYKEYS_CHANGINGERROR'] = 'Error changing primary keys'; +$lang['L_SQL_VIEW_COMPACT'] = 'View: compact'; +$lang['L_SQL_VIEW_STANDARD'] = 'View: standard'; +$lang['L_FIELDS_OF_TABLE'] = 'Fields of table'; +$lang['L_ENGINE'] = 'Engine'; +$lang['L_USERNAME'] = 'Username'; +$lang['L_PASSWORD'] = 'Password'; +$lang['L_PASSWORD_REPEAT'] = 'Password (repeat)'; +$lang['L_INFO_SIZE'] = 'الحجم'; +$lang['L_TABLE_TYPE'] = 'Type'; +$lang['L_KEY_DELETED'] = 'Index deleted'; +$lang['L_KEY_DELETEERROR'] = 'Error deleting index'; +$lang['L_KEY_ADDED'] = 'Index added'; +$lang['L_KEY_ADDERROR'] = 'Error adding index'; diff --git a/msd/language/ch/help.html b/msd/language/ch/help.html new file mode 100644 index 0000000..250ec11 --- /dev/null +++ b/msd/language/ch/help.html @@ -0,0 +1,146 @@ +
+

MyOOS [Dumper] based on MySQLDumper 1.24.4

+ +

About this project

+

MyOOS [Dumper] is an improved version of MySQLDumper 1.24.4 (January 24, 2011). This enhancement takes into account the development of PHP.

. +

Most of all stability, security and handling are the main focus of MyOOS [Dumper]. But also an attractive template is included, which can be edited and adapted to your own needs.

. + + +

MyOOS [Dumper] is a backup program for MySQL databases, written in PHP and Perl. With it, backup copies of the data (store, blog, etc.) can be created and restored if necessary. Especially for web space without shell access, MyOOS [Dumper] is a useful alternative.

. + +

The idea for MySQLDumper came from Daniel Schlichtholz. He opened the MySQLDumper forum in 2004, whereupon programmers wrote new scripts and extended existing ones.

+ + +

Wish List / Future Attractions

. +

Do you have any suggestions for improvements? Feel free to contact the development team via the forum https://foren.myoos.de/viewforum.php?f=41.

+ + +

Contribute

+

If you would like to help us improve the MyOOS project, we welcome your pull requests via GitHub here.

+https://github.com/r23/MyOOS-Dumper/ + + +

Financial Support

. +

You can use PayPal Me
. +https://www.paypal.com/paypalme/r23de?locale.x=de_DE

+ +

or via the QR code
. +Financial support for MyOOS [Dumper]

+ +Send money to the MyOOS project.
+ +

We hope you enjoy this project.

The MyOOS [Dumper] Team

+ +MyOOS [Dumper]
+ +

MyOOS [Dumper] Help

+ +

Download

+

You can always get the latest versions via GitHub
. +https://github.com/r23/MyOOS-Dumper/releases

+ + +

System requirement

. +

The script works on any server (Windows, Linux, ...)
+with PHP >= version 7.4 with GZip support, MySQL (version 4.1 or higher), JavaScript (must be enabled)

. +

Copy the mod folder from the MyOOS archive to a separate working folder.

. + +

Installation

. +The installation process is straightforward. +

From the MyOOS archive, copy the mod folder into any folder.
+Upload all the files from the mod folder to your web server. (For example, to the lowest level in [server web directory/]mod)
+... done!
+You can now call MyOOS [Dumper] in your web browser by "https://example.com/mod/",
+to complete the installation. Just follow the instructions.
+
Note:
If on your server the script is not allowed to create directories,
+you have to do this manually, because MyOOS [Dumper] stores the data ordered in +directories.
+The script aborts with an appropriate statement!
+After you have created the directories (according to the hint), it runs normally and without restrictions.
+ +

Perl script instructions

. +Most have a cgi-bin directory where perl can be run.
+This is usually accessible by browser via http://www.example.com/cgi-bin/.
+
+For this case, please perform the following steps:

. + +1. call the Backup page in MyOOS [Dumper] and click on "Backup Perl".
+2. copy the path that is behind entry in crondump.pl for $absolute_path_of_configdir:.
+3. open the file "crondump.pl" in the editor.
+4. enter the copied path there at absolute_path_of_configdir (no spaces).
+5. save crondump.pl .
+6. copy crondump.pl, as well as perltest.pl and simpletest.pl into the cgi-bin directory (ascii mode in FTP).
+7. give the files the permissions 755.
+7b. If the ending cgi is desired, change the ending of all 3 files from pl -> cgi (rename).
+8. call the configuration in MyOOS [Dumper].
+9. select the page Cronscript.
+10. change perl execution path to /cgi-bin/ .
+10b. If the scripts have .pl, change the file extension to .cgi .
+11.Save the configuration.

+ +Done, the scripts can now be called from the backup page.

. + +For those who can run Perl in all directories, the following steps will suffice:

. + +1. call in the MyOOS [Dumper] the page Backup.
+2. copy the path that is behind entry in crondump.pl for $absolute_path_of_configdir:.
+3. open the file "crondump.pl" in the editor.
+4. enter the copied path there at absolute_path_of_configdir (no spaces).
+5. save crondump.pl .
+6. give the files the permissions 755.
+6b. If the extension cgi is desired, change the extension of all 3 files from pl -> cgi (rename).
+(ev. 10b+11 from above)
+
+ +Windowsuser have to change the first line of all scripts, there is the path of Perl. Example:
+instead of: #!/usr/bin/perl -w
+now: #!C:_usr/bin/perl.exe -w
+ +

Operation

    . + +
    Menu
    . +In the selection list above you set the database.
    +All actions refer to the database set here. + +
    Home
    +Here you can learn about your system, the different installed versions and details about the +versions and details about the configured databases.
    +If you click on the database name, you will see a list of the tables with the number of entries +with the number of entries, the size and the last update date. + +
    Configuration
    . +Here you can edit your configuration, save it or restore the initial configuration. +restore. +

      +
    • Configured databases: the listing of configured databases. The active database is listed in bold.
    • +
    • Table prefix: here you can specify (for each database) a prefix. This is a filter that will take into account for dumps only the tables that start with this prefix (for example, all tables that start with "phpBB_"). If you want all tables in this database to be saved, just leave the field empty.
    • . +
    • GZip compression: Here you can enable compression. It is recommended to enable it, because the files will be much smaller after all and disk space is always scarce.
    • . +
    • Email with Dumpfile: If this option is enabled, an email with the dump as an attachment will be sent after the backup is complete (caution, compression should absolutely be on, otherwise the attachment will be too large and may not be sent!).
    • +
    • Email address: Recipient address for the email.
    • +
    • Sender of the email: this address appears as the sender in the email.
    • +
    • FTP Transfer: If this option is enabled, the backup file will be sent via FTP after the backup is completed.
    • +
    • FTP Server: The address of the FTP server (e.g. ftp.mybackups.com).
    • +
    • FTP Server Port: The port of the FTP server (usually 21).
    • +
    • FTP User: The username of the FTP account.
    • +
    • FTP Password: The password of the FTP account.
    • +
    • FTP Upload Folder: The directory where the backup file should go (upload permissions must exist!).
    • +
    • Automatic deletion of backups: If this option is enabled, older backups will be deleted automatically according to the following rules.
    • . +
    • Number of backup files: A value > 0 deletes all backup files except for the number specified here.
    • +
    • Language: here you specify the language for the interface.
    • +
    + +
    Administration
    . +This is where the actual actions are performed.
    +It will show you all the files in the backup directory. +For the actions "Restore" and "Delete" a file must be selected. +
      +
    • Restore: This will update the database with the selected backup file.
    • +
    • Delete: This lets you delete the selected backup file.
    • +
    • Start new backup: Here you start a new backup (dump) according to the parameters set in the configuration.
    • . +
    + +
    Log
    +Here you can see and delete the log entries. +
    Credits / Help
    +this page. +
diff --git a/msd/language/ch/lang.php b/msd/language/ch/lang.php new file mode 100644 index 0000000..e2e45c1 --- /dev/null +++ b/msd/language/ch/lang.php @@ -0,0 +1,109 @@ +nöd verfüegbar'; +$lang['L_VOM'] = 'vo'; +$lang['L_MYSQLVARS'] = 'MySQL-Variable'; +$lang['L_MYSQLSYS'] = 'MySQL-Befähl'; +$lang['L_STATUS'] = 'Status'; +$lang['L_PROZESSE'] = 'Prozäss'; +$lang['L_INFO_NOVARS'] = 'kei Variable verfüegbar'; +$lang['L_INHALT'] = 'Inhalt'; +$lang['L_INFO_NOSTATUS'] = 'kei Status verfüegbar'; +$lang['L_INFO_NOPROCESSES'] = 'kei laufendi Prozäss'; +$lang['L_FM_FREESPACE'] = 'Freie Speicher uf em Server'; +$lang['L_LOAD_DATABASE'] = 'Datebank neu lade'; +$lang['L_HOME'] = 'an Aafang'; +$lang['L_CONFIG'] = 'Konfiguration'; +$lang['L_DUMP'] = 'Backup mache'; +$lang['L_RESTORE'] = 'Reschtauriere'; +$lang['L_FILE_MANAGE'] = 'Verwaltig'; +$lang['L_LOG'] = 'Log'; +$lang['L_CHOOSE_DB'] = 'Datebank uswähle'; +$lang['L_CREDITS'] = 'Kredits und Hilf'; +$lang['L_MULTI_PART'] = 'Multipart-Backup'; +$lang['L_LOGFILENOTWRITABLE'] = "s'Logfile cha nöd gschribe wärde"; +$lang['L_SQL_ERROR1'] = 'Fähler bi de Aafrag:'; +$lang['L_SQL_ERROR2'] = 'MySQL mäldet:'; +$lang['L_UNKNOWN'] = 'ubekannt'; +$lang['L_UNKNOWN_NUMBER_OF_RECORDS'] = 'ubekannt'; +$lang['L_OK'] = 'ok'; +$lang['L_CRON_COMPLETELOG'] = 'Kompletti Usgab logge'; +$lang['L_NO'] = 'nei'; +$lang['L_CREATE_DATABASE'] = 'Neui Datebank aalegge'; +$lang['L_EXPORTFINISHED'] = 'Export fertig gmacht'; +$lang['L_SQL_BROWSER'] = 'SQL-Browser'; +$lang['L_SERVER'] = 'Server'; +$lang['L_MYSQL_CONNECTION_ENCODING'] = 'Standardkodierig vom MySQL-Server'; +$lang['L_TITLE_SHOW_DATA'] = 'Date aazeige'; +$lang['L_PRIMARYKEY_CONFIRMDELETE'] = 'Really delete primary key?'; +$lang['L_SETPRIMARYKEYSFOR'] = 'Set new primary keys for table'; +$lang['L_PRIMARYKEY_FIELD'] = 'Primary key field'; +$lang['L_PRIMARYKEYS_SAVE'] = 'Save primary keys'; +$lang['L_CANCEL'] = 'Cancel'; +$lang['L_VISIT_HOMEPAGE'] = 'Visit Homepage'; +$lang['L_SECONDS'] = 'Seconds'; +$lang['L_BACKUPS'] = 'Backups'; +$lang['L_MINUTES'] = 'Minutes'; +$lang['L_PAGE_REFRESHS'] = 'Page refreshs'; +$lang['L_MINUTE'] = 'Minute'; +$lang['L_SETKEYSFOR'] = 'Set new indexes for table'; +$lang['L_KEY_CONFIRMDELETE'] = 'Really delete index?'; diff --git a/msd/language/ch/lang_config_overview.php b/msd/language/ch/lang_config_overview.php new file mode 100644 index 0000000..1dbc112 --- /dev/null +++ b/msd/language/ch/lang_config_overview.php @@ -0,0 +1,125 @@ +%s
in %s'; +$lang['L_FTP'] = 'FTP'; +$lang['L_SFTP_SEND_TO'] = 'an %s
in %s'; +$lang['L_SFTP_SEND_TO'] = 'an %s
in %s'; +$lang['L_SFTP'] = 'SFTP'; +$lang['L_EMAIL_CC'] = 'CC-Empfänger'; +$lang['L_NAME'] = 'Name'; +$lang['L_CONFIRM_CONFIGFILE_DELETE'] = 'Söll d Konfigurationsdatei %s würkli glöscht werde?'; +$lang['L_ERROR_DELETING_CONFIGFILE'] = 'Fähler: d Konfigurationsdatei %s hät nöd chöne glöscht wärde!'; +$lang['L_SUCCESS_DELETING_CONFIGFILE'] = 'D Konfigurationsdatei %s isch erfolgriich glöscht worde.'; +$lang['L_SUCCESS_CONFIGFILE_CREATED'] = 'D Konfigurationsdatei %s isch erfolgriich aagleit worde.'; +$lang['L_ERROR_CONFIGFILE_NAME'] = 'Im Dateiname "%s" häts ungültigi Zeiche.'; +$lang['L_CREATE_CONFIGFILE'] = 'E neui Konfigurationsdatei aalegge'; +$lang['L_ERROR_LOADING_CONFIGFILE'] = 'D Konfigurationsdatei "%s" hät nöd chöne glade wärde.'; +$lang['L_BACKUP_DBS_PHP'] = 'DBs zum Sichere (Perl)'; +$lang['L_BACKUP_DBS_PERL'] = 'DBs zum Sichere (Perl)'; +$lang['L_CRON_COMMENT'] = 'Kommentar iigää'; +$lang['L_AUTODETECT'] = 'auto detect'; diff --git a/msd/language/ch/lang_dump.php b/msd/language/ch/lang_dump.php new file mode 100644 index 0000000..b58726b --- /dev/null +++ b/msd/language/ch/lang_dump.php @@ -0,0 +1,50 @@ +%s` chöne gfunde werde.'; +$lang['L_DUMP_ENDERGEBNIS'] = 'Es sind %s Tabälle mit zäme %s Datesätz gsicheret worde.
'; +$lang['L_MAILERROR'] = 'Leider isch bim Verschicke vo de E-Mail en Fähler underloffe!'; +$lang['L_EMAILBODY_ATTACH'] = 'Im Aahang finded Si d Sicherig vo Ihrer MySQL-Datenbank.
Sicherig vo de Datenbank `%s`

Folgende Datei wurde erzeugt:

%s

Fründlichi Grüess

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_MP_NOATTACH'] = 'Es isch e Multipart-Sicherig erstellt worde.
Die Sicherige werdet nöd als Aahang mitglieferet!
Sicherig vo de Datenbank `%s`

Folgendi Dateie sind erzügt worde:

%s


Fründlichi Grüess

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_MP_ATTACH'] = 'Es isch e Multipart-Sicherig erstellt worde.
D Sicherige werdet i separate E-Mails als Anhang glieferet!
Sicherig vo de Datenbank `%s`

Folgendi Dateie sind erzügt worde:

%s


Fründlichi Grüess

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_FOOTER'] = '


Fründlichi Grüess

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_TOOBIG'] = 'D Sicherig überschriitet d Maximalgrössi von %s und isch drum nöd aagehänkt worde.
Sicherig vo de Datenbank `%s`

Folgendi Datei isch erzügt worde:

%s

Fründlichi Grüess

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_NOATTACH'] = 'S Backup isch nöd aaghänkt worde.
Sicherig vo de Datenbank `%s`

Folgendi Datei isch erzügt worde:

%s

Fründlichi Grüess

MyOOS [Dumper]
'; +$lang['L_EMAIL_ONLY_ATTACHMENT'] = '... nume dr Aahang'; +$lang['L_TABLESELECTION'] = 'Tabälleuswahl'; +$lang['L_SELECTALL'] = 'ali uuswähle'; +$lang['L_DESELECTALL'] = 'Uswahl ufhebä'; +$lang['L_STARTDUMP'] = 'Backup starte'; +$lang['L_LASTBUFROM'] = 'sletschti Update vom'; +$lang['L_NOT_SUPPORTED'] = 'Das Backup cha diä Funktion nöd.'; +$lang['L_MULTIDUMP'] = 'Multidump: Es sind %d Datenbanke gesicheret worde.'; +$lang['L_FILESENDFTP'] = 'verschicke grad File via FTP... heb bitte e chli Geduld.'; +$lang['L_FTPCONNERROR'] = 'FTP-Verbindig nöd hergstellt! Verbindig mit'; +$lang['L_FTPCONNERROR1'] = 'als User'; +$lang['L_FTPCONNERROR2'] = 'nöd mögli'; +$lang['L_FTPCONNERROR3'] = 'FTP-Upload isch fählerhaft gsi!'; +$lang['L_FTPCONNECTED1'] = 'Verbunde mit'; +$lang['L_FTPCONNECTED2'] = 'uf'; +$lang['L_FTPCONNECTED3'] = 'gschribe'; +$lang['L_FILESENDSFTP'] = 'verschicke grad File via SFTP... heb bitte e chli Geduld.'; +$lang['L_SFTPCONNERROR'] = 'SFTP-Verbindig nöd hergstellt! Verbindig mit'; +$lang['L_NR_TABLES_SELECTED'] = '- mit %s gewählte Tabälle'; +$lang['L_NR_TABLES_OPTIMIZED'] = '%s Tabälle sind optimiert worde.'; +$lang['L_DUMP_ERRORS'] = '

%s Fähler ufträte: aaluege

'; +$lang['L_FATAL_ERROR_DUMP'] = "Fatale Fähler: d CREATE-Aawiisig vo de Tabelle '%s' i de Datenbank '%s' hät nöd chöne gläse werde!"; diff --git a/msd/language/ch/lang_filemanagement.php b/msd/language/ch/lang_filemanagement.php new file mode 100644 index 0000000..f16ea8c --- /dev/null +++ b/msd/language/ch/lang_filemanagement.php @@ -0,0 +1,75 @@ +%s"'; +$lang['L_DELETE_FILE_ERROR'] = 'Error deleting file "%s"!'; +$lang['L_FM_DUMP_HEADER'] = 'Backup'; +$lang['L_DOCRONBUTTON'] = 'Perl-Cronscript usfüere'; +$lang['L_DOPERLTEST'] = 'Perl-Module teschte'; +$lang['L_DOSIMPLETEST'] = 'Perl teschte'; +$lang['L_PERLOUTPUT1'] = 'Iitrag in crondump.pl für absolute_path_of_con'; +$lang['L_PERLOUTPUT2'] = 'Ufruef im Browser oder für externe Cronjob'; +$lang['L_PERLOUTPUT3'] = 'Ufruef i de Shell oder für d Crontab'; +$lang['L_RESTORE_OF_TABLES'] = 'Reschtauriere vo bestimmte Tabälle'; +$lang['L_CONVERTER'] = 'Backup-Konverter'; +$lang['L_CONVERT_FILE'] = 'z konvertierendi Datei'; +$lang['L_CONVERT_FILENAME'] = 'Name vo de Zieldatei (ohni Endig)'; +$lang['L_CONVERTING'] = 'Konvertierig'; +$lang['L_CONVERT_FILEREAD'] = "Datei '%s' wird eingläse"; +$lang['L_CONVERT_FINISHED'] = "Konvertierig abgschlosse, '%s' isch erzügt worde."; +$lang['L_NO_MOD_BACKUPFILE'] = 'Dateie vo andere Programm'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Maximali Dateigrössi'; +$lang['L_MAX_UPLOAD_SIZE_INFO'] = 'Wänn Ihri Backup-Datei grösser als das agebne Limit isch, müend Si diä per FTP in "work/backup"-Ordner ufelade. Dänn wird diä Datei, do i de Verwaltig aazeigt und laht sich dänn für en Reschtaurierig uswähle.'; +$lang['L_ENCODING'] = 'Kodierig'; +$lang['L_FM_CHOOSE_ENCODING'] = 'Kodierig vo de Backupdatei wähle'; +$lang['L_CHOOSE_CHARSET'] = 'Leider hät nöd chöne automatich ermittlet mit welem Zeichesatz diä Backupdatei sinerziit aagleit worde isch.
Sie müend diä Kodierig, i dere Zeichechette i dere Datei sind, vo Hand aagäh.
Dänn stellt MyOOS [Dumper] diä Verbindigskännig zum MySQL-Server uf de usgwählti Zeichesatz und fangt mit de Reschtaurierig vo de Date a.>br>Sötted Si nach de Reschtaurierig Problem mit Sonderzeiche ha, chönd Si probiere, das Backup mit ere andere Zeichesatzuswahl z reschtauriere.
Vill Glück. ;)'; +$lang['L_DOWNLOAD_FILE'] = 'Download file'; +$lang['L_BACKUP_NOT_POSSIBLE'] = 'A backup of the system database `%s` is not possible!'; diff --git a/msd/language/ch/lang_help.php b/msd/language/ch/lang_help.php new file mode 100644 index 0000000..5a50d2d --- /dev/null +++ b/msd/language/ch/lang_help.php @@ -0,0 +1,34 @@ +d Installation isch abgschlosse --> starte MyOOS [Dumper]
'; +$lang['L_INSTALL_TOMENU'] = 'zum Hauptmenü'; +$lang['L_INSTALLMENU'] = 'Hauptmenü'; +$lang['L_STEP'] = 'Schritt'; +$lang['L_INSTALL'] = 'Installation'; +$lang['L_UNINSTALL'] = 'Uninstall'; +$lang['L_TOOLS'] = 'Tools'; +$lang['L_EDITCONF'] = 'Konfiguration bearbeite'; +$lang['L_OSWEITER'] = 'ohni Spiichere wiiter'; +$lang['L_ERRORMAN'] = 'Fähler bim Schriibe vo de Konfiguration!
Bitte editiered Si diä Datei'; +$lang['L_MANUELL'] = 'vo Hand'; +$lang['L_CREATEDIRS'] = 'mache Verzeichnis'; +$lang['L_INSTALL_CONTINUE'] = 'mit de Installation wiitermache'; +$lang['L_CONNECTTOMYSQL'] = 'zu MySQL verbinde'; +$lang['L_DBPARAMETER'] = 'Datebank-Parameter'; +$lang['L_CONFIGNOTWRITABLE'] = 'Diä Datei "config.php" ist nöd beschriibbar. Gänd Si ire miteme FTP-Programm entsprächende Rächt, z. B. de CHMod-Wert 0777.'; +$lang['L_DBCONNECTION'] = 'Datebank-Verbindig'; +$lang['L_CONNECTIONERROR'] = 'Fähler: Es hät kei Verbindig chöne gmacht wärde.'; +$lang['L_CONNECTION_OK'] = 'Datebankverbindig isch gmacht worde.'; +$lang['L_SAVEANDCONTINUE'] = 'spiichere und wiitermache mit de Installation'; +$lang['L_CONFBASIC'] = 'Grundiischtellige'; +$lang['L_INSTALL_STEP2FINISHED'] = 'D Iischtellige sind erfolgriich gsicheret worde'; +$lang['L_INSTALL_STEP2_1'] = 'Installation mit de Standardkonfiguration wiitermache'; +$lang['L_LASTSTEP'] = 'Abschluss vo de Installation'; +$lang['L_IDOMANUAL'] = 'Ich mache diä Verzeichniss vo Hand'; +$lang['L_DOFROM'] = 'usgehend vo'; +$lang['L_FTPMODE2'] = 'Mache diä Verzeichniss per FTP:'; +$lang['L_CONNECT'] = 'verbinde'; +$lang['L_DIRS_CREATED'] = 'D Verzeichniss sind ordnigsgmäss gmacht worde.'; +$lang['L_CONNECT_TO'] = 'verbinde zu'; +$lang['L_CHANGEDIR'] = 'Wächsel is Verzeichnis'; +$lang['L_CHANGEDIRERROR'] = 'Wächsel is Verzeichnis nöd mögli'; +$lang['L_FTP_OK'] = 'FTP-Parameter sind ok'; +$lang['L_SFTP_OK'] = 'FTP-Parameter sind ok'; +$lang['L_CREATEDIRS2'] = 'Verzeichnis mache'; +$lang['L_FTP_NOTCONNECTED'] = 'FTP-Verbindig nöd gmacht!'; +$lang['L_CONNWITH'] = 'Verbindig mit'; +$lang['L_ASUSER'] = 'als User'; +$lang['L_NOTPOSSIBLE'] = 'nöd mögli'; +$lang['L_DIRCR1'] = 'mache es Arbetsverzeichnis'; +$lang['L_DIRCR2'] = 'mache es Backup-Verzeichnis'; +$lang['L_DIRCR4'] = 'mache es Log-Verzeichnis'; +$lang['L_DIRCR5'] = 'mache es Konfigurationsverzeichnis'; +$lang['L_INDIR'] = 'bi im Verzeichnis'; +$lang['L_CHECK_DIRS'] = 'überprüefe'; +$lang['L_DISABLEDFUNCTIONS'] = 'Abgschalteti Funktionen'; +$lang['L_NOFTPPOSSIBLE'] = 'Es hät kei FTP-Funktionen zur Verfüegig!'; +$lang['L_NOGZPOSSIBLE'] = 'Es hät kei Kompressions-Funktione zur Verfüegig!'; +$lang['L_UI1'] = 'Es werded alli Arbetsverzeichnis mit allne Backups wo drin sind glöscht.'; +$lang['L_UI2'] = 'Sind Si sicher, dass Si das wänd?'; +$lang['L_UI3'] = 'Nei, sofort abbräche'; +$lang['L_UI4'] = 'jo, bitte wiitermache'; +$lang['L_UI5'] = 'lösche Arbetsverzeichnis'; +$lang['L_UI6'] = 'alls isch erfolgriich glöscht worde'; +$lang['L_UI7'] = 'Lösched Si bitte das Skriptverzeichnis'; +$lang['L_UI8'] = 'en Ebeni nach obe'; +$lang['L_UI9'] = 'Es hät en Fähler gäh, Lösche isch nöd mögli gsi

Fähler bi Verzeichnis'; +$lang['L_IMPORT'] = 'Konfiguration importiere'; +$lang['L_IMPORT3'] = 'D Konfiguration isch glade...'; +$lang['L_IMPORT4'] = 'D Konfiguration isch gsicheret.'; +$lang['L_IMPORT5'] = 'MyOOS [Dumper] starte'; +$lang['L_IMPORT6'] = 'Installations-Menü'; +$lang['L_IMPORT7'] = 'Konfiguration ufelade'; +$lang['L_IMPORT8'] = 'zrugg zum Upload'; +$lang['L_IMPORT9'] = 'Das isch kei Konfigurationssicherig!'; +$lang['L_IMPORT10'] = 'D Konfiguration isch erfolgriich ufeglade worde.'; +$lang['L_IMPORT11'] = 'Fähler: Es hät Problem gäh bim Schriibe vo de sql_statements.'; +$lang['L_IMPORT12'] = 'Fähler: Es hät Problem gäh bim Schriibe vo de config.php.'; +$lang['L_INSTALL_HELP_PORT'] = '(läär = Standardport)'; +$lang['L_INSTALL_HELP_SOCKET'] = '(läär = Standardsocket)'; +$lang['L_TRYAGAIN'] = 'namal versueche'; +$lang['L_SOCKET'] = 'Socket'; +$lang['L_PORT'] = 'Port'; +$lang['L_FOUND_DB'] = 'gfundeni DB:'; +$lang['L_FM_FILEUPLOAD'] = 'Datei ufelade'; +$lang['L_PASS'] = 'Passwort'; +$lang['L_NO_DB_FOUND_INFO'] = 'D Verbindig zur Datebank isch erfolgriich gsi.
Ihri Zuegangsdate sind gültig und sind vom MySQL-Server akzeptiert worde.
Leider hät de MyOOS [Dumper] kei Datebank gfunde.
Di automatischi Erkännig isch bi mänge Hoschter gschpeert.
Si müend Ihri Datebank nachem Abschluss vo de Installation under em Menüpunkt "Konfiguration" "Verbindigsparameter iiblände" agäh.
Gönd Si bitte sofort nach em Abschluss vo de Installation det hii und träged Si de Name vo Irer Datebank det ii.'; +$lang['L_ENTER_DB_INFO'] = 'First click the button "Connect to MySQL". Only if no database could be detected you need to provide a database name here.'; diff --git a/msd/language/ch/lang_log.php b/msd/language/ch/lang_log.php new file mode 100644 index 0000000..0352edb --- /dev/null +++ b/msd/language/ch/lang_log.php @@ -0,0 +1,7 @@ + Mached Si bitte vo Hand e Datei mit folgendem Inhalt'; +$lang['L_HTACC_CHECK_ERROR'] = 'It could not be checked whether the program is protected!
The simulated external access could not be carried out.'; +$lang['L_HTACC_NOT_NEEDED'] = 'The program is protected by higher-level authorizations; local directory protection is not required.'; +$lang['L_HTACC_COMPLETE'] = 'The program is protected, the directory protection is complete.'; +$lang['L_HTACC_INCOMPLETE'] = 'The program is not protected, the directory protection is incomplete!'; +$lang['L_HTACC_PROPOSED'] = 'The program is not protected, directory protection is strongly recommended!'; +$lang['L_HTACC_EDIT'] = '.htaccess editiere'; +$lang['L_HTACCESS18'] = '.htaccess mache in'; +$lang['L_HTACCESS19'] = 'neu lade'; +$lang['L_HTACCESS20'] = 'Skript ausfüere'; +$lang['L_HTACCESS21'] = 'Handler zuefüege'; +$lang['L_HTACCESS22'] = 'Usfüerbar mache'; +$lang['L_HTACCESS23'] = 'Verzeichnis-Listing'; +$lang['L_HTACCESS24'] = 'Error-Dokument'; +$lang['L_HTACCESS25'] = 'Rewrite aktiviere'; +$lang['L_HTACCESS26'] = 'Deny / Allow'; +$lang['L_HTACCESS27'] = 'Redirect'; +$lang['L_HTACCESS28'] = 'Error-Log'; +$lang['L_HTACCESS29'] = 'wiiteri Bischpiel und Dokumentation'; +$lang['L_HTACCESS30'] = 'Provider'; +$lang['L_HTACCESS31'] = 'generell'; +$lang['L_HTACCESS32'] = 'Achtung! Diä .htaccess hät e diräkti Uswirkig uf de Browser.
Bi falscher Anwendig sind diä Siite nüme erreichbar.'; +$lang['L_DISABLEDFUNCTIONS'] = 'Abgschalteti Funktione'; +$lang['L_NOGZPOSSIBLE'] = 'Will zlib nöd inschtalliert isch, schtönd kei GZip-Funktione zur Verfüegig!'; +$lang['L_DELETE_HTACCESS'] = 'Verzeichnisschutz wägmache (.htaccess lösche)'; +$lang['L_WRONG_RIGHTS'] = "Diä Datei oder das Verzeichnis '%s' isch für mi nöd beschriibbar.
Entweder hät si de falschi Besitzer (Owner) oder di falsche Rächt (Chmod).
Bitte setzed Si diä richtige Attribut mit Irem FTP-Programm.
Diä Datei oder das Verzeichnis brucht diä Rächt %s.
"; +$lang['L_CANT_CREATE_DIR'] = "Ha s Verzeichnis '%s' nöd chöne mache. Mached Si s bitte mit Irem FTP-Programm"; +$lang['L_TABLE_TYPE'] = 'Type'; +$lang['L_CHECK'] = 'check'; +$lang['L_OS'] = 'Operating system'; +$lang['L_MOD_VERSION'] = 'MyOOS [Dumper] - Version'; +$lang['L_NEW_MOD_VERSION'] = 'New Version'; +$lang['L_NEW_MOD_VERSION_INFO'] = 'There is a new version of MyOOS [Dumper] available.'; +$lang['L_UPDATED_IMPORTANT'] = 'Important: Before updating, please backup your files.'; +$lang['L_UPDATE'] = 'Update now'; +$lang['L_MYSQL_VERSION'] = 'MySQL-Version'; +$lang['L_PHP_VERSION'] = 'PHP-Version'; +$lang['L_MAX_EXECUTION_TIME'] = 'Max execution time'; +$lang['L_PHP_EXTENSIONS'] = 'PHP-Extensions'; +$lang['L_MEMORY'] = 'Memory'; +$lang['L_FILE_MISSING'] = 'Han diä Datei nöd gfunde'; +$lang['L_INSTALLING_UPDATES'] = 'Installing Updates'; +$lang['L_UPDATE_SUCCESSFUL'] = 'Update successful'; +$lang['L_UPDATE_FAILED'] = 'Update failed'; +$lang['L_UP_TO_DATE'] = 'Current Version is up to date'; diff --git a/msd/language/ch/lang_restore.php b/msd/language/ch/lang_restore.php new file mode 100644 index 0000000..143b5d1 --- /dev/null +++ b/msd/language/ch/lang_restore.php @@ -0,0 +1,19 @@ +%d Tabälle agleit worde.'; +$lang['L_FILE_MISSING'] = 'Han diä Datei nöd gfunde'; +$lang['L_RESTORE_DB'] = "Datebank '%s' uf Server '%s'."; +$lang['L_RESTORE_COMPLETE'] = '%s Tabälle sind angleit worde.'; +$lang['L_RESTORE_RUN1'] = '
Es sind bis ez %s vo %s Datesätz erfolgriich iitreit worde.'; +$lang['L_RESTORE_RUN2'] = "
Momentan werdet Date vo de Tabälle '%s' analysiert.

"; +$lang['L_RESTORE_COMPLETE2'] = '%s Datesätz sind iitreit worde.'; +$lang['L_RESTORE_TABLES_COMPLETED'] = 'Es sind bis ez %d vo %d Tabälle agleit worde.'; +$lang['L_RESTORE_TOTAL_COMPLETE'] = '
Herzliche Glückwunsch.

Diä Datenbank isch komplett reschtauriert worde.
Alli Date us de Backup-Datei sind erfolgriich i diä Datebank itreit worde.

Alls fertig. :-)'; +$lang['L_DB_SELECT_ERROR'] = "
Fähler:
Uuswahl vo de Datebank '"; +$lang['L_DB_SELECT_ERROR2'] = "' abverheit!"; +$lang['L_FILE_OPEN_ERROR'] = 'Fähler: Diä Datei hät nöd chöne ufgmacht wärde'; +$lang['L_PROGRESS_OVER_ALL'] = 'Fortschritt total'; +$lang['L_BACK_TO_OVERVIEW'] = 'Datebank-Übersicht'; +$lang['L_RESTORE_RUN0'] = '
Es sind bis ez %s Datesätz erfolgriich iitreit worde.'; +$lang['L_UNKNOWN_SQLCOMMAND'] = 'Unbekannte SQL-Befehl:'; +$lang['L_NOTICES'] = 'Hiiwis'; diff --git a/msd/language/ch/lang_sql.php b/msd/language/ch/lang_sql.php new file mode 100644 index 0000000..d1988f4 --- /dev/null +++ b/msd/language/ch/lang_sql.php @@ -0,0 +1,190 @@ +%s Ziile exportiert'; +$lang['L_CSV_FIELDCOUNT_NOMATCH'] = 'D Aazahl vo de Tabällefelder schtimmed nöd mit de date vo de z importierende Date überii (%d statt %d).'; +$lang['L_CSV_FIELDSLINES'] = '%d Fälder ermittlet, total %d Ziile'; +$lang['L_CSV_ERRORCREATETABLE'] = 'Fähler bim Mache vo de Tabälle `%s`!'; +$lang['L_FM_UPLOADFILEREQUEST'] = 'Gänd Si bitte e Datei aa.'; +$lang['L_CSV_NODATA'] = 'Kei Date zum Importiere gfunde!'; +$lang['L_SQLLIB_GENERALFUNCTIONS'] = 'allgemeini Funktione'; +$lang['L_SQLLIB_RESETAUTO'] = 'Auto-Wert zruggsetze'; +$lang['L_SQLLIB_BOARDS'] = 'Boards'; +$lang['L_SQLLIB_DEACTIVATEBOARD'] = 'Board deaktiviere'; +$lang['L_SQLLIB_ACTIVATEBOARD'] = 'Board aktiviere'; +$lang['L_SQL_NOTABLESSELECTED'] = 'Es sind kei Tabälle usgwählt!'; +$lang['L_TOOLS'] = 'Tools'; +$lang['L_TOOLS_TOOLBOX'] = 'Datebank uswähle / Datebankfunktionen / Im- und Export'; +$lang['L_SQL_OPENFILE'] = 'SQL-Datei ufmache'; +$lang['L_SQL_OPENFILE_BUTTON'] = 'Ufelade'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Maximali Dateigrössi'; +$lang['L_SQL_SEARCH'] = 'Suechi'; +$lang['L_SQL_SEARCHWORDS'] = 'Suechbegriff'; +$lang['L_START_SQL_SEARCH'] = 'Suechi starte'; +$lang['L_RESET_SEARCHWORDS'] = 'Iigab zruggsetze'; +$lang['L_SEARCH_OPTIONS'] = 'Suechoptione'; +$lang['L_SEARCH_RESULTS'] = 'D Suech nach "%s" i de Tabälle "%s" liferet folgendi Träffer'; +$lang['L_SEARCH_NO_RESULTS'] = 'D Suech nach "%s" i de Tabälle "%s" lieferet kei Ergebniss!'; +$lang['L_NO_ENTRIES'] = 'Diä Tabälle "%s" ist läär und hät keine Iiträg.'; +$lang['L_SEARCH_ACCESS_KEYS'] = 'Blättere: vürschi=ALT+V, zrugg=ALT+C'; +$lang['L_SEARCH_OPTIONS_OR'] = 'e Spalte mues mindeschtens ein Suechbegriff enthalte (ODER-Suche)'; +$lang['L_SEARCH_OPTIONS_CONCAT'] = 'en Datesatz mues alli Suechbegriffe enthalte, diä chöned aber i beliebige Spalte sii (Recheintensiv!)'; +$lang['L_SEARCH_OPTIONS_AND'] = 'e Spalte mues alli Suechbegriff enthalte (UND-Suche)'; +$lang['L_SEARCH_IN_TABLE'] = 'Suech i de Tabälle'; +$lang['L_ERROR_NO_FIELDS'] = 'Search error: it could not be determined which fields the table "%s" has!'; +$lang['L_SQL_EDIT_TABLESTRUCTURE'] = 'Tabällestruktur bearbeite'; +$lang['L_DEFAULT_CHARSET'] = 'Standardzeichesatz'; +$lang['L_TITLE_KEY_PRIMARY'] = 'Primärschlüssel'; +$lang['L_TITLE_KEY_UNIQUE'] = 'Eidütige Schlüssel'; +$lang['L_TITLE_INDEX'] = 'Index'; +$lang['L_TITLE_KEY_FULLTEXT'] = 'Volltextschlüssel'; +$lang['L_TITLE_NOKEY'] = 'Kei Schlüssel '; +$lang['L_TITLE_SEARCH'] = 'Suechi'; +$lang['L_TITLE_MYSQL_HELP'] = 'MySQL Dokumentation '; +$lang['L_TITLE_UPLOAD'] = 'SQL-Datei ufelade '; +$lang['L_PRIMARYKEY_DELETED'] = 'Primary key deleted'; +$lang['L_PRIMARYKEY_NOTFOUND'] = 'Primary key not found'; +$lang['L_PRIMARYKEYS_CHANGED'] = 'Primary keys changed'; +$lang['L_PRIMARYKEYS_CHANGINGERROR'] = 'Error changing primary keys'; +$lang['L_SQL_VIEW_COMPACT'] = 'View: compact'; +$lang['L_SQL_VIEW_STANDARD'] = 'View: standard'; +$lang['L_FIELDS_OF_TABLE'] = 'Fields of table'; +$lang['L_ENGINE'] = 'Engine'; +$lang['L_USERNAME'] = 'Username'; +$lang['L_PASSWORD'] = 'Password'; +$lang['L_PASSWORD_REPEAT'] = 'Password (repeat)'; +$lang['L_INFO_SIZE'] = 'Grössi'; +$lang['L_TABLE_TYPE'] = 'Type'; +$lang['L_KEY_DELETED'] = 'Index deleted'; +$lang['L_KEY_DELETEERROR'] = 'Error deleting index'; +$lang['L_KEY_ADDED'] = 'Index added'; +$lang['L_KEY_ADDERROR'] = 'Error adding index'; diff --git a/msd/language/da/help.html b/msd/language/da/help.html new file mode 100644 index 0000000..fe88bc4 --- /dev/null +++ b/msd/language/da/help.html @@ -0,0 +1,147 @@ +

+

MyOOS [Dumper] based on MySQLDumper 1.24.4

+ +

About this project

+

MyOOS [Dumper] is an improved version of MySQLDumper 1.24.4 (January 24, 2011). This enhancement takes into account the development of PHP.

. +

Most of all stability, security and handling are the main focus of MyOOS [Dumper]. But also an attractive template is included, which can be edited and adapted to your own needs.

. + + +

MyOOS [Dumper] is a backup program for MySQL databases, written in PHP and Perl. With it, backup copies of the data (store, blog, etc.) can be created and restored if necessary. Especially for web space without shell access, MyOOS [Dumper] is a useful alternative.

. + +

The idea for MySQLDumper came from Daniel Schlichtholz. He opened the MySQLDumper forum in 2004, whereupon programmers wrote new scripts and extended existing ones.

+ + + +

Wish List / Future Attractions

. +

Do you have any suggestions for improvements? Feel free to contact the development team via the forum https://foren.myoos.de/viewforum.php?f=41.

+ + +

Contribute

+

If you would like to help us improve the MyOOS project, we welcome your pull requests via GitHub here.

+https://github.com/r23/MyOOS-Dumper/ + + +

Financial Support

. +

You can use PayPal Me
. +https://www.paypal.com/paypalme/r23de?locale.x=de_DE

+ +

or via the QR code
. +Financial support for MyOOS [Dumper]

+ +Send money to the MyOOS project.
+ +

We hope you enjoy this project.

The MyOOS [Dumper] Team

+ +MyOOS [Dumper]
+ +

MyOOS [Dumper] Help

+ +

Download

+

You can always get the latest versions via GitHub
. +https://github.com/r23/MyOOS-Dumper/releases

+ + +

System requirement

. +

The script works on any server (Windows, Linux, ...)
+with PHP >= version 7.4 with GZip support, MySQL (version 4.1 or higher), JavaScript (must be enabled)

. +

Copy the mod folder from the MyOOS archive to a separate working folder.

. + +

Installation

. +The installation process is straightforward. +

From the MyOOS archive, copy the mod folder into any folder.
+Upload all the files from the mod folder to your web server. (For example, to the lowest level in [server web directory/]mod)
+... done!
+You can now call MyOOS [Dumper] in your web browser by "https://example.com/mod/",
+to complete the installation. Just follow the instructions.
+
Note:
If on your server the script is not allowed to create directories,
+you have to do this manually, because MyOOS [Dumper] stores the data ordered in +directories.
+The script aborts with an appropriate statement!
+After you have created the directories (according to the hint), it runs normally and without restrictions.
+ +

Perl script instructions

. +Most have a cgi-bin directory where perl can be run.
+This is usually accessible by browser via http://www.example.com/cgi-bin/.
+
+For this case, please perform the following steps:

. + +1. call the Backup page in MyOOS [Dumper] and click on "Backup Perl".
+2. copy the path that is behind entry in crondump.pl for $absolute_path_of_configdir:.
+3. open the file "crondump.pl" in the editor.
+4. enter the copied path there at absolute_path_of_configdir (no spaces).
+5. save crondump.pl .
+6. copy crondump.pl, as well as perltest.pl and simpletest.pl into the cgi-bin directory (ascii mode in FTP).
+7. give the files the permissions 755.
+7b. If the ending cgi is desired, change the ending of all 3 files from pl -> cgi (rename).
+8. call the configuration in MyOOS [Dumper].
+9. select the page Cronscript.
+10. change perl execution path to /cgi-bin/ .
+10b. If the scripts have .pl, change the file extension to .cgi .
+11.Save the configuration.

+ +Done, the scripts can now be called from the backup page.

. + +For those who can run Perl in all directories, the following steps will suffice:

. + +1. call in the MyOOS [Dumper] the page Backup.
+2. copy the path that is behind entry in crondump.pl for $absolute_path_of_configdir:.
+3. open the file "crondump.pl" in the editor.
+4. enter the copied path there at absolute_path_of_configdir (no spaces).
+5. save crondump.pl .
+6. give the files the permissions 755.
+6b. If the extension cgi is desired, change the extension of all 3 files from pl -> cgi (rename).
+(ev. 10b+11 from above)
+
+ +Windowsuser have to change the first line of all scripts, there is the path of Perl. Example:
+instead of: #!/usr/bin/perl -w
+now: #!C:_usr/bin/perl.exe -w
+ +

Operation

    . + +
    Menu
    . +In the selection list above you set the database.
    +All actions refer to the database set here. + +
    Home
    +Here you can learn about your system, the different installed versions and details about the +versions and details about the configured databases.
    +If you click on the database name, you will see a list of the tables with the number of entries +with the number of entries, the size and the last update date. + +
    Configuration
    . +Here you can edit your configuration, save it or restore the initial configuration. +restore. +

      +
    • Configured databases: the listing of configured databases. The active database is listed in bold.
    • +
    • Table prefix: here you can specify (for each database) a prefix. This is a filter that will take into account for dumps only the tables that start with this prefix (for example, all tables that start with "phpBB_"). If you want all tables in this database to be saved, just leave the field empty.
    • . +
    • GZip compression: Here you can enable compression. It is recommended to enable it, because the files will be much smaller after all and disk space is always scarce.
    • . +
    • Email with Dumpfile: If this option is enabled, an email with the dump as an attachment will be sent after the backup is complete (caution, compression should absolutely be on, otherwise the attachment will be too large and may not be sent!).
    • +
    • Email address: Recipient address for the email.
    • +
    • Sender of the email: this address appears as the sender in the email.
    • +
    • FTP Transfer: If this option is enabled, the backup file will be sent via FTP after the backup is completed.
    • +
    • FTP Server: The address of the FTP server (e.g. ftp.mybackups.com).
    • +
    • FTP Server Port: The port of the FTP server (usually 21).
    • +
    • FTP User: The username of the FTP account.
    • +
    • FTP Password: The password of the FTP account.
    • +
    • FTP Upload Folder: The directory where the backup file should go (upload permissions must exist!).
    • +
    • Automatic deletion of backups: If this option is enabled, older backups will be deleted automatically according to the following rules.
    • . +
    • Number of backup files: A value > 0 deletes all backup files except for the number specified here.
    • +
    • Language: here you specify the language for the interface.
    • +
    + +
    Administration
    . +This is where the actual actions are performed.
    +It will show you all the files in the backup directory. +For the actions "Restore" and "Delete" a file must be selected. +
      +
    • Restore: This will update the database with the selected backup file.
    • +
    • Delete: This lets you delete the selected backup file.
    • +
    • Start new backup: Here you start a new backup (dump) according to the parameters set in the configuration.
    • . +
    + +
    Log
    +Here you can see and delete the log entries. +
    Credits / Help
    +this page. +
diff --git a/msd/language/da/lang.php b/msd/language/da/lang.php new file mode 100644 index 0000000..24258c0 --- /dev/null +++ b/msd/language/da/lang.php @@ -0,0 +1,109 @@ +ikke tilgængelig'; +$lang['L_VOM'] = 'fra'; +$lang['L_MYSQLVARS'] = 'MySQL Variabler'; +$lang['L_MYSQLSYS'] = 'MySQL Kommandoer'; +$lang['L_STATUS'] = 'Tilstand'; +$lang['L_PROZESSE'] = 'Processer'; +$lang['L_INFO_NOVARS'] = 'ingen variabler tilgængelige'; +$lang['L_INHALT'] = 'Værdi'; +$lang['L_INFO_NOSTATUS'] = 'ingen tilstand tilgængelig'; +$lang['L_INFO_NOPROCESSES'] = 'ingen kørende processer'; +$lang['L_FM_FREESPACE'] = 'Fri plads på Server'; +$lang['L_LOAD_DATABASE'] = 'Genindlæs databaser'; +$lang['L_HOME'] = 'Hjem'; +$lang['L_CONFIG'] = 'Konfiguration'; +$lang['L_DUMP'] = 'Backup'; +$lang['L_RESTORE'] = 'Genetabler'; +$lang['L_FILE_MANAGE'] = 'Fil Administration'; +$lang['L_LOG'] = 'Log'; +$lang['L_CHOOSE_DB'] = 'Vælg Database'; +$lang['L_CREDITS'] = 'Bidragydere / Hjælp'; +$lang['L_MULTI_PART'] = 'Multipart Backup'; +$lang['L_LOGFILENOTWRITABLE'] = 'Kan ikke skrive Logfil !'; +$lang['L_SQL_ERROR1'] = 'Fejl i forespørgsel:'; +$lang['L_SQL_ERROR2'] = 'MySQL siger:'; +$lang['L_UNKNOWN'] = 'ukendt'; +$lang['L_UNKNOWN_NUMBER_OF_RECORDS'] = 'ukendt'; +$lang['L_OK'] = 'OK'; +$lang['L_CRON_COMPLETELOG'] = 'Log komplet output'; +$lang['L_NO'] = 'nej'; +$lang['L_CREATE_DATABASE'] = 'Opret ny database'; +$lang['L_EXPORTFINISHED'] = 'Eksport færdiggjort.'; +$lang['L_SQL_BROWSER'] = 'SQL-Browser'; +$lang['L_SERVER'] = 'Server'; +$lang['L_MYSQL_CONNECTION_ENCODING'] = 'Standard encoding of MySQL-Server'; +$lang['L_TITLE_SHOW_DATA'] = 'Show data'; +$lang['L_PRIMARYKEY_CONFIRMDELETE'] = 'Really delete primary key?'; +$lang['L_SETPRIMARYKEYSFOR'] = 'Set new primary keys for table'; +$lang['L_PRIMARYKEY_FIELD'] = 'Primary key field'; +$lang['L_PRIMARYKEYS_SAVE'] = 'Save primary keys'; +$lang['L_CANCEL'] = 'Cancel'; +$lang['L_VISIT_HOMEPAGE'] = 'Visit Homepage'; +$lang['L_SECONDS'] = 'Seconds'; +$lang['L_BACKUPS'] = 'Backups'; +$lang['L_MINUTES'] = 'Minutes'; +$lang['L_PAGE_REFRESHS'] = 'Page refreshs'; +$lang['L_MINUTE'] = 'Minute'; +$lang['L_SETKEYSFOR'] = 'Set new indexes for table'; +$lang['L_KEY_CONFIRMDELETE'] = 'Really delete index?'; diff --git a/msd/language/da/lang_config_overview.php b/msd/language/da/lang_config_overview.php new file mode 100644 index 0000000..6865e9b --- /dev/null +++ b/msd/language/da/lang_config_overview.php @@ -0,0 +1,131 @@ +%s
into %s'; +$lang['L_FTP'] = 'FTP'; +$lang['L_SFTP'] = 'SFTP'; +$lang['L_EMAIL_CC'] = 'CC-Receiver'; +$lang['L_NAME'] = 'Name'; +$lang['L_CONFIRM_CONFIGFILE_DELETE'] = 'Really delete the configuration file %s?'; +$lang['L_ERROR_DELETING_CONFIGFILE'] = "Error: couldn't delete configuration file %s!"; +$lang['L_SUCCESS_DELETING_CONFIGFILE'] = 'The configuration file %s has successfully been deleted.'; +$lang['L_SUCCESS_CONFIGFILE_CREATED'] = 'Configuration file %s has successfully been created.'; +$lang['L_ERROR_CONFIGFILE_NAME'] = 'Filename "%s" contains invalid characters.'; +$lang['L_CREATE_CONFIGFILE'] = 'Create a new configuration file'; +$lang['L_ERROR_LOADING_CONFIGFILE'] = "Couldn't load configfile \"%s\"."; +$lang['L_BACKUP_DBS_PHP'] = 'DBs to backup (PHP)'; +$lang['L_BACKUP_DBS_PERL'] = 'DBs to backup (PERL)'; +$lang['L_CRON_COMMENT'] = 'Indtast kommentar'; +$lang['L_AUTODETECT'] = 'auto detect'; diff --git a/msd/language/da/lang_dump.php b/msd/language/da/lang_dump.php new file mode 100644 index 0000000..eae952b --- /dev/null +++ b/msd/language/da/lang_dump.php @@ -0,0 +1,61 @@ +%s` '; +$lang['L_DUMP_ENDERGEBNIS'] = 'Filen indeholder %s tabeller med %s poster.
'; +$lang['L_MAILERROR'] = 'Afsendelse af email slog fejl!'; +$lang['L_EMAILBODY_ATTACH'] = 'Den vedhæftede fil indeholder backup af din MySQL-Database.
Backup af Database `%s` +

Følgende fil blev oprettet:

%s

Venlig hilsen

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_MP_NOATTACH'] = 'En Multipart Backup blev oprettet.
Backupfilerne er ikke vedhæftet denne email!
Backup af Database `%s` +

Følgende filer blev oprettet:

%s +

Venlig hilsen

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_MP_ATTACH'] = 'En Multipart Backup er blevet oprettet.
Backupfilerne er vedhæftet separate emails.
Backup af Database `%s` +

Følgende filer blev oprettet:

%s

Med venlig hilsen

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_FOOTER'] = '

Venlig hilsen

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_TOOBIG'] = 'Backupfilen oversteg maksimumstørrelsen på %s og blev ikke vedhæftet denne email.
Backup sf Database `%s` +

Følgende fil blev oprettet:

%s +

Venlig hilsen

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_NOATTACH'] = 'Filer er ikke vedhæftet denne email!
Backup af Database `%s` +

Følgende fil blev oprettet:

%s +

Venlig hilsen

MyOOS [Dumper]
'; +$lang['L_EMAIL_ONLY_ATTACHMENT'] = ' ... kun vedhæftet.'; +$lang['L_TABLESELECTION'] = 'Tabelvælg'; +$lang['L_SELECTALL'] = 'Vælg alle'; +$lang['L_DESELECTALL'] = 'Fravælg alle'; +$lang['L_STARTDUMP'] = 'Start Backup'; +$lang['L_LASTBUFROM'] = 'sidst opdateret fra'; +$lang['L_NOT_SUPPORTED'] = 'Denne backup understøtter ikke denne funktion.'; +$lang['L_MULTIDUMP'] = 'Multidump: Backup af %d Databaser færdige.'; +$lang['L_FILESENDFTP'] = 'send fil via FTP... vær venligst tålmodig. '; +$lang['L_FTPCONNERROR'] = 'FTP-forbindelse ikke etableret! Forbind med '; +$lang['L_FTPCONNERROR1'] = ' som bruger '; +$lang['L_FTPCONNERROR2'] = ' ikke muligt'; +$lang['L_FTPCONNERROR3'] = 'FTP-upload fejlede! '; +$lang['L_FTPCONNECTED1'] = 'Forbundet med '; +$lang['L_FTPCONNECTED2'] = ' på '; +$lang['L_FTPCONNECTED3'] = ' overførsel korrekt gennemført'; +$lang['L_FILESENDSFTP'] = 'send fil via SFTP... vær venligst tålmodig. '; +$lang['L_SFTPCONNERROR'] = 'SFTP-forbindelse ikke etableret! Forbind med '; +$lang['L_NR_TABLES_SELECTED'] = '- med %s valgte tabeller'; +$lang['L_NR_TABLES_OPTIMIZED'] = '%s tabeller er blevet optimeret.'; +$lang['L_DUMP_ERRORS'] = '

%s fejl optrådte: se log

+ + +'; +$lang['L_FATAL_ERROR_DUMP'] = "Fatal error: the CREATE-Statement of table '%s' in database '%s' couldn't be read!"; diff --git a/msd/language/da/lang_filemanagement.php b/msd/language/da/lang_filemanagement.php new file mode 100644 index 0000000..49b9b93 --- /dev/null +++ b/msd/language/da/lang_filemanagement.php @@ -0,0 +1,79 @@ +%s"'; +$lang['L_DELETE_FILE_ERROR'] = 'Error deleting file "%s"!'; +$lang['L_FM_DUMP_HEADER'] = 'Backup'; +$lang['L_DOCRONBUTTON'] = 'Kør Perl Cron scriptet'; +$lang['L_DOPERLTEST'] = 'Test Perl-moduler'; +$lang['L_DOSIMPLETEST'] = 'Test Perl'; +$lang['L_PERLOUTPUT1'] = 'Linie i crondump.pl for absolute_path_of_configdir'; +$lang['L_PERLOUTPUT2'] = 'URL for browseren eller for eksternt Cron job'; +$lang['L_PERLOUTPUT3'] = 'Kommandolinie i Shell eller for Crontab'; +$lang['L_RESTORE_OF_TABLES'] = 'Choose tables to be restored'; +$lang['L_CONVERTER'] = 'Backupkonvertering'; +$lang['L_CONVERT_FILE'] = 'Fil der skal konverteres'; +$lang['L_CONVERT_FILENAME'] = 'Navn på destinationsfilen (uden filtype)'; +$lang['L_CONVERTING'] = 'Konverterer'; +$lang['L_CONVERT_FILEREAD'] = "Læs fil '%s'"; +$lang['L_CONVERT_FINISHED'] = "Konvertering afsluttet, '%s' blev skrevet korrekt."; +$lang['L_NO_MOD_BACKUPFILE'] = 'Backups af andre scripts'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Maksimal filstørrelse'; +$lang['L_MAX_UPLOAD_SIZE_INFO'] = 'Hvis din Dumpfil er større end den ovennævnte grænse, skal du uploade den via FTP til folderen "work/backup". +Derefter kan du vælge den og begynde genetableringsprocessen.'; +$lang['L_ENCODING'] = 'encoding'; +$lang['L_FM_CHOOSE_ENCODING'] = 'Choose encoding of backup file'; +$lang['L_CHOOSE_CHARSET'] = "MyOOS [Dumper] couldn't detect the encoding of the backup file automatically. +
You must choose the charset with which this backup was saved. +
If you discover any problems with some characters after restoring, you can repeat the backup-progress and then choose another character set. +
Good luck. ;)"; +$lang['L_DOWNLOAD_FILE'] = 'Download file'; +$lang['L_BACKUP_NOT_POSSIBLE'] = 'A backup of the system database `%s` is not possible!'; diff --git a/msd/language/da/lang_help.php b/msd/language/da/lang_help.php new file mode 100644 index 0000000..f1b350f --- /dev/null +++ b/msd/language/da/lang_help.php @@ -0,0 +1,40 @@ +Installation gennemført --> start MyOOS [Dumper]
'; +$lang['L_INSTALL_TOMENU'] = 'Tilbage til hovedmenu'; +$lang['L_INSTALLMENU'] = 'Hovedmenu'; +$lang['L_STEP'] = 'Trin'; +$lang['L_INSTALL'] = 'Installation'; +$lang['L_UNINSTALL'] = 'Afinstallation'; +$lang['L_TOOLS'] = 'Funktioner'; +$lang['L_EDITCONF'] = 'Ret konfiguration'; +$lang['L_OSWEITER'] = 'Fortsæt uden at gemme'; +$lang['L_ERRORMAN'] = 'Fejl ved lagring af konfiguration!
Redigér venligst filen '; +$lang['L_MANUELL'] = 'manuelt'; +$lang['L_CREATEDIRS'] = 'Opret foldere'; +$lang['L_INSTALL_CONTINUE'] = 'Fortsæt med installation'; +$lang['L_CONNECTTOMYSQL'] = 'Forbind til MySQL '; +$lang['L_DBPARAMETER'] = 'Databaseparametre'; +$lang['L_CONFIGNOTWRITABLE'] = 'Kan ikke skrive til fil "config.php". +Brug venligst dit FTP-program og giv denne fil passende rettigheder, f.eks. CHMOD 0777.'; +$lang['L_DBCONNECTION'] = 'Databaseforbindelse'; +$lang['L_CONNECTIONERROR'] = 'Fejl: kan ikke forbinde.'; +$lang['L_CONNECTION_OK'] = 'Databaseforbindelse etableret.'; +$lang['L_SAVEANDCONTINUE'] = 'Gem og fortsæt installation'; +$lang['L_CONFBASIC'] = 'Basisparametre'; +$lang['L_INSTALL_STEP2FINISHED'] = 'Databaseparametre blev gemt.'; +$lang['L_INSTALL_STEP2_1'] = 'Fortsæt installation med standard-indstillingerne'; +$lang['L_LASTSTEP'] = 'Installation afsluttet'; +$lang['L_FTPMODE'] = 'Opret nødvendige foldere i safe-mode'; +$lang['L_IDOMANUAL'] = 'Jeg opretter selv folderne'; +$lang['L_DOFROM'] = 'startende fra'; +$lang['L_FTPMODE2'] = 'Opret folderne med FTP:'; +$lang['L_CONNECT'] = 'forbind'; +$lang['L_DIRS_CREATED'] = 'Folderne er blevet oprettet og har fået tildelt korrekte tilladelser.'; +$lang['L_CONNECT_TO'] = 'forbind til'; +$lang['L_CHANGEDIR'] = 'skift til folder'; +$lang['L_CHANGEDIRERROR'] = 'skift til folder var ikke muligt'; +$lang['L_FTP_OK'] = 'FTP-parameter er ok'; +$lang['L_SFTP_OK'] = 'FTP-parameter er ok'; +$lang['L_CREATEDIRS2'] = 'Opret foldere'; +$lang['L_FTP_NOTCONNECTED'] = 'FTP-forbindelse ikke etableret!'; +$lang['L_CONNWITH'] = 'Forbindelse med'; +$lang['L_ASUSER'] = 'som bruger'; +$lang['L_NOTPOSSIBLE'] = 'ikke muligt'; +$lang['L_DIRCR1'] = 'opret arbejdsfolder'; +$lang['L_DIRCR2'] = 'opret backupdir'; +$lang['L_DIRCR4'] = 'opret logdir'; +$lang['L_DIRCR5'] = 'opret configurationdir'; +$lang['L_INDIR'] = 'nu i dir (folder)'; +$lang['L_CHECK_DIRS'] = 'Check mine foldere'; +$lang['L_DISABLEDFUNCTIONS'] = 'Deaktiverede Funktioner'; +$lang['L_NOFTPPOSSIBLE'] = 'Du har ikke adgang til FTP-funktioner!'; +$lang['L_NOGZPOSSIBLE'] = 'Du har ikke adgang til komprimerings-funktioner!'; +$lang['L_UI1'] = 'Alle arbejdsfoldere, hvilke kan indeholde backups, vil blive slettet.'; +$lang['L_UI2'] = 'Er du sikker på at du vil gøre dette?'; +$lang['L_UI3'] = 'nej, afbryd øjeblikkeligt'; +$lang['L_UI4'] = 'ja, fortsæt venligst'; +$lang['L_UI5'] = 'sletter arbejdsfoldere'; +$lang['L_UI6'] = 'alle blev korrekt slettet.'; +$lang['L_UI7'] = 'Slet venligst script folderen'; +$lang['L_UI8'] = 'et niveau op'; +$lang['L_UI9'] = 'Der opstod en fejl, sletning var ikke muligt

Fejl med folder '; +$lang['L_IMPORT'] = 'Import Konfiguration'; +$lang['L_IMPORT3'] = 'Konfiguration blev indlæst ...'; +$lang['L_IMPORT4'] = 'Konfiguration blev gemt.'; +$lang['L_IMPORT5'] = 'Start MyOOS [Dumper]'; +$lang['L_IMPORT6'] = 'Installationsmenu'; +$lang['L_IMPORT7'] = 'Upload konfiguration'; +$lang['L_IMPORT8'] = 'tilbage til upload'; +$lang['L_IMPORT9'] = 'Dette er ikke en konfigurationsbackup!'; +$lang['L_IMPORT10'] = 'Konfiguration korrekt uploadet ...'; +$lang['L_IMPORT11'] = 'Fejl: Der var problemer med at skrive til sql_statements'; +$lang['L_IMPORT12'] = 'Fejl: Der var problemer med at skrive til config.php'; +$lang['L_INSTALL_HELP_PORT'] = '(tom = Standardport)'; +$lang['L_INSTALL_HELP_SOCKET'] = '(tom = Standard Socket)'; +$lang['L_TRYAGAIN'] = 'Prøv igen'; +$lang['L_SOCKET'] = 'Socket'; +$lang['L_PORT'] = 'Port'; +$lang['L_FOUND_DB'] = 'fundet db:'; +$lang['L_FM_FILEUPLOAD'] = 'Upload fil'; +$lang['L_PASS'] = 'Kodeord'; +$lang['L_NO_DB_FOUND_INFO'] = 'Forbindelsen til databasen blev korrekt etableret.
Dine brugerdata er gyldige og blev accepteret af MySQL-serveren.
Men MyOOS [Dumper] kunne ikke finde nogen database.
Den automatiske visning af databaser via script er slået fra på visse servere.
Du skal indtaste databasenavnet manuelt efter installationen er færdiggjort. Klik på "konfiguration" "Forbindelsesparametr - vis" og indtast databasenavnet dér.'; +$lang['L_ENTER_DB_INFO'] = 'First click the button "Connect to MySQL". Only if no database could be detected you need to provide a database name here.'; diff --git a/msd/language/da/lang_log.php b/msd/language/da/lang_log.php new file mode 100644 index 0000000..15b7bf0 --- /dev/null +++ b/msd/language/da/lang_log.php @@ -0,0 +1,10 @@ +ikke skrive Logfil!'; +$lang['L_NOREVERSE'] = 'Ældste indlæg først'; +$lang['L_REVERSE'] = 'Seneste indlæg først + + +'; diff --git a/msd/language/da/lang_main.php b/msd/language/da/lang_main.php new file mode 100644 index 0000000..3155795 --- /dev/null +++ b/msd/language/da/lang_main.php @@ -0,0 +1,85 @@ +Opret venligst de 2 filer manuelt med følgende indhold'; +$lang['L_HTACC_CHECK_ERROR'] = 'It could not be checked whether the program is protected!
The simulated external access could not be carried out.'; +$lang['L_HTACC_NOT_NEEDED'] = 'The program is protected by higher-level authorizations; local directory protection is not required.'; +$lang['L_HTACC_COMPLETE'] = 'The program is protected, the directory protection is complete.'; +$lang['L_HTACC_INCOMPLETE'] = 'The program is not protected, the directory protection is incomplete!'; +$lang['L_HTACC_PROPOSED'] = 'The program is not protected, directory protection is strongly recommended!';; +$lang['L_HTACC_EDIT'] = 'Rediger .htaccess'; +$lang['L_HTACCESS18'] = 'Opret .htaccess i '; +$lang['L_HTACCESS19'] = 'Genindlæs'; +$lang['L_HTACCESS20'] = 'Udfør script'; +$lang['L_HTACCESS21'] = 'Tilføj handler'; +$lang['L_HTACCESS22'] = 'Lav til eksekverbar'; +$lang['L_HTACCESS23'] = 'Folder-indholdslistning'; +$lang['L_HTACCESS24'] = 'Fejl-dokument'; +$lang['L_HTACCESS25'] = 'Aktivér rewrite'; +$lang['L_HTACCESS26'] = 'Deny / Allow'; +$lang['L_HTACCESS27'] = 'Redirect'; +$lang['L_HTACCESS28'] = 'Error Log'; +$lang['L_HTACCESS29'] = 'Flere eksempler og dokumentation'; +$lang['L_HTACCESS30'] = 'Leverandør'; +$lang['L_HTACCESS31'] = 'Generelt'; +$lang['L_HTACCESS32'] = 'Bemærk! .htaccess påvirker dirkte browserens opførsel.
Med forkert indhold kan disse sider blive utilgængelige.'; +$lang['L_DISABLEDFUNCTIONS'] = 'Funktioner slået fra'; +$lang['L_NOGZPOSSIBLE'] = 'Da Zlib ikke er installeret/tilgængeligt, kan du ikke bruge GZip-funktionerne!'; +$lang['L_DELETE_HTACCESS'] = 'Fjern folderbeskyttelse (slet .htaccess)'; +$lang['L_WRONG_RIGHTS'] = "Kan ikke skrive til filen eller folderen '%s'.
Fil-rettighederne (chmod) er ikke sat korrekt eller har den forkerte ejer.
Sæt venligst de korrekte attributter via din FTP-klient.
Filen eller mappen skal være sat til %s.
"; +$lang['L_CANT_CREATE_DIR'] = "Kunne ikke oprette folderen '%s'. Opret den venligst med en FTP-klient."; +$lang['L_TABLE_TYPE'] = 'Type'; +$lang['L_CHECK'] = 'check'; +$lang['L_OS'] = 'Operating system'; +$lang['L_MOD_VERSION'] = 'MyOOS [Dumper] - Version'; +$lang['L_NEW_MOD_VERSION'] = 'Ny version'; +$lang['L_NEW_MOD_VERSION_INFO'] = 'En ny version af MyOOS [Dumper] er tilgængelig.'; +$lang['L_UPDATED_IMPORTANT'] = 'Important: Before updating, please backup your files.'; +$lang['L_UPDATE'] = 'Update now'; +$lang['L_MYSQL_VERSION'] = 'MySQL-Version'; +$lang['L_PHP_VERSION'] = 'PHP-Version'; +$lang['L_MAX_EXECUTION_TIME'] = 'Max execution time'; +$lang['L_PHP_EXTENSIONS'] = 'PHP-Extensions'; +$lang['L_MEMORY'] = 'Memory'; +$lang['L_FILE_MISSING'] = 'kunne ikke finde fil'; +$lang['L_INSTALLING_UPDATES'] = 'Installation af opdateringer'; +$lang['L_UPDATE_SUCCESSFUL'] = 'Opdatering vellykket'; +$lang['L_UPDATE_FAILED'] = 'Opdatering mislykkedes'; +$lang['L_UP_TO_DATE'] = 'Den aktuelle version er opdateret'; diff --git a/msd/language/da/lang_restore.php b/msd/language/da/lang_restore.php new file mode 100644 index 0000000..a6bab09 --- /dev/null +++ b/msd/language/da/lang_restore.php @@ -0,0 +1,19 @@ +%d tabeller.'; +$lang['L_FILE_MISSING'] = 'kunne ikke finde fil'; +$lang['L_RESTORE_DB'] = "Database '%s' på '%s'."; +$lang['L_RESTORE_COMPLETE'] = '%s tabeller oprettet.'; +$lang['L_RESTORE_RUN1'] = '
Foreløbigt er der korrekt tilføjet %s af %s poster.'; +$lang['L_RESTORE_RUN2'] = "
Tabellen '%s' er under genetablering.

"; +$lang['L_RESTORE_COMPLETE2'] = '%s poster indsat.'; +$lang['L_RESTORE_TABLES_COMPLETED'] = 'Foreløbigt er der oprettet %d af %d tabeller.'; +$lang['L_RESTORE_TOTAL_COMPLETE'] = '
Tillykke.

Genetableringen af databasen er færdig.
Alle data fra Backupfilen er blevet genetableret.

Processen er færdig. :-)'; +$lang['L_DB_SELECT_ERROR'] = '
Fejl:
Valg af database '; +$lang['L_DB_SELECT_ERROR2'] = ' fejlede!'; +$lang['L_FILE_OPEN_ERROR'] = 'Fejl: kunne ikke åbne fil.'; +$lang['L_PROGRESS_OVER_ALL'] = 'Samlede fremskridt'; +$lang['L_BACK_TO_OVERVIEW'] = 'Database-oversigt'; +$lang['L_RESTORE_RUN0'] = '
foreløbigt er der korrekt tilføjet %s poster.'; +$lang['L_UNKNOWN_SQLCOMMAND'] = 'ukendt SQL-kommando'; +$lang['L_NOTICES'] = 'Bemærkninger'; diff --git a/msd/language/da/lang_sql.php b/msd/language/da/lang_sql.php new file mode 100644 index 0000000..55bb5b6 --- /dev/null +++ b/msd/language/da/lang_sql.php @@ -0,0 +1,190 @@ +%s linier eksporteret'; +$lang['L_CSV_FIELDCOUNT_NOMATCH'] = 'Felt-tælleren stemmer ikke overens med de importerede data (%d i stedet for %d).'; +$lang['L_CSV_FIELDSLINES'] = '%d felter genkendt, totalt %d linier'; +$lang['L_CSV_ERRORCREATETABLE'] = 'Fejl ved oprettelse af tabel `%s` !'; +$lang['L_FM_UPLOADFILEREQUEST'] = 'vælg venligst en fil.'; +$lang['L_CSV_NODATA'] = 'Ingen data fundet til import!'; +$lang['L_SQLLIB_GENERALFUNCTIONS'] = 'generelle funktioner'; +$lang['L_SQLLIB_RESETAUTO'] = 'nulstil auto-increment (forøgelse)'; +$lang['L_SQLLIB_BOARDS'] = 'Boards'; +$lang['L_SQLLIB_DEACTIVATEBOARD'] = 'deaktiver Board'; +$lang['L_SQLLIB_ACTIVATEBOARD'] = 'aktiver Board'; +$lang['L_SQL_NOTABLESSELECTED'] = 'Ingen tabeller valgt!'; +$lang['L_TOOLS'] = 'Funktioner'; +$lang['L_TOOLS_TOOLBOX'] = 'Vælg Database / Datebasefunktioner / Import - Eksport'; +$lang['L_SQL_OPENFILE'] = 'Åbn SQL-fil'; +$lang['L_SQL_OPENFILE_BUTTON'] = 'Upload'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Maksimal filstørrelse'; +$lang['L_SQL_SEARCH'] = 'Søg'; +$lang['L_SQL_SEARCHWORDS'] = 'Søgeord'; +$lang['L_START_SQL_SEARCH'] = 'Start søgning'; +$lang['L_RESET_SEARCHWORDS'] = 'nulstil søgeord'; +$lang['L_SEARCH_OPTIONS'] = 'Søgeindstillinger'; +$lang['L_SEARCH_RESULTS'] = 'Søgningen efter "%s" i tabellen "%s" giver følgende resultater'; +$lang['L_SEARCH_NO_RESULTS'] = 'Søgningen efter "%s" i tabel "%s" gav ingen rsultater!'; +$lang['L_NO_ENTRIES'] = 'Tabel "%s" er tom og indeholder ingen poster.'; +$lang['L_SEARCH_ACCESS_KEYS'] = 'Bladre: fremad=ALT+V, baglæns=ALT+C'; +$lang['L_SEARCH_OPTIONS_OR'] = 'en kolonne skal indeholde et af søgeordene (ELLER-søgning)'; +$lang['L_SEARCH_OPTIONS_CONCAT'] = 'en række skal indeholde alle søgeordene men kan være i hvilkensomhelst kolonne (kan tage noget tid)'; +$lang['L_SEARCH_OPTIONS_AND'] = 'en kolonne skal indeholde ALLE søgeord (OG-søgning)'; +$lang['L_SEARCH_IN_TABLE'] = 'Søg i tabel'; +$lang['L_ERROR_NO_FIELDS'] = 'Search error: it could not be determined which fields the table "%s" has!'; +$lang['L_SQL_EDIT_TABLESTRUCTURE'] = 'Edit table structure'; +$lang['L_DEFAULT_CHARSET'] = 'Default character set'; +$lang['L_TITLE_KEY_PRIMARY'] = 'Primary key'; +$lang['L_TITLE_KEY_UNIQUE'] = 'Unique key'; +$lang['L_TITLE_INDEX'] = 'Index'; +$lang['L_TITLE_KEY_FULLTEXT'] = 'Fulltext key'; +$lang['L_TITLE_NOKEY'] = 'No key'; +$lang['L_TITLE_SEARCH'] = 'Search'; +$lang['L_TITLE_MYSQL_HELP'] = 'MySQl Documentation'; +$lang['L_TITLE_UPLOAD'] = 'Upload SQL file'; +$lang['L_PRIMARYKEY_DELETED'] = 'Primary key deleted'; +$lang['L_PRIMARYKEY_NOTFOUND'] = 'Primary key not found'; +$lang['L_PRIMARYKEYS_CHANGED'] = 'Primary keys changed'; +$lang['L_PRIMARYKEYS_CHANGINGERROR'] = 'Error changing primary keys'; +$lang['L_SQL_VIEW_COMPACT'] = 'View: compact'; +$lang['L_SQL_VIEW_STANDARD'] = 'View: standard'; +$lang['L_FIELDS_OF_TABLE'] = 'Fields of table'; +$lang['L_ENGINE'] = 'Engine'; +$lang['L_USERNAME'] = 'Username'; +$lang['L_PASSWORD'] = 'Password'; +$lang['L_PASSWORD_REPEAT'] = 'Password (repeat)'; +$lang['L_INFO_SIZE'] = 'Størrelse'; +$lang['L_TABLE_TYPE'] = 'Type'; +$lang['L_KEY_DELETED'] = 'Index deleted'; +$lang['L_KEY_DELETEERROR'] = 'Error deleting index'; +$lang['L_KEY_ADDED'] = 'Index added'; +$lang['L_KEY_ADDERROR'] = 'Error adding index'; diff --git a/msd/language/de/help.html b/msd/language/de/help.html new file mode 100644 index 0000000..5ebc9de --- /dev/null +++ b/msd/language/de/help.html @@ -0,0 +1,149 @@ +
+

MyOOS [Dumper] based on MySQLDumper 1.24.4

+ +

Über dieses Projekt

+

MyOOS [Dumper] ist eine verbesserte Version von MySQLDumper 1.24.4 (24. Januar 2011). Diese Weiterentwicklung berücksichtig die Entwicklung von PHP.

+

Vor allem Stabilität, Sicherheit und Handhabung stehen bei MyOOS [Dumper] maßgeblich im Vordergrund. Aber auch ein ansprechendes Template wird mitgeliefert, welches beliebig bearbeitet und an eigene Bedürfnisse angepasst werden kann.

+ + +

MyOOS [Dumper] ist ein Sicherungsprogramm für MySQL-Datenbanken, geschrieben in PHP und Perl. Damit können Sicherungskopien der Daten (Shop, Blog, usw.) erstellt und bei Bedarf auch wieder hergestellt werden. Besonders bei Web-Space ohne Shell-Zugang bietet sich MyOOS [Dumper] als sinnvolle Alternative an.

+ +

Die Idee für MySQLDumper kam von Daniel Schlichtholz. Er eröffnete 2004 das Forum MySQLDumper, woraufhin Programmierer neue Skripte schrieben und bestehende erweiterten.

+ + + +

Wunschliste / Künftige Attraktionen

+

Hast du Verbesserungsvorschläge? Zögere nicht, das Entwicklerteam über das Forum https://foren.myoos.de/viewforum.php?f=41 zu kontaktieren.

+ + +

Mitwirken

+

Wenn du uns dabei helfen möchtest, das MyOOS Projekt zu verbessern, freuen wir uns hier auf deine Pull Requests via GitHub.

+https://github.com/r23/MyOOS/ + + +

Finanzielle Unterstützung

+

Man kann mit PayPal Me
+https://www.paypal.com/paypalme/r23de?locale.x=de_DE

+ +

oder über den QR Code
+Finanzielle Unterstützung für MyOOS [Dumper]

+ +Geld an das MyOOS Projekt senden.
+ +

Wir wünschen Dir viel Vergnügen mit diesem Projekt.

Das MyOOS [Dumper]-Team

+ +MyOOS [Dumper]
+MyOOS [Dumper]
+ + +

MyOOS [Dumper] Hilfe

+ +

Download

+

Aktuelle Versionen erhälst du immer über GitHub
+https://github.com/r23/MyOOS/releases

+ + +

Systemvoraussetzung

+

Das Script arbeitet auf jedem Server (Windows, Linux, ...)
+mit PHP >= Version 7.4 mit GZip-Unterstützung, MySQL (ab Version 4.1), JavaScript (muss aktiviert sein).

+

Aus dem MyOOS Archiv den Ordner mod in einen separaten Arbeitsordner kopieren.

+ +

Installation

+Die Installation geht einfach von statten. +

Aus dem MyOOS Archiv den Ordner mod in einen beliebigen Ordner kopieren.
+Ladet alle Dateien aus dem ordner mod auf deinen Webserver hoch. (z. B. in die unterste Ebene in [Server Webverzeichnis/]mod)
+... fertig!
+Du kannst MyOOS [Dumper] nun im Webbrowser durch "https://example.com/mod/" aufrufen,
+um die Installation abzuschließen. Folgt einfach den Instruktionen.
+
Hinweis:
Falls auf Eurem Server das Script keine Verzeichnisse erstellen darf,
+müsst Ihr dies dann von Hand nachholen, da MyOOS [Dumper] die Daten geordnet in +Verzeichnissen ablegt.
+Das Script bricht mit einer entsprechenden Anweisung ab!
+Nachdem Ihr die Verzeichnisse (dem Hinweis entsprechend) erstellt habt, läuft es normal und ohne Einschränkungen.
+ +

Perlskript Anleitung

+Die Meisten haben ein cgi-bin Verzeichnis, in dem Perl ausgeführt werden kann.
+Dies ist meist per Browser über http://www.example.com/cgi-bin/ erreichbar.
+
+Für diesen Fall bitte folgende Schritte durchführen:

+ +1. Rufe im MyOOS [Dumper] die Seite Backup auf und klicke auf "Backup Perl".
+2. Kopiere den Pfad, der hinter Eintrag in crondump.pl für $absolute_path_of_configdir: steht.
+3. Öffne die Datei "crondump.pl" im Editor.
+4. Trage den kopierten Pfad dort bei absolute_path_of_configdir ein (keine Leerzeichen).
+5. Speichere crondump.pl .
+6. Kopiere crondump.pl, sowie perltest.pl und simpletest.pl ins cgi-bin-Verzeichnis (Ascii-Modus im FTP).
+7. Gebe den Dateien die Rechte 755.
+7b. Wenn die Endung cgi gewünscht ist, ändere bei allen 3 Dateien die Endung von pl -> cgi (umbenennen).
+8. Rufe die Konfiguration im MyOOS [Dumper] auf.
+9. Wähle die Seite Cronscript.
+10. Ändere Perl Ausführungspfad in /cgi-bin/ .
+10b. Wenn die Scripte .pl haben, ändere die Dateiendung auf .cgi .
+11. Speichere die Konfiguration.

+ +Fertig, die Skripte lassen sich nun von der Backupseite aufrufen.

+ +Wer Perl in allen Verzeichnissen ausführen kann, dem reichen folgende Schritte:

+ +1. Rufe im MyOOS [Dumper] die Seite Backup auf.
+2. Kopiere den Pfad, der hinter Eintrag in crondump.pl für $absolute_path_of_configdir: steht.
+3. Öffne die Datei "crondump.pl" im Editor.
+4. Trage den kopierten Pfad dort bei absolute_path_of_configdir ein (keine Leerzeichen).
+5. Speichere crondump.pl .
+6. gebe den Datein die Rechte 755.
+6b. Wenn die Endung cgi gewünscht ist, ändere bei allen 3 Dateien die Endung von pl -> cgi (umbenennen).
+(ev. 10b+11 von oben)
+
+ +Windowsuser müssen bei allen Scripten die erste Zeile ändern, dort steht der Pfad von Perl. Beispiel:
+statt: #!/usr/bin/perl -w
+jetzt: #!C:\perl\bin\perl.exe -w
+ +

Bedienung

    + +
    Menü
    +In der obigen Auswahlliste stellt Ihr die Datenbank ein.
    +Alle Aktionen beziehen sich auf die hier eingestellte Datenbank. + +
    Startseite
    +Hier erfahrt Ihr Einiges über Euer System, die verschiedenen, installierten +Versionen und Details über die konfigurierten Datenbanken.
    +Wenn Ihr auf den Datenbanknamen klickt, so seht Ihr eine Auflistung der Tabellen +mit der Anzahl der Einträge, der Größe und das letzte Aktualisierungsdatum. + +
    Konfiguration
    +Hier könnt Ihr eure Konfiguration bearbeiten, abspeichern oder die Ausgangskonfiguration +wieder herstellen. +

      +
    • Konfigurierte Datenbanken: die Auflistung der konfigurierten Datenbanken. Die aktive Datenbank wird in bold gelistet.
    • +
    • Tabellen-Präfix: hier könnt Ihr (für jede Datenbank) einen Präfix angeben. Dies ist ein Filter, der bei Dumps nur die Tabellen berücksichtigt, die mit diesem Präfix beginnen (z.B. alle Tabellen, die mit "phpBB_" beginnen). Wenn alle Tabellen dieser Datenbank gespeichert werden sollen, so lasst das Feld einfach leer.
    • +
    • GZip-Kompression: Hier kann die Kompression aktiviert werden. Empfehlenswert ist die Aktivierung, da die Dateien doch wesentlich kleiner werden und Speicherplatz immer rar ist.
    • +
    • Email mit Dumpfile: Ist diese Option aktiviert, so wird nach abgeschlossenem Backup eine Email mit dem Dump als Anhang verschickt (Vorsicht, Kompression sollte unbedingt an sein, sonst wird der Anhang zu gross und kann evtl. nicht versandt werden!).
    • +
    • Email-Adresse: Empfängeradresse für die Email.
    • +
    • Absender der Email: diese Adresse taucht als Absender in der Email auf.
    • +
    • FTP-Transfer: Ist diese Option aktiviert, so wird nach abgeschlossenem Backup die Backupdatei per FTP versandt.
    • +
    • FTP Server: Die Adresse des FTP-Servers (z. B. ftp.mybackups.de).
    • +
    • FTP Server Port: Der Port des FTP-Servers (in der Regel 21).
    • +
    • FTP User: Der Benutzername des FTP-Accounts.
    • +
    • FTP Passwort: Das Passwort des FTP-Accounts.
    • +
    • FTP Upload-Ordner: Das Verzeichnis, in das die Backupdatei soll (es müssen Upload-Berechtigungen bestehen!).
    • +
    • Automatisches Löschen der Backups: Wenn diese Option aktiviert ist, werden ältere Backups nach den folgenden Regeln automatisch gelöscht.
    • +
    • Anzahl von Backupdateien: Ein Wert > 0 löscht alle Backupdateien, bis auf die hier angegebe Zahl.
    • +
    • Sprache: hier legst du die Sprache für das Interface fest.
    • +
    + +
    Verwaltung
    +Hier werden die eigenlichen Aktionen durchgeführt.
    +Es werden Dir alle Dateien im Backup-Verzeichnis angezeigt. +Für die Aktionen "Restore" und "Delete" muss eine Datei selektiert sein. +
      +
    • Restore: Hiermit wird die Datenbank mit der ausgewählten Backupdatei aktualisiert.
    • +
    • Delete: Hiermit kannst Du die selektierte Backupdatei löschen.
    • +
    • Neues Backup starten: Hier startest Du ein neues Backup (Dump) nach den in der Konfiguration eingestellten Parametern.
    • +
    + +
    Log
    +Hier kannst Du die Logeinträge sehen und löschen. +
    Credits / Hilfe
    +diese Seite. +
\ No newline at end of file diff --git a/msd/language/de/lang.php b/msd/language/de/lang.php new file mode 100644 index 0000000..a821252 --- /dev/null +++ b/msd/language/de/lang.php @@ -0,0 +1,109 @@ +nicht verfügbar'; +$lang['L_VOM'] = 'vom'; +$lang['L_MYSQLVARS'] = 'MySQL-Variablen'; +$lang['L_MYSQLSYS'] = 'MySQL-Befehle'; +$lang['L_STATUS'] = 'Status'; +$lang['L_PROZESSE'] = 'Prozesse'; +$lang['L_INFO_NOVARS'] = 'keine Variablen verfügbar'; +$lang['L_INHALT'] = 'Inhalt'; +$lang['L_INFO_NOSTATUS'] = 'kein Status verfügbar'; +$lang['L_INFO_NOPROCESSES'] = 'keine laufenden Prozesse'; +$lang['L_FM_FREESPACE'] = 'Freier Speicher auf Server'; +$lang['L_LOAD_DATABASE'] = 'Datenbanken neu laden'; +$lang['L_HOME'] = 'Startseite'; +$lang['L_CONFIG'] = 'Konfiguration'; +$lang['L_DUMP'] = 'Sicherung'; +$lang['L_RESTORE'] = 'Wiederherstellung'; +$lang['L_FILE_MANAGE'] = 'Verwaltung'; +$lang['L_LOG'] = 'Log'; +$lang['L_CHOOSE_DB'] = 'Datenbank wählen'; +$lang['L_CREDITS'] = 'Credits / Hilfe'; +$lang['L_MULTI_PART'] = 'Multipart-Backup'; +$lang['L_LOGFILENOTWRITABLE'] = 'Log-File kann nicht geschrieben werden!'; +$lang['L_SQL_ERROR1'] = 'Fehler bei der Anfrage:'; +$lang['L_SQL_ERROR2'] = 'MySQL meldet:'; +$lang['L_UNKNOWN'] = 'unbekannt'; +$lang['L_UNKNOWN_NUMBER_OF_RECORDS'] = 'unbekannt'; +$lang['L_OK'] = 'OK'; +$lang['L_CRON_COMPLETELOG'] = 'Komplette Ausgabe loggen'; +$lang['L_NO'] = 'nein'; +$lang['L_CREATE_DATABASE'] = 'Neue Datenbank anlegen'; +$lang['L_EXPORTFINISHED'] = 'Export beendet.'; +$lang['L_SQL_BROWSER'] = 'SQL-Browser'; +$lang['L_SERVER'] = 'Server'; +$lang['L_MYSQL_CONNECTION_ENCODING'] = 'Standardkodierung des MySQL-Servers'; +$lang['L_TITLE_SHOW_DATA'] = 'Daten anzeigen'; +$lang['L_PRIMARYKEY_CONFIRMDELETE'] = 'Primärschlüssel wirklich löschen?'; +$lang['L_SETPRIMARYKEYSFOR'] = 'Setzen neuer Primärschlüssel für die Tabelle'; +$lang['L_PRIMARYKEY_FIELD'] = 'Schlüsselfeld'; +$lang['L_PRIMARYKEYS_SAVE'] = 'Primärschlüssel speichern'; +$lang['L_CANCEL'] = 'Abbruch'; +$lang['L_VISIT_HOMEPAGE'] = 'Besuchen Sie die Homepage'; +$lang['L_SECONDS'] = 'Sekunden'; +$lang['L_BACKUPS'] = 'Sicherungsdateien'; +$lang['L_MINUTES'] = 'Minuten'; +$lang['L_PAGE_REFRESHS'] = 'Seitenaufrufe'; +$lang['L_MINUTE'] = 'Minute'; +$lang['L_SETKEYSFOR'] = 'Setzen neuer Indizes für die Tabelle'; +$lang['L_KEY_CONFIRMDELETE'] = 'Index wirklich löschen?'; diff --git a/msd/language/de/lang_config_overview.php b/msd/language/de/lang_config_overview.php new file mode 100644 index 0000000..738b6bd --- /dev/null +++ b/msd/language/de/lang_config_overview.php @@ -0,0 +1,129 @@ +%s
in %s'; +$lang['L_FTP'] = 'FTP'; +$lang['L_SFTP_SEND_TO'] = 'an %s
in %s'; +$lang['L_SFTP'] = 'SFTP'; +$lang['L_EMAIL_CC'] = 'CC-Empfänger'; +$lang['L_NAME'] = 'Name'; +$lang['L_CONFIRM_CONFIGFILE_DELETE'] = 'Soll die Konfigurationsdatei %s wirklich gelöscht werden?'; +$lang['L_ERROR_DELETING_CONFIGFILE'] = 'Fehler: die Konfigurationsdatei %s konnte nicht gelöscht werden!'; +$lang['L_SUCCESS_DELETING_CONFIGFILE'] = 'Die Konfigurationsdatei %s wurde erfolgreich gelöscht.'; +$lang['L_SUCCESS_CONFIGFILE_CREATED'] = 'Die Konfigurationsdatei %s wurde erfolgreich angelegt.'; +$lang['L_ERROR_CONFIGFILE_NAME'] = 'Der Dateiname "%s" enthält ungültige Zeichen.'; +$lang['L_CREATE_CONFIGFILE'] = 'Eine neue Konfigurationsdatei anlegen'; +$lang['L_ERROR_LOADING_CONFIGFILE'] = 'Die Konfigurationsdatei "%s" konnte nicht geladen werden.'; +$lang['L_BACKUP_DBS_PHP'] = 'zu sichernde DBs (PHP)'; +$lang['L_BACKUP_DBS_PERL'] = 'zu sichernde DBs (PERL)'; +$lang['L_CRON_COMMENT'] = 'Kommentar eingeben'; +$lang['L_AUTODETECT'] = 'automatisch ermitteln'; diff --git a/msd/language/de/lang_dump.php b/msd/language/de/lang_dump.php new file mode 100644 index 0000000..be8af55 --- /dev/null +++ b/msd/language/de/lang_dump.php @@ -0,0 +1,57 @@ +%s` gefunden werden.'; +$lang['L_DUMP_ENDERGEBNIS'] = 'Es wurden %s Tabellen mit insgesamt %s Datensätzen gesichert.
'; +$lang['L_MAILERROR'] = 'Leider ist beim Verschicken der E-Mail ein Fehler aufgetreten!'; +$lang['L_EMAILBODY_ATTACH'] = 'In der Anlage finden Sie die Sicherung Ihrer MySQL-Datenbank.
Sicherung der Datenbank `%s` +

Folgende Datei wurde erzeugt:

%s

Viele Grüße

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_MP_NOATTACH'] = 'Es wurde eine Multipart-Sicherung erstellt.
Die Sicherungen werden nicht als Anhang mitgeliefert!
Sicherung der Datenbank `%s` +

Folgende Dateien wurden erzeugt:

%s


Viele Grüße

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_MP_ATTACH'] = 'Es wurde eine Multipart-Sicherung erstellt.
Die Sicherungen werden in separaten E-Mails als Anhang geliefert!
Sicherung der Datenbank `%s` +

Folgende Dateien wurden erzeugt:

%s


Viele Grüße

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_FOOTER'] = '


Viele Grüße

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_TOOBIG'] = 'Die Sicherung überschreitet die Maximalgröße von %s und wurde daher nicht angehängt.
Sicherung der Datenbank `%s` +

Folgende Datei wurde erzeugt:

%s +

Viele Grüße

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_NOATTACH'] = 'Das Backup wurde nicht angehängt.
Sicherung der Datenbank `%s` +

Folgende Datei wurde erzeugt:

%s +

Viele Grüße

MyOOS [Dumper]
'; +$lang['L_EMAIL_ONLY_ATTACHMENT'] = ' ... nur der Anhang'; +$lang['L_TABLESELECTION'] = 'Tabellenauswahl'; +$lang['L_SELECTALL'] = 'alle auswählen'; +$lang['L_DESELECTALL'] = 'Auswahl aufheben'; +$lang['L_STARTDUMP'] = 'Backup starten'; +$lang['L_LASTBUFROM'] = 'letztes Update vom'; +$lang['L_NOT_SUPPORTED'] = 'Dieses Backup unterstützt diese Funktion nicht.'; +$lang['L_MULTIDUMP'] = 'Multidump: Es wurden %d Datenbanken gesichert.'; +$lang['L_FILESENDFTP'] = 'versende File via FTP... bitte habe etwas Geduld. '; +$lang['L_FTPCONNERROR'] = 'FTP-Verbindung nicht hergestellt! Verbindung mit '; +$lang['L_FTPCONNERROR1'] = ' als Benutzer '; +$lang['L_FTPCONNERROR2'] = ' nicht möglich'; +$lang['L_FTPCONNERROR3'] = 'FTP-Upload war fehlerhaft! '; +$lang['L_FTPCONNECTED1'] = 'Verbunden mit '; +$lang['L_FTPCONNECTED2'] = ' auf '; +$lang['L_FTPCONNECTED3'] = ' geschrieben'; +$lang['L_FILESENDSFTP'] = 'versende File via SFTP... bitte habe etwas Geduld. '; +$lang['L_SFTPCONNERROR'] = 'SFTP-Verbindung nicht hergestellt! Verbindung mit '; +$lang['L_NR_TABLES_SELECTED'] = '- mit %s gewählten Tabellen'; +$lang['L_NR_TABLES_OPTIMIZED'] = '%s Tabellen wurden optimiert.'; +$lang['L_DUMP_ERRORS'] = '

%s Fehler aufgetreten: anzeigen

'; +$lang['L_FATAL_ERROR_DUMP'] = "Schwerwiegender Fehler: die CREATE-Anweisung der Tabelle '%s' in der Datenbank '%s' konnte nicht gelesen werden!"; diff --git a/msd/language/de/lang_filemanagement.php b/msd/language/de/lang_filemanagement.php new file mode 100644 index 0000000..2df79dc --- /dev/null +++ b/msd/language/de/lang_filemanagement.php @@ -0,0 +1,80 @@ +%s"'; +$lang['L_DELETE_FILE_ERROR'] = 'Die Datei "%s" konnte nicht gelöscht werden!'; +$lang['L_FM_DUMP_HEADER'] = 'Sicherung'; +$lang['L_DOCRONBUTTON'] = 'Perl-Cronscript ausführen'; +$lang['L_DOPERLTEST'] = 'Perl-Module testen'; +$lang['L_DOSIMPLETEST'] = 'Perl testen'; +$lang['L_PERLOUTPUT1'] = 'Eintrag in crondump.pl für absolute_path_of_configdir'; +$lang['L_PERLOUTPUT2'] = 'Aufruf im Browser oder für externen Cronjob'; +$lang['L_PERLOUTPUT3'] = 'Aufruf in der Shell oder für die Crontab'; +$lang['L_RESTORE_OF_TABLES'] = 'Wiederherstellen bestimmter Tabellen'; +$lang['L_CONVERTER'] = 'Backup-Konverter'; +$lang['L_CONVERT_FILE'] = 'zu konvertierende Datei'; +$lang['L_CONVERT_FILENAME'] = 'Name der Zieldatei (ohne Endung)'; +$lang['L_CONVERTING'] = 'Konvertierung'; +$lang['L_CONVERT_FILEREAD'] = "Datei '%s' wird eingelesen"; +$lang['L_CONVERT_FINISHED'] = "Konvertierung abgeschlossen, '%s' wurde erzeugt."; +$lang['L_NO_MOD_BACKUPFILE'] = 'Dateien anderer Programme'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Maximale Dateigröße'; +$lang['L_MAX_UPLOAD_SIZE_INFO'] = 'Wenn Ihre Backup-Datei größer als das angegebene Limit ist, dann müssen Sie diese per FTP in den "work/backup"-Ordner hochladen. +Danach wird diese Datei hier in der Verwaltung angezeigt und lässt sich für eine Wiederherstellung auswählen.'; +$lang['L_ENCODING'] = 'Kodierung'; +$lang['L_FM_CHOOSE_ENCODING'] = 'Kodierung der Backupdatei wählen'; +$lang['L_CHOOSE_CHARSET'] = 'Leider konnte nicht automatisch ermittelt werden mit welchem Zeichensatz diese Backupdatei seinerzeit angelegt wurde. +
Sie müssen die Kodierung, in der Zeichenketten in dieser Datei vorliegen, manuell angeben. +
Danach stellt MyOOS [Dumper] die Verbindungskennung zum MySQL-Server auf den ausgewählten Zeichensatz und beginnt mit der Wiederherstellung der Daten. +
Sollten Sie nach der Wiederherstellung Probleme mit Sonderzeichen entdecken, so können Sie versuchen, das Backup mit einer anderen Zeichensatzauswahl wiederherzustellen. +
Viel Glück. ;)'; +$lang['L_DOWNLOAD_FILE'] = 'Datei herunterladen'; +$lang['L_BACKUP_NOT_POSSIBLE'] = 'Eine Sicherung der Systemdatenbank `%s` ist nicht möglich!'; diff --git a/msd/language/de/lang_help.php b/msd/language/de/lang_help.php new file mode 100644 index 0000000..effeb21 --- /dev/null +++ b/msd/language/de/lang_help.php @@ -0,0 +1,41 @@ +die Installation ist abgeschlossen --> starte MyOOS [Dumper]
'; +$lang['L_INSTALL_TOMENU'] = 'zum Hauptmenü'; +$lang['L_INSTALLMENU'] = 'Hauptmenü'; +$lang['L_STEP'] = 'Schritt'; +$lang['L_INSTALL'] = 'Installation'; +$lang['L_UNINSTALL'] = 'Deinstallation'; +$lang['L_TOOLS'] = 'Tools'; +$lang['L_EDITCONF'] = 'Konfiguration bearbeiten'; +$lang['L_OSWEITER'] = 'ohne Speichern weiter'; +$lang['L_ERRORMAN'] = 'Fehler beim Schreiben der Konfiguration!
Bitte editieren Sie die Datei '; +$lang['L_MANUELL'] = 'manuell'; +$lang['L_CREATEDIRS'] = 'erstelle Verzeichnisse'; +$lang['L_INSTALL_CONTINUE'] = 'mit der Installation fortfahren'; +$lang['L_CONNECTTOMYSQL'] = ' zu MySQL verbinden '; +$lang['L_DBPARAMETER'] = 'Datenbank-Parameter'; +$lang['L_CONFIGNOTWRITABLE'] = 'Die Datei "config.php" ist nicht beschreibbar. +Geben Sie ihr mit einem FTP-Programm entsprechende Rechte, z. B. den CHMod-Wert 0777.'; +$lang['L_DBCONNECTION'] = 'Datenbank-Verbindung'; +$lang['L_CONNECTIONERROR'] = 'Fehler: Es konnte keine Verbindung herstellt werden.'; +$lang['L_CONNECTION_OK'] = 'Datenbank-Verbindung wurde hergestellt.'; +$lang['L_SAVEANDCONTINUE'] = 'speichern und Installation fortsetzen'; +$lang['L_CONFBASIC'] = 'Grundeinstellungen'; +$lang['L_INSTALL_STEP2FINISHED'] = 'Die Einstellungen wurden erfolgreich gesichert.'; +$lang['L_INSTALL_STEP2_1'] = 'Installation mit Standardkonfiguration fortsetzen'; +$lang['L_LASTSTEP'] = 'Abschluss der Installation'; +$lang['L_IDOMANUAL'] = 'Ich erstelle die Verzeichnisse manuell'; +$lang['L_DOFROM'] = 'ausgehend von'; +$lang['L_FTPMODE2'] = 'Erstelle die Verzeichnisse per FTP:'; +$lang['L_CONNECT'] = 'verbinden'; +$lang['L_DIRS_CREATED'] = 'Die Verzeichnisse wurden ordnungsgemäß erstellt.'; +$lang['L_CONNECT_TO'] = 'verbinde zu'; +$lang['L_CHANGEDIR'] = 'Wechsel ins Verzeichnis'; +$lang['L_CHANGEDIRERROR'] = 'Wechsel ins Verzeichnis nicht möglich'; +$lang['L_FTP_OK'] = 'FTP-Parameter sind ok'; +$lang['L_SFTP_OK'] = 'FTP-Parameter sind ok'; +$lang['L_CREATEDIRS2'] = 'Verzeichnisse erstellen'; +$lang['L_FTP_NOTCONNECTED'] = 'FTP-Verbindung nicht hergestellt!'; +$lang['L_CONNWITH'] = 'Verbindung mit'; +$lang['L_ASUSER'] = 'als Benutzer'; +$lang['L_NOTPOSSIBLE'] = 'nicht möglich'; +$lang['L_DIRCR1'] = 'erstelle Arbeitsverzeichnis'; +$lang['L_DIRCR2'] = 'erstelle Backup-Verzeichnis'; +$lang['L_DIRCR4'] = 'erstelle Log-Verzeichnis'; +$lang['L_DIRCR5'] = 'erstelle Konfigurationsverzeichnis'; +$lang['L_INDIR'] = 'bin im Verzeichnis'; +$lang['L_CHECK_DIRS'] = 'Verzeichnisse überprüfen'; +$lang['L_DISABLEDFUNCTIONS'] = 'Abgeschaltete Funktionen'; +$lang['L_NOFTPPOSSIBLE'] = 'Es stehen keine FTP-Funktionen zur Verfügung!'; +$lang['L_NOGZPOSSIBLE'] = 'Es stehen keine Kompressions-Funktionen zur Verfügung!'; +$lang['L_UI1'] = 'Es werden alle Arbeitsverzeichnisse incl. den darin enthaltenen Backups gelöscht.'; +$lang['L_UI2'] = 'Sind Sie sicher, dass Sie das möchten?'; +$lang['L_UI3'] = 'Nein, sofort abbrechen'; +$lang['L_UI4'] = 'ja, bitte fortfahren'; +$lang['L_UI5'] = 'lösche Arbeitsverzeichnis'; +$lang['L_UI6'] = 'alles wurde erfolgreich gelöscht.'; +$lang['L_UI7'] = 'Bitte löschen Sie das Skriptverzeichnis'; +$lang['L_UI8'] = 'eine Ebene nach oben'; +$lang['L_UI9'] = 'Ein Fehler trat auf, löschen war nicht möglich

Fehler bei Verzeichnis '; +$lang['L_IMPORT'] = 'Konfiguration importieren'; +$lang['L_IMPORT3'] = 'Die Konfiguration wurde geladen...'; +$lang['L_IMPORT4'] = 'Die Konfiguration wurde gesichert.'; +$lang['L_IMPORT5'] = 'MyOOS [Dumper] starten'; +$lang['L_IMPORT6'] = 'Installations-Menü'; +$lang['L_IMPORT7'] = 'Konfiguration hochladen'; +$lang['L_IMPORT8'] = 'zurück zum Upload'; +$lang['L_IMPORT9'] = 'Dies ist keine Konfigurationssicherung!'; +$lang['L_IMPORT10'] = 'Die Konfiguration wurde erfolgreich hochgeladen...'; +$lang['L_IMPORT11'] = 'Fehler: Es gab Probleme beim Schreiben der sql_statements.'; +$lang['L_IMPORT12'] = 'Fehler: Es gab Probleme beim Schreiben der config.php.'; +$lang['L_INSTALL_HELP_PORT'] = '(leer = Standardport)'; +$lang['L_INSTALL_HELP_SOCKET'] = '(leer = Standardsocket)'; +$lang['L_TRYAGAIN'] = 'noch einmal versuchen'; +$lang['L_SOCKET'] = 'Socket'; +$lang['L_PORT'] = 'Port'; +$lang['L_FOUND_DB'] = 'gefundene DB: '; +$lang['L_FM_FILEUPLOAD'] = 'Datei hochladen'; +$lang['L_PASS'] = 'Passwort'; +$lang['L_NO_DB_FOUND_INFO'] = 'Die Verbindung zur Datenbank konnte erfolgreich hergestellt werden.
+Ihre Zugangsdaten sind gültig und wurden vom MySQL-Server akzeptiert.
+Leider konnte MyOOS [Dumper] keine Datenbank finden.
+Die automatische Erkennung per Programm ist bei manchen Hostern gesperrt.
+Sie müssen Ihre Datenbank nach dem Abschluß der Installation unter dem Menüpunkt "Konfiguration" "Verbindungsparameter einblenden" angeben.
+Bitte begeben Sie sich nach Abschluß der Installation umgehend dort hin und tragen den Namen Ihrer Datenbank dort ein.'; +$lang['L_ENTER_DB_INFO'] = 'Klicken Sie zuerst auf den Button "zu MySQL verbinden". Nur wenn daraufhin keine Datenbank erkannt werden konnte, ist hier eine Angabe notwendig.'; diff --git a/msd/language/de/lang_log.php b/msd/language/de/lang_log.php new file mode 100644 index 0000000..645f898 --- /dev/null +++ b/msd/language/de/lang_log.php @@ -0,0 +1,7 @@ +Bitte erzeugen Sie die Dateien manuell mit folgendem Inhalt'; +$lang['L_HTACC_CHECK_ERROR'] = 'Es konnte nicht überprüft werden, ob das Programm geschützt ist!
Der simulierte externe Zugriff konnte nicht ausgeführt werden.'; +$lang['L_HTACC_NOT_NEEDED'] = 'Das Programm ist durch übergeordnete Berechtigungen geschützt; ein lokaler Verzeichnisschutz ist nicht erforderlich.'; +$lang['L_HTACC_COMPLETE'] = 'Das Programm ist geschützt, der Verzeichnisschutz ist vollständig.'; +$lang['L_HTACC_INCOMPLETE'] = 'Das Programm ist nicht geschützt, der Verzeichnisschutz ist unvollständig!'; +$lang['L_HTACC_PROPOSED'] = 'Das Programm ist nicht geschützt, ein Verzeichnisschutz wird dringend empfohlen!'; +$lang['L_HTACC_EDIT'] = '.htaccess editieren'; +$lang['L_HTACCESS18'] = '.htaccess erstellen in '; +$lang['L_HTACCESS19'] = 'Neu laden '; +$lang['L_HTACCESS20'] = 'Skript ausführen'; +$lang['L_HTACCESS21'] = 'Handler zufügen'; +$lang['L_HTACCESS22'] = 'Ausführbar machen'; +$lang['L_HTACCESS23'] = 'Verzeichnis-Listing'; +$lang['L_HTACCESS24'] = 'Error-Dokument'; +$lang['L_HTACCESS25'] = 'Rewrite aktivieren'; +$lang['L_HTACCESS26'] = 'Deny / Allow'; +$lang['L_HTACCESS27'] = 'Redirect'; +$lang['L_HTACCESS28'] = 'Error-Log'; +$lang['L_HTACCESS29'] = 'weitere Beispiele und Dokumentation'; +$lang['L_HTACCESS30'] = 'Provider'; +$lang['L_HTACCESS31'] = 'Allgemein'; +$lang['L_HTACCESS32'] = 'Achtung! Die .htaccess hat eine direkte Auswirkung auf den Browser.
Bei falscher Anwendung sind die Seiten nicht mehr erreichbar.'; +$lang['L_DISABLEDFUNCTIONS'] = 'Abgeschaltete Funktionen'; +$lang['L_NOGZPOSSIBLE'] = 'Da zlib nicht installiert ist, stehen keine GZip-Funktionen zur Verfügung!'; +$lang['L_DELETE_HTACCESS'] = 'Verzeichnisschutz entfernen (.htaccess löschen)'; +$lang['L_WRONG_RIGHTS'] = "Die Datei oder das Verzeichnis '%s' ist für mich nicht beschreibbar.
+Entweder hat sie den falschen Besitzer (Owner) oder die falschen Rechte (Chmod).
+Bitte setzen Sie die richtigen Attribute mit Ihrem FTP-Programm.
+Die Datei oder das Verzeichnis benötigt die Rechte %s.
"; +$lang['L_CANT_CREATE_DIR'] = "Ich konnte das Verzeichnis '%s' nicht erstellen. +Bitte erstellen Sie es mit Ihrem FTP-Programm."; +$lang['L_TABLE_TYPE'] = 'Typ'; +$lang['L_CHECK'] = 'prüfen'; +$lang['L_OS'] = 'Betriebssystem'; +$lang['L_MOD_VERSION'] = 'MyOOS [Dumper] - Version'; +$lang['L_NEW_MOD_VERSION'] = 'Neue Version'; +$lang['L_NEW_MOD_VERSION_INFO'] = 'Es ist eine neue Version von MyOOS [Dumper] verfügbar.'; +$lang['L_UPDATED_IMPORTANT'] = 'Wichtig: Vor der Aktualisierung bitte Ihre Dateien sichern.'; +$lang['L_UPDATE'] = 'Jetzt aktualisieren'; +$lang['L_MYSQL_VERSION'] = 'MySQL-Version'; +$lang['L_PHP_VERSION'] = 'PHP-Version'; +$lang['L_MAX_EXECUTION_TIME'] = 'Maximale Ausführungszeit'; +$lang['L_PHP_EXTENSIONS'] = 'PHP-Erweiterungen'; +$lang['L_MEMORY'] = 'Speicher'; +$lang['L_FILE_MISSING'] = 'konnte Datei nicht finden'; +$lang['L_INSTALLING_UPDATES'] = 'Installation von Updates'; +$lang['L_UPDATE_SUCCESSFUL'] = 'Aktualisierung erfolgreich'; +$lang['L_UPDATE_FAILED'] = 'Aktualisierung fehlgeschlagen'; +$lang['L_UP_TO_DATE'] = 'Aktuelle Version ist auf dem neuesten Stand'; diff --git a/msd/language/de/lang_restore.php b/msd/language/de/lang_restore.php new file mode 100644 index 0000000..2d4b5d1 --- /dev/null +++ b/msd/language/de/lang_restore.php @@ -0,0 +1,19 @@ +%d Tabellen angelegt.'; +$lang['L_FILE_MISSING'] = 'konnte Datei nicht finden'; +$lang['L_RESTORE_DB'] = "Datenbank '%s' auf Server '%s'."; +$lang['L_RESTORE_COMPLETE'] = '%s Tabellen wurden angelegt.'; +$lang['L_RESTORE_RUN1'] = '
Es wurden bisher %s von %s Datensätzen erfolgreich eingetragen.'; +$lang['L_RESTORE_RUN2'] = "
Momentan werden Daten der Tabelle '%s' analysiert.

"; +$lang['L_RESTORE_COMPLETE2'] = '%s Datensätze wurden eingetragen.'; +$lang['L_RESTORE_TABLES_COMPLETED'] = 'Es wurden bisher %d von %d Tabellen angelegt.'; +$lang['L_RESTORE_TOTAL_COMPLETE'] = '
Herzlichen Glückwunsch.

Die Datenbank wurde komplett wiederhergestellt.
Alle Daten aus der Backup-Datei wurden erfolgreich in die Datenbank eingetragen.

Alles fertig. :-)'; +$lang['L_DB_SELECT_ERROR'] = "
Fehler:
Auswahl der Datenbank '"; +$lang['L_DB_SELECT_ERROR2'] = "' fehlgeschlagen!"; +$lang['L_FILE_OPEN_ERROR'] = 'Fehler: Die Datei konnte nicht geöffnet werden.'; +$lang['L_PROGRESS_OVER_ALL'] = 'Fortschritt gesamt'; +$lang['L_BACK_TO_OVERVIEW'] = 'Datenbank-Übersicht'; +$lang['L_RESTORE_RUN0'] = '
Es wurden bisher %s Datensätze erfolgreich eingetragen.'; +$lang['L_UNKNOWN_SQLCOMMAND'] = 'Unbekannter SQL-Befehl:'; +$lang['L_NOTICES'] = 'Hinweise'; diff --git a/msd/language/de/lang_sql.php b/msd/language/de/lang_sql.php new file mode 100644 index 0000000..f01dfd8 --- /dev/null +++ b/msd/language/de/lang_sql.php @@ -0,0 +1,190 @@ +%s Zeilen exportiert'; +$lang['L_CSV_FIELDCOUNT_NOMATCH'] = 'Die Anzahl der Tabellenfelder stimmen nicht mit den zu importierenden Daten überein (%d statt %d).'; +$lang['L_CSV_FIELDSLINES'] = '%d Felder ermittelt, insgesamt %d Zeilen'; +$lang['L_CSV_ERRORCREATETABLE'] = 'Fehler beim Erstellen der Tabelle `%s`!'; +$lang['L_FM_UPLOADFILEREQUEST'] = 'Bitte geben Sie eine Datei an.'; +$lang['L_CSV_NODATA'] = 'Keine Daten zum Importieren gefunden!'; +$lang['L_SQLLIB_GENERALFUNCTIONS'] = 'allgemeine Funktionen'; +$lang['L_SQLLIB_RESETAUTO'] = 'Auto-Wert zurücksetzen'; +$lang['L_SQLLIB_BOARDS'] = 'Boards'; +$lang['L_SQLLIB_DEACTIVATEBOARD'] = 'Board deaktivieren'; +$lang['L_SQLLIB_ACTIVATEBOARD'] = 'Board aktivieren'; +$lang['L_SQL_NOTABLESSELECTED'] = 'Es sind keine Tabellen ausgewählt!'; +$lang['L_TOOLS'] = 'Tools'; +$lang['L_TOOLS_TOOLBOX'] = 'Datenbank auswählen / Datenbankfunktionen / Im- und Export '; +$lang['L_SQL_OPENFILE'] = 'SQL-Datei öffnen'; +$lang['L_SQL_OPENFILE_BUTTON'] = 'Hochaden'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Maximale Dateigröße'; +$lang['L_SQL_SEARCH'] = 'Suche'; +$lang['L_SQL_SEARCHWORDS'] = 'Suchbegriff(e)'; +$lang['L_START_SQL_SEARCH'] = 'Suche starten'; +$lang['L_RESET_SEARCHWORDS'] = 'Eingabe zurücksetzen'; +$lang['L_SEARCH_OPTIONS'] = 'Suchoptionen'; +$lang['L_SEARCH_RESULTS'] = 'Die Suche nach "%s" in der Tabelle "%s" lieferte folgende Treffer'; +$lang['L_SEARCH_NO_RESULTS'] = 'Die Suche nach "%s" in der Tabelle "%s" liefert keine Ergebnisse!'; +$lang['L_NO_ENTRIES'] = 'Die Tabelle "%s" ist leer und hat keine Einträge.'; +$lang['L_SEARCH_ACCESS_KEYS'] = 'Blättern: vor=ALT+V, zurück=ALT+C'; +$lang['L_SEARCH_OPTIONS_OR'] = 'eine Spalte muss mindestens einen Suchbegriff enthalten (ODER-Suche)'; +$lang['L_SEARCH_OPTIONS_CONCAT'] = 'ein Datensatz muss alle Suchbegriffe enthalten, diese können aber in beliebigen Spalten sein (Rechenintensiv!)'; +$lang['L_SEARCH_OPTIONS_AND'] = 'eine Spalte muss alle Suchbegriffe enthalten (UND-Suche)'; +$lang['L_SEARCH_IN_TABLE'] = 'Suche in Tabelle'; +$lang['L_ERROR_NO_FIELDS'] = 'Fehler bei Suche: es konnte nicht ermittelt werden, welche Felder die Tabelle "%s" hat!'; +$lang['L_SQL_EDIT_TABLESTRUCTURE'] = 'Tabellenstruktur bearbeiten'; +$lang['L_DEFAULT_CHARSET'] = 'Standardzeichensatz'; +$lang['L_TITLE_KEY_PRIMARY'] = 'Primärschlüssel'; +$lang['L_TITLE_KEY_UNIQUE'] = 'Eindeutiger Schlüssel'; +$lang['L_TITLE_INDEX'] = 'Index'; +$lang['L_TITLE_KEY_FULLTEXT'] = 'Volltextschlüssel'; +$lang['L_TITLE_NOKEY'] = 'Kein Schlüssel'; +$lang['L_TITLE_SEARCH'] = 'Suche'; +$lang['L_TITLE_MYSQL_HELP'] = 'MySQL Dokumentation'; +$lang['L_TITLE_UPLOAD'] = 'SQL-Datei hochladen'; +$lang['L_PRIMARYKEY_DELETED'] = 'Primärschlüssel gelöscht'; +$lang['L_PRIMARYKEY_NOTFOUND'] = 'Primärschlüssel nicht gefunden'; +$lang['L_PRIMARYKEYS_CHANGED'] = 'Primärschlüssel geändert'; +$lang['L_PRIMARYKEYS_CHANGINGERROR'] = 'Fehler beim Ändern der Primärschlüssel'; +$lang['L_SQL_VIEW_COMPACT'] = 'Ansicht: kompakt'; +$lang['L_SQL_VIEW_STANDARD'] = 'Ansicht: normal'; +$lang['L_FIELDS_OF_TABLE'] = 'Felder der Tabelle'; +$lang['L_ENGINE'] = 'Typ'; +$lang['L_USERNAME'] = 'Benutzername'; +$lang['L_PASSWORD'] = 'Kennwort'; +$lang['L_PASSWORD_REPEAT'] = 'Kennwort (Wiederholung)'; +$lang['L_INFO_SIZE'] = 'Größe'; +$lang['L_TABLE_TYPE'] = 'Typ'; +$lang['L_KEY_DELETED'] = 'Index gelöscht'; +$lang['L_KEY_DELETEERROR'] = 'Fehler beim Löschen des Index'; +$lang['L_KEY_ADDED'] = 'Index angelegt'; +$lang['L_KEY_ADDERROR'] = 'Fehler beim Anlegen des Index'; diff --git a/msd/language/de_du/help.html b/msd/language/de_du/help.html new file mode 100644 index 0000000..ec99117 --- /dev/null +++ b/msd/language/de_du/help.html @@ -0,0 +1,150 @@ +
+

MyOOS [Dumper] based on MySQLDumper 1.24.4

+ +

Über dieses Projekt

+

MyOOS [Dumper] ist eine verbesserte Version von MySQLDumper 1.24.4 (24. Januar 2011). Diese Weiterentwicklung berücksichtig die Entwicklung von PHP.

+

Vor allem Stabilität, Sicherheit und Handhabung stehen bei MyOOS [Dumper] maßgeblich im Vordergrund. Aber auch ein ansprechendes Template wird mitgeliefert, welches beliebig bearbeitet und an eigene Bedürfnisse angepasst werden kann.

+ + +

MyOOS [Dumper] ist ein Sicherungsprogramm für MySQL-Datenbanken, geschrieben in PHP und Perl. Damit können Sicherungskopien der Daten (Shop, Blog, usw.) erstellt und bei Bedarf auch wieder hergestellt werden. Besonders bei Web-Space ohne Shell-Zugang bietet sich MyOOS [Dumper] als sinnvolle Alternative an.

+ +

Die Idee für MySQLDumper kam von Daniel Schlichtholz. Er eröffnete 2004 das Forum MySQLDumper, woraufhin Programmierer neue Skripte schrieben und bestehende erweiterten.

+

Offizielle Entwicklersite: www.mysqldumper.de

+ + + +

Wunschliste / Künftige Attraktionen

+

Hast du Verbesserungsvorschläge? Zögere nicht, das Entwicklerteam über das Forum https://foren.myoos.de/viewforum.php?f=41 zu kontaktieren.

+ + +

Mitwirken

+

Wenn du uns dabei helfen möchtest, das MyOOS Projekt zu verbessern, freuen wir uns hier auf deine Pull Requests via GitHub.

+https://github.com/r23/MyOOS-Dumper/ + + +

Finanzielle Unterstützung

+

Man kann mit PayPal Me
+https://www.paypal.com/paypalme/r23de?locale.x=de_DE

+ +

oder über den QR Code
+Finanzielle Unterstützung für MyOOS [Dumper]

+ +Geld an das MyOOS Projekt senden.
+ +

Wir wünschen Dir viel Vergnügen mit diesem Projekt.

Das MyOOS [Dumper]-Team

+ +MyOOS [Dumper]
+MyOOS [Dumper]
+ + +

MyOOS [Dumper] Hilfe

+ +

Download

+

Aktuelle Versionen erhälst du immer über GitHub
+https://github.com/r23/MyOOS-Dumper/releases

+ + +

Systemvoraussetzung

+

Das Script arbeitet auf jedem Server (Windows, Linux, ...)
+mit PHP >= Version 7.4 mit GZip-Unterstützung, MySQL (ab Version 4.1), JavaScript (muss aktiviert sein).

+

Aus dem MyOOS Archiv den Ordner mod in einen separaten Arbeitsordner kopieren.

+ +

Installation

+Die Installation geht einfach von statten. +

Aus dem MyOOS Archiv den Ordner mod in einen beliebigen Ordner kopieren.
+Ladet alle Dateien aus dem ordner mod auf deinen Webserver hoch. (z. B. in die unterste Ebene in [Server Webverzeichnis/]mod)
+... fertig!
+Du kannst MyOOS [Dumper] nun im Webbrowser durch "https://example.com/mod/" aufrufen,
+um die Installation abzuschließen. Folgt einfach den Instruktionen.
+
Hinweis:
Falls auf Eurem Server das Script keine Verzeichnisse erstellen darf,
+müsst Ihr dies dann von Hand nachholen, da MyOOS [Dumper] die Daten geordnet in +Verzeichnissen ablegt.
+Das Script bricht mit einer entsprechenden Anweisung ab!
+Nachdem Ihr die Verzeichnisse (dem Hinweis entsprechend) erstellt habt, läuft es normal und ohne Einschränkungen.
+ +

Perlskript Anleitung

+Die Meisten haben ein cgi-bin Verzeichnis, in dem Perl ausgeführt werden kann.
+Dies ist meist per Browser über http://www.example.com/cgi-bin/ erreichbar.
+
+Für diesen Fall bitte folgende Schritte durchführen:

+ +1. Rufe im MyOOS [Dumper] die Seite Backup auf und klicke auf "Backup Perl".
+2. Kopiere den Pfad, der hinter Eintrag in crondump.pl für $absolute_path_of_configdir: steht.
+3. Öffne die Datei "crondump.pl" im Editor.
+4. Trage den kopierten Pfad dort bei absolute_path_of_configdir ein (keine Leerzeichen).
+5. Speichere crondump.pl .
+6. Kopiere crondump.pl, sowie perltest.pl und simpletest.pl ins cgi-bin-Verzeichnis (Ascii-Modus im FTP).
+7. Gebe den Dateien die Rechte 755.
+7b. Wenn die Endung cgi gewünscht ist, ändere bei allen 3 Dateien die Endung von pl -> cgi (umbenennen).
+8. Rufe die Konfiguration im MyOOS [Dumper] auf.
+9. Wähle die Seite Cronscript.
+10. Ändere Perl Ausführungspfad in /cgi-bin/ .
+10b. Wenn die Scripte .pl haben, ändere die Dateiendung auf .cgi .
+11. Speichere die Konfiguration.

+ +Fertig, die Skripte lassen sich nun von der Backupseite aufrufen.

+ +Wer Perl in allen Verzeichnissen ausführen kann, dem reichen folgende Schritte:

+ +1. Rufe im MyOOS [Dumper] die Seite Backup auf.
+2. Kopiere den Pfad, der hinter Eintrag in crondump.pl für $absolute_path_of_configdir: steht.
+3. Öffne die Datei "crondump.pl" im Editor.
+4. Trage den kopierten Pfad dort bei absolute_path_of_configdir ein (keine Leerzeichen).
+5. Speichere crondump.pl .
+6. gebe den Datein die Rechte 755.
+6b. Wenn die Endung cgi gewünscht ist, ändere bei allen 3 Dateien die Endung von pl -> cgi (umbenennen).
+(ev. 10b+11 von oben)
+
+ +Windowsuser müssen bei allen Scripten die erste Zeile ändern, dort steht der Pfad von Perl. Beispiel:
+statt: #!/usr/bin/perl -w
+jetzt: #!C:\perl\bin\perl.exe -w
+ +

Bedienung

    + +
    Menü
    +In der obigen Auswahlliste stellt Ihr die Datenbank ein.
    +Alle Aktionen beziehen sich auf die hier eingestellte Datenbank. + +
    Startseite
    +Hier erfahrt Ihr Einiges über Euer System, die verschiedenen, installierten +Versionen und Details über die konfigurierten Datenbanken.
    +Wenn Ihr auf den Datenbanknamen klickt, so seht Ihr eine Auflistung der Tabellen +mit der Anzahl der Einträge, der Größe und das letzte Aktualisierungsdatum. + +
    Konfiguration
    +Hier könnt Ihr eure Konfiguration bearbeiten, abspeichern oder die Ausgangskonfiguration +wieder herstellen. +

      +
    • Konfigurierte Datenbanken: die Auflistung der konfigurierten Datenbanken. Die aktive Datenbank wird in bold gelistet.
    • +
    • Tabellen-Präfix: hier könnt Ihr (für jede Datenbank) einen Präfix angeben. Dies ist ein Filter, der bei Dumps nur die Tabellen berücksichtigt, die mit diesem Präfix beginnen (z.B. alle Tabellen, die mit "phpBB_" beginnen). Wenn alle Tabellen dieser Datenbank gespeichert werden sollen, so lasst das Feld einfach leer.
    • +
    • GZip-Kompression: Hier kann die Kompression aktiviert werden. Empfehlenswert ist die Aktivierung, da die Dateien doch wesentlich kleiner werden und Speicherplatz immer rar ist.
    • +
    • Email mit Dumpfile: Ist diese Option aktiviert, so wird nach abgeschlossenem Backup eine Email mit dem Dump als Anhang verschickt (Vorsicht, Kompression sollte unbedingt an sein, sonst wird der Anhang zu gross und kann evtl. nicht versandt werden!).
    • +
    • Email-Adresse: Empfängeradresse für die Email.
    • +
    • Absender der Email: diese Adresse taucht als Absender in der Email auf.
    • +
    • FTP-Transfer: Ist diese Option aktiviert, so wird nach abgeschlossenem Backup die Backupdatei per FTP versandt.
    • +
    • FTP Server: Die Adresse des FTP-Servers (z. B. ftp.mybackups.de).
    • +
    • FTP Server Port: Der Port des FTP-Servers (in der Regel 21).
    • +
    • FTP User: Der Benutzername des FTP-Accounts.
    • +
    • FTP Passwort: Das Passwort des FTP-Accounts.
    • +
    • FTP Upload-Ordner: Das Verzeichnis, in das die Backupdatei soll (es müssen Upload-Berechtigungen bestehen!).
    • +
    • Automatisches Löschen der Backups: Wenn diese Option aktiviert ist, werden ältere Backups nach den folgenden Regeln automatisch gelöscht.
    • +
    • Anzahl von Backupdateien: Ein Wert > 0 löscht alle Backupdateien, bis auf die hier angegebe Zahl.
    • +
    • Sprache: hier legst du die Sprache für das Interface fest.
    • +
    + +
    Verwaltung
    +Hier werden die eigenlichen Aktionen durchgeführt.
    +Es werden Dir alle Dateien im Backup-Verzeichnis angezeigt. +Für die Aktionen "Restore" und "Delete" muss eine Datei selektiert sein. +
      +
    • Restore: Hiermit wird die Datenbank mit der ausgewählten Backupdatei aktualisiert.
    • +
    • Delete: Hiermit kannst Du die selektierte Backupdatei löschen.
    • +
    • Neues Backup starten: Hier startest Du ein neues Backup (Dump) nach den in der Konfiguration eingestellten Parametern.
    • +
    + +
    Log
    +Hier kannst Du die Logeinträge sehen und löschen. +
    Credits / Hilfe
    +diese Seite. +
\ No newline at end of file diff --git a/msd/language/de_du/lang.php b/msd/language/de_du/lang.php new file mode 100644 index 0000000..4d1accc --- /dev/null +++ b/msd/language/de_du/lang.php @@ -0,0 +1,109 @@ +nicht verfügbar'; +$lang['L_VOM'] = 'vom'; +$lang['L_MYSQLVARS'] = 'MySQL-Variablen'; +$lang['L_MYSQLSYS'] = 'MySQL-Befehle'; +$lang['L_STATUS'] = 'Status'; +$lang['L_PROZESSE'] = 'Prozesse'; +$lang['L_INFO_NOVARS'] = 'keine Variablen verfügbar'; +$lang['L_INHALT'] = 'Inhalt'; +$lang['L_INFO_NOSTATUS'] = 'kein Status verfügbar'; +$lang['L_INFO_NOPROCESSES'] = 'keine laufenden Prozesse'; +$lang['L_FM_FREESPACE'] = 'Freier Speicher auf Server'; +$lang['L_LOAD_DATABASE'] = 'Datenbanken neu laden'; +$lang['L_HOME'] = 'Startseite'; +$lang['L_CONFIG'] = 'Konfiguration'; +$lang['L_DUMP'] = 'Sicherung'; +$lang['L_RESTORE'] = 'Wiederherstellung'; +$lang['L_FILE_MANAGE'] = 'Verwaltung'; +$lang['L_LOG'] = 'Log'; +$lang['L_CHOOSE_DB'] = 'Datenbank wählen'; +$lang['L_CREDITS'] = 'Credits / Hilfe'; +$lang['L_MULTI_PART'] = 'Multipart-Backup'; +$lang['L_LOGFILENOTWRITABLE'] = 'Log-File kann nicht geschrieben werden!'; +$lang['L_SQL_ERROR1'] = 'Fehler bei der Anfrage:'; +$lang['L_SQL_ERROR2'] = 'MySQL meldet:'; +$lang['L_UNKNOWN'] = 'unbekannt'; +$lang['L_UNKNOWN_NUMBER_OF_RECORDS'] = 'unbekannt'; +$lang['L_OK'] = 'OK'; +$lang['L_CRON_COMPLETELOG'] = 'Komplette Ausgabe loggen'; +$lang['L_NO'] = 'nein'; +$lang['L_CREATE_DATABASE'] = 'Neue Datenbank anlegen'; +$lang['L_EXPORTFINISHED'] = 'Export beendet.'; +$lang['L_SQL_BROWSER'] = 'SQL-Browser'; +$lang['L_SERVER'] = 'Server'; +$lang['L_MYSQL_CONNECTION_ENCODING'] = 'Standardkodierung des MySQL-Servers'; +$lang['L_TITLE_SHOW_DATA'] = 'Daten anzeigen'; +$lang['L_PRIMARYKEY_CONFIRMDELETE'] = 'Primärschlüssel wirklich löschen?'; +$lang['L_SETPRIMARYKEYSFOR'] = 'Setzen neuer Primärschlüssel für die Tabelle'; +$lang['L_PRIMARYKEY_FIELD'] = 'Schlüsselfeld'; +$lang['L_PRIMARYKEYS_SAVE'] = 'Primärschlüssel speichern'; +$lang['L_CANCEL'] = 'Abbruch'; +$lang['L_VISIT_HOMEPAGE'] = 'Besuche die Homepage'; +$lang['L_SECONDS'] = 'Sekunden'; +$lang['L_BACKUPS'] = 'Sicherungsdateien'; +$lang['L_MINUTES'] = 'Minuten'; +$lang['L_PAGE_REFRESHS'] = 'Seitenaufrufe'; +$lang['L_MINUTE'] = 'Minute'; +$lang['L_SETKEYSFOR'] = 'Setzen neuer Indizes für die Tabelle'; +$lang['L_KEY_CONFIRMDELETE'] = 'Index wirklich löschen?'; diff --git a/msd/language/de_du/lang_config_overview.php b/msd/language/de_du/lang_config_overview.php new file mode 100644 index 0000000..5562227 --- /dev/null +++ b/msd/language/de_du/lang_config_overview.php @@ -0,0 +1,129 @@ +%s
in %s'; +$lang['L_FTP'] = 'FTP'; +$lang['L_SFTP_SEND_TO'] = 'an %s
in %s'; +$lang['L_SFTP'] = 'SFTP'; +$lang['L_EMAIL_CC'] = 'CC-Empfänger'; +$lang['L_NAME'] = 'Name'; +$lang['L_CONFIRM_CONFIGFILE_DELETE'] = 'Soll die Konfigurationsdatei %s wirklich gelöscht werden?'; +$lang['L_ERROR_DELETING_CONFIGFILE'] = 'Fehler: die Konfigurationsdatei %s konnte nicht gelöscht werden!'; +$lang['L_SUCCESS_DELETING_CONFIGFILE'] = 'Die Konfigurationsdatei %s wurde erfolgreich gelöscht.'; +$lang['L_SUCCESS_CONFIGFILE_CREATED'] = 'Die Konfigurationsdatei %s wurde erfolgreich angelegt.'; +$lang['L_ERROR_CONFIGFILE_NAME'] = 'Der Dateiname "%s" enthält ungültige Zeichen.'; +$lang['L_CREATE_CONFIGFILE'] = 'Eine neue Konfigurationsdatei anlegen'; +$lang['L_ERROR_LOADING_CONFIGFILE'] = 'Die Konfigurationsdatei "%s" konnte nicht geladen werden.'; +$lang['L_BACKUP_DBS_PHP'] = 'zu sichernde DBs (PHP)'; +$lang['L_BACKUP_DBS_PERL'] = 'zu sichernde DBs (PERL)'; +$lang['L_CRON_COMMENT'] = 'Kommentar eingeben'; +$lang['L_AUTODETECT'] = 'automatisch ermitteln'; diff --git a/msd/language/de_du/lang_dump.php b/msd/language/de_du/lang_dump.php new file mode 100644 index 0000000..8f91a47 --- /dev/null +++ b/msd/language/de_du/lang_dump.php @@ -0,0 +1,57 @@ +%s` gefunden werden.'; +$lang['L_DUMP_ENDERGEBNIS'] = 'Es wurden %s Tabellen mit insgesamt %s Datensätzen gesichert.
'; +$lang['L_MAILERROR'] = 'Leider ist beim Verschicken der E-Mail ein Fehler aufgetreten!'; +$lang['L_EMAILBODY_ATTACH'] = 'In der Anlage findest du die Sicherung deiner MySQL-Datenbank.
Sicherung der Datenbank `%s` +

Folgende Datei wurde erzeugt:

%s

Viele Grüße

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_MP_NOATTACH'] = 'Es wurde eine Multipart-Sicherung erstellt.
Die Sicherungen werden nicht als Anhang mitgeliefert!
Sicherung der Datenbank `%s` +

Folgende Dateien wurden erzeugt:

%s


Viele Grüße

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_MP_ATTACH'] = 'Es wurde eine Multipart-Sicherung erstellt.
Die Sicherungen werden in separaten E-Mails als Anhang geliefert!
Sicherung der Datenbank `%s` +

Folgende Dateien wurden erzeugt:

%s


Viele Grüße

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_FOOTER'] = '


Viele Grüße

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_TOOBIG'] = 'Die Sicherung überschreitet die Maximalgröße von %s und wurde daher nicht angehängt.
Sicherung der Datenbank `%s` +

Folgende Datei wurde erzeugt:

%s +

Viele Grüße

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_NOATTACH'] = 'Das Backup wurde nicht angehängt.
Sicherung der Datenbank `%s` +

Folgende Datei wurde erzeugt:

%s +

Viele Grüße

MyOOS [Dumper]
'; +$lang['L_EMAIL_ONLY_ATTACHMENT'] = ' ... nur der Anhang'; +$lang['L_TABLESELECTION'] = 'Tabellenauswahl'; +$lang['L_SELECTALL'] = 'alle auswählen'; +$lang['L_DESELECTALL'] = 'Auswahl aufheben'; +$lang['L_STARTDUMP'] = 'Backup starten'; +$lang['L_LASTBUFROM'] = 'letztes Update vom'; +$lang['L_NOT_SUPPORTED'] = 'Dieses Backup unterstützt diese Funktion nicht.'; +$lang['L_MULTIDUMP'] = 'Multidump: Es wurden %d Datenbanken gesichert.'; +$lang['L_FILESENDFTP'] = 'versende File via FTP... bitte habe etwas Geduld. '; +$lang['L_FTPCONNERROR'] = 'FTP-Verbindung nicht hergestellt! Verbindung mit '; +$lang['L_FTPCONNERROR1'] = ' als Benutzer '; +$lang['L_FTPCONNERROR2'] = ' nicht möglich'; +$lang['L_FTPCONNERROR3'] = 'FTP-Upload war fehlerhaft! '; +$lang['L_FTPCONNECTED1'] = 'Verbunden mit '; +$lang['L_FTPCONNECTED2'] = ' auf '; +$lang['L_FTPCONNECTED3'] = ' geschrieben'; +$lang['L_FILESENDSFTP'] = 'versende File via SFTP... bitte habe etwas Geduld. '; +$lang['L_SFTPCONNERROR'] = 'SFTP-Verbindung nicht hergestellt! Verbindung mit '; +$lang['L_NR_TABLES_SELECTED'] = '- mit %s gewählten Tabellen'; +$lang['L_NR_TABLES_OPTIMIZED'] = '%s Tabellen wurden optimiert.'; +$lang['L_DUMP_ERRORS'] = '

%s Fehler aufgetreten: anzeigen

'; +$lang['L_FATAL_ERROR_DUMP'] = "Schwerwiegender Fehler: die CREATE-Anweisung der Tabelle '%s' in der Datenbank '%s' konnte nicht gelesen werden!"; diff --git a/msd/language/de_du/lang_filemanagement.php b/msd/language/de_du/lang_filemanagement.php new file mode 100644 index 0000000..6f3a972 --- /dev/null +++ b/msd/language/de_du/lang_filemanagement.php @@ -0,0 +1,80 @@ +%s"'; +$lang['L_DELETE_FILE_ERROR'] = 'Die Datei "%s" konnte nicht gelöscht werden!'; +$lang['L_FM_DUMP_HEADER'] = 'Sicherung'; +$lang['L_DOCRONBUTTON'] = 'Perl-Cronscript ausführen'; +$lang['L_DOPERLTEST'] = 'Perl-Module testen'; +$lang['L_DOSIMPLETEST'] = 'Perl testen'; +$lang['L_PERLOUTPUT1'] = 'Eintrag in crondump.pl für absolute_path_of_configdir'; +$lang['L_PERLOUTPUT2'] = 'Aufruf im Browser oder für externen Cronjob'; +$lang['L_PERLOUTPUT3'] = 'Aufruf in der Shell oder für die Crontab'; +$lang['L_RESTORE_OF_TABLES'] = 'Wiederherstellen bestimmter Tabellen'; +$lang['L_CONVERTER'] = 'Backup-Konverter'; +$lang['L_CONVERT_FILE'] = 'zu konvertierende Datei'; +$lang['L_CONVERT_FILENAME'] = 'Name der Zieldatei (ohne Endung)'; +$lang['L_CONVERTING'] = 'Konvertierung'; +$lang['L_CONVERT_FILEREAD'] = "Datei '%s' wird eingelesen"; +$lang['L_CONVERT_FINISHED'] = "Konvertierung abgeschlossen, '%s' wurde erzeugt."; +$lang['L_NO_MOD_BACKUPFILE'] = 'Dateien anderer Programme'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Maximale Dateigröße'; +$lang['L_MAX_UPLOAD_SIZE_INFO'] = 'Wenn Deine Backup-Datei größer als das angegebene Limit ist, dann musst Du diese per FTP in den "work/backup"-Ordner hochladen. +Danach wird diese Datei hier in der Verwaltung angezeigt und lässt sich für eine Wiederherstellung auswählen.'; +$lang['L_ENCODING'] = 'Kodierung'; +$lang['L_FM_CHOOSE_ENCODING'] = 'Kodierung der Backupdatei wählen'; +$lang['L_CHOOSE_CHARSET'] = 'Leider konnte nicht automatisch ermittelt werden mit welchem Zeichensatz diese Backupdatei seinerzeit angelegt wurde. +
Du musst die Kodierung, in der Zeichenketten in dieser Datei vorliegen, manuell angeben. +
Danach stellt MyOOS [Dumper] die Verbindungskennung zum MySQL-Server auf den ausgewählten Zeichensatz und beginnt mit der Wiederherstellung der Daten. +
Solltest Du nach der Wiederherstellung Probleme mit Sonderzeichen entdecken, so kannst Du versuchen, das Backup mit einer anderen Zeichensatzauswahl wiederherzustellen. +
Viel Glück. ;)'; +$lang['L_DOWNLOAD_FILE'] = 'Datei herunterladen'; +$lang['L_BACKUP_NOT_POSSIBLE'] = 'Eine Sicherung der Systemdatenbank `%s` ist nicht möglich!'; diff --git a/msd/language/de_du/lang_help.php b/msd/language/de_du/lang_help.php new file mode 100644 index 0000000..42ea112 --- /dev/null +++ b/msd/language/de_du/lang_help.php @@ -0,0 +1,41 @@ +die Installation ist abgeschlossen --> starte MyOOS [Dumper]
'; +$lang['L_INSTALL_TOMENU'] = 'zum Hauptmenü'; +$lang['L_INSTALLMENU'] = 'Hauptmenü'; +$lang['L_STEP'] = 'Schritt'; +$lang['L_INSTALL'] = 'Installation'; +$lang['L_UNINSTALL'] = 'Deinstallation'; +$lang['L_TOOLS'] = 'Tools'; +$lang['L_EDITCONF'] = 'Konfiguration bearbeiten'; +$lang['L_OSWEITER'] = 'ohne Speichern weiter'; +$lang['L_ERRORMAN'] = 'Fehler beim Schreiben der Konfiguration!
Bitte editiere die Datei '; +$lang['L_MANUELL'] = 'manuell'; +$lang['L_CREATEDIRS'] = 'erstelle Verzeichnisse'; +$lang['L_INSTALL_CONTINUE'] = 'mit der Installation fortfahren'; +$lang['L_CONNECTTOMYSQL'] = ' zu MySQL verbinden '; +$lang['L_DBPARAMETER'] = 'Datenbank-Parameter'; +$lang['L_CONFIGNOTWRITABLE'] = 'Die Datei "config.php" ist nicht beschreibbar. +Gib ihr mit einem FTP-Programm entsprechende Rechte, z. B. den CHMod-Wert 0777.'; +$lang['L_DBCONNECTION'] = 'Datenbank-Verbindung'; +$lang['L_CONNECTIONERROR'] = 'Fehler: Es konnte keine Verbindung herstellt werden.'; +$lang['L_CONNECTION_OK'] = 'Datenbank-Verbindung wurde hergestellt.'; +$lang['L_SAVEANDCONTINUE'] = 'speichern und Installation fortsetzen'; +$lang['L_CONFBASIC'] = 'Grundeinstellungen'; +$lang['L_INSTALL_STEP2FINISHED'] = 'Die Einstellungen wurden erfolgreich gesichert.'; +$lang['L_INSTALL_STEP2_1'] = 'Installation mit Standardkonfiguration fortsetzen'; +$lang['L_LASTSTEP'] = 'Abschluss der Installation'; +$lang['L_IDOMANUAL'] = 'Ich erstelle die Verzeichnisse manuell'; +$lang['L_DOFROM'] = 'ausgehend von'; +$lang['L_FTPMODE2'] = 'Erstelle die Verzeichnisse per FTP:'; +$lang['L_CONNECT'] = 'verbinden'; +$lang['L_DIRS_CREATED'] = 'Die Verzeichnisse wurden ordnungsgemäß erstellt.'; +$lang['L_CONNECT_TO'] = 'verbinde zu'; +$lang['L_CHANGEDIR'] = 'Wechsel ins Verzeichnis'; +$lang['L_CHANGEDIRERROR'] = 'Wechsel ins Verzeichnis nicht möglich'; +$lang['L_FTP_OK'] = 'FTP-Parameter sind ok'; +$lang['L_SFTP_OK'] = 'FTP-Parameter sind ok'; +$lang['L_CREATEDIRS2'] = 'Verzeichnisse erstellen'; +$lang['L_FTP_NOTCONNECTED'] = 'FTP-Verbindung nicht hergestellt!'; +$lang['L_CONNWITH'] = 'Verbindung mit'; +$lang['L_ASUSER'] = 'als Benutzer'; +$lang['L_NOTPOSSIBLE'] = 'nicht möglich'; +$lang['L_DIRCR1'] = 'erstelle Arbeitsverzeichnis'; +$lang['L_DIRCR2'] = 'erstelle Backup-Verzeichnis'; +$lang['L_DIRCR4'] = 'erstelle Log-Verzeichnis'; +$lang['L_DIRCR5'] = 'erstelle Konfigurationsverzeichnis'; +$lang['L_INDIR'] = 'bin im Verzeichnis'; +$lang['L_CHECK_DIRS'] = 'Verzeichnisse überprüfen'; +$lang['L_DISABLEDFUNCTIONS'] = 'Abgeschaltete Funktionen'; +$lang['L_NOFTPPOSSIBLE'] = 'Es stehen keine FTP-Funktionen zur Verfügung!'; +$lang['L_NOGZPOSSIBLE'] = 'Es stehen keine Kompressions-Funktionen zur Verfügung!'; +$lang['L_UI1'] = 'Es werden alle Arbeitsverzeichnisse incl. den darin enthaltenen Backups gelöscht.'; +$lang['L_UI2'] = 'Bist du sicher, dass du das möchtest?'; +$lang['L_UI3'] = 'Nein, sofort abbrechen'; +$lang['L_UI4'] = 'ja, bitte fortfahren'; +$lang['L_UI5'] = 'lösche Arbeitsverzeichnis'; +$lang['L_UI6'] = 'alles wurde erfolgreich gelöscht.'; +$lang['L_UI7'] = 'Bitte lösche das Skriptverzeichnis'; +$lang['L_UI8'] = 'eine Ebene nach oben'; +$lang['L_UI9'] = 'Ein Fehler trat auf, löschen war nicht möglich

Fehler bei Verzeichnis '; +$lang['L_IMPORT'] = 'Konfiguration importieren'; +$lang['L_IMPORT3'] = 'Die Konfiguration wurde geladen...'; +$lang['L_IMPORT4'] = 'Die Konfiguration wurde gesichert.'; +$lang['L_IMPORT5'] = 'MyOOS [Dumper] starten'; +$lang['L_IMPORT6'] = 'Installations-Menü'; +$lang['L_IMPORT7'] = 'Konfiguration hochladen'; +$lang['L_IMPORT8'] = 'zurück zum Upload'; +$lang['L_IMPORT9'] = 'Dies ist keine Konfigurationssicherung!'; +$lang['L_IMPORT10'] = 'Die Konfiguration wurde erfolgreich hochgeladen...'; +$lang['L_IMPORT11'] = 'Fehler: Es gab Probleme beim Schreiben der sql_statements.'; +$lang['L_IMPORT12'] = 'Fehler: Es gab Probleme beim Schreiben der config.php.'; +$lang['L_INSTALL_HELP_PORT'] = '(leer = Standardport)'; +$lang['L_INSTALL_HELP_SOCKET'] = '(leer = Standardsocket)'; +$lang['L_TRYAGAIN'] = 'noch einmal versuchen'; +$lang['L_SOCKET'] = 'Socket'; +$lang['L_PORT'] = 'Port'; +$lang['L_FOUND_DB'] = 'gefundene DB: '; +$lang['L_FM_FILEUPLOAD'] = 'Datei hochladen'; +$lang['L_PASS'] = 'Passwort'; +$lang['L_NO_DB_FOUND_INFO'] = 'Die Verbindung zur Datenbank konnte erfolgreich hergestellt werden.
+Deine Zugangsdaten sind gültig und wurden vom MySQL-Server akzeptiert.
+Leider konnte MyOOS [Dumper] keine Datenbank finden.
+Die automatische Erkennung per Programm ist bei manchen Hostern gesperrt.
+Du musst Deine Datenbank nach dem Abschluß der Installation unter dem Menüpunkt "Konfiguration" "Verbindungsparameter einblenden" angeben.
+Bitte begebe Dich nach Abschluß der Installation umgehend dort hin und trage den Namen Deiner Datenbank dort ein.'; +$lang['L_ENTER_DB_INFO'] = 'Klicke zuerst auf den Button "zu MySQL verbinden". Nur wenn daraufhin keine Datenbank erkannt werden konnte, ist hier eine Angabe notwendig.'; diff --git a/msd/language/de_du/lang_log.php b/msd/language/de_du/lang_log.php new file mode 100644 index 0000000..645f898 --- /dev/null +++ b/msd/language/de_du/lang_log.php @@ -0,0 +1,7 @@ +Bitte erzeuge die Dateien manuell mit folgendem Inhalt'; +$lang['L_HTACC_CHECK_ERROR'] = 'Es konnte nicht überprüft werden, ob das Programm geschützt ist!
Der simulierte externe Zugriff konnte nicht ausgeführt werden.'; +$lang['L_HTACC_NOT_NEEDED'] = 'Das Programm ist durch übergeordnete Berechtigungen geschützt; ein lokaler Verzeichnisschutz ist nicht erforderlich.'; +$lang['L_HTACC_COMPLETE'] = 'Das Programm ist geschützt, der Verzeichnisschutz ist vollständig.'; +$lang['L_HTACC_INCOMPLETE'] = 'Das Programm ist nicht geschützt, der Verzeichnisschutz ist unvollständig!'; +$lang['L_HTACC_PROPOSED'] = 'Das Programm ist nicht geschützt, ein Verzeichnisschutz wird dringend empfohlen!'; +$lang['L_HTACC_EDIT'] = '.htaccess editieren'; +$lang['L_HTACCESS18'] = '.htaccess erstellen in '; +$lang['L_HTACCESS19'] = 'Neu laden '; +$lang['L_HTACCESS20'] = 'Skript ausführen'; +$lang['L_HTACCESS21'] = 'Handler hinzufügen'; +$lang['L_HTACCESS22'] = 'Ausführbar machen'; +$lang['L_HTACCESS23'] = 'Verzeichnisinhalt (Directory Listing)'; +$lang['L_HTACCESS24'] = 'Error-Dokument'; +$lang['L_HTACCESS25'] = 'Rewrite aktivieren'; +$lang['L_HTACCESS26'] = 'Deny / Allow'; +$lang['L_HTACCESS27'] = 'Redirect'; +$lang['L_HTACCESS28'] = 'Error-Log'; +$lang['L_HTACCESS29'] = 'weitere Beispiele und Dokumentation'; +$lang['L_HTACCESS30'] = 'Provider'; +$lang['L_HTACCESS31'] = 'Allgemein'; +$lang['L_HTACCESS32'] = 'Achtung! Die .htaccess hat eine direkte Auswirkung auf den Browser.
Bei falscher Anwendung sind die Seiten nicht mehr erreichbar.'; +$lang['L_DISABLEDFUNCTIONS'] = 'Abgeschaltete Funktionen'; +$lang['L_NOGZPOSSIBLE'] = 'Da zlib nicht installiert ist, stehen keine GZip-Funktionen zur Verfügung!'; +$lang['L_DELETE_HTACCESS'] = 'Verzeichnisschutz entfernen (.htaccess löschen)'; +$lang['L_WRONG_RIGHTS'] = "Die Datei oder das Verzeichnis '%s' ist für mich nicht beschreibbar.
+Entweder hat sie/es den falschen Besitzer (Owner) oder die falschen Rechte (Chmod).
+Bitte setze die richtigen Attribute mit Deinem FTP-Programm.
+Die Datei oder das Verzeichnis benötigt die Rechte %s.
"; +$lang['L_CANT_CREATE_DIR'] = "Ich konnte das Verzeichnis '%s' nicht erstellen. +Bitte erstelle es mit Deinem FTP-Programm."; +$lang['L_CANT_CREATE_DIR'] = "Ich konnte das '%s'-Verzeichnis nicht erstellen. +Bitte mit Deinem FTP-Programm erstellen."; +$lang['L_TABLE_TYPE'] = 'Typ'; +$lang['L_CHECK'] = 'prüfen'; +$lang['L_OS'] = 'Betriebssystem'; +$lang['L_MOD_VERSION'] = 'MyOOS [Dumper] - Version'; +$lang['L_NEW_MOD_VERSION'] = 'Neue Version'; +$lang['L_NEW_MOD_VERSION_INFO'] = 'Es ist eine neue Version von MyOOS [Dumper] verfügbar.'; +$lang['L_UPDATED_IMPORTANT'] = 'Wichtig: Sichere vor der Aktualisierung bitte Deine Dateien.'; +$lang['L_UPDATE'] = 'Jetzt aktualisieren'; +$lang['L_MYSQL_VERSION'] = 'MySQL-Version'; +$lang['L_PHP_VERSION'] = 'PHP-Version'; +$lang['L_MAX_EXECUTION_TIME'] = 'Maximale Ausführungszeit'; +$lang['L_PHP_EXTENSIONS'] = 'PHP-Erweiterungen'; +$lang['L_MEMORY'] = 'Speicher'; +$lang['L_INSTALLING_UPDATES'] = 'Installation von Updates'; +$lang['L_UPDATE_SUCCESSFUL'] = 'Aktualisierung erfolgreich'; +$lang['L_UPDATE_FAILED'] = 'Aktualisierung fehlgeschlagen'; +$lang['L_UP_TO_DATE'] = 'Aktuelle Version ist auf dem neuesten Stand'; diff --git a/msd/language/de_du/lang_restore.php b/msd/language/de_du/lang_restore.php new file mode 100644 index 0000000..2d4b5d1 --- /dev/null +++ b/msd/language/de_du/lang_restore.php @@ -0,0 +1,19 @@ +%d Tabellen angelegt.'; +$lang['L_FILE_MISSING'] = 'konnte Datei nicht finden'; +$lang['L_RESTORE_DB'] = "Datenbank '%s' auf Server '%s'."; +$lang['L_RESTORE_COMPLETE'] = '%s Tabellen wurden angelegt.'; +$lang['L_RESTORE_RUN1'] = '
Es wurden bisher %s von %s Datensätzen erfolgreich eingetragen.'; +$lang['L_RESTORE_RUN2'] = "
Momentan werden Daten der Tabelle '%s' analysiert.

"; +$lang['L_RESTORE_COMPLETE2'] = '%s Datensätze wurden eingetragen.'; +$lang['L_RESTORE_TABLES_COMPLETED'] = 'Es wurden bisher %d von %d Tabellen angelegt.'; +$lang['L_RESTORE_TOTAL_COMPLETE'] = '
Herzlichen Glückwunsch.

Die Datenbank wurde komplett wiederhergestellt.
Alle Daten aus der Backup-Datei wurden erfolgreich in die Datenbank eingetragen.

Alles fertig. :-)'; +$lang['L_DB_SELECT_ERROR'] = "
Fehler:
Auswahl der Datenbank '"; +$lang['L_DB_SELECT_ERROR2'] = "' fehlgeschlagen!"; +$lang['L_FILE_OPEN_ERROR'] = 'Fehler: Die Datei konnte nicht geöffnet werden.'; +$lang['L_PROGRESS_OVER_ALL'] = 'Fortschritt gesamt'; +$lang['L_BACK_TO_OVERVIEW'] = 'Datenbank-Übersicht'; +$lang['L_RESTORE_RUN0'] = '
Es wurden bisher %s Datensätze erfolgreich eingetragen.'; +$lang['L_UNKNOWN_SQLCOMMAND'] = 'Unbekannter SQL-Befehl:'; +$lang['L_NOTICES'] = 'Hinweise'; diff --git a/msd/language/de_du/lang_sql.php b/msd/language/de_du/lang_sql.php new file mode 100644 index 0000000..bd22064 --- /dev/null +++ b/msd/language/de_du/lang_sql.php @@ -0,0 +1,190 @@ +%s Zeilen exportiert'; +$lang['L_CSV_FIELDCOUNT_NOMATCH'] = 'Die Anzahl der Tabellenfelder stimmen nicht mit den zu importierenden Daten überein (%d statt %d).'; +$lang['L_CSV_FIELDSLINES'] = '%d Felder ermittelt, insgesamt %d Zeilen'; +$lang['L_CSV_ERRORCREATETABLE'] = 'Fehler beim Erstellen der Tabelle `%s`!'; +$lang['L_FM_UPLOADFILEREQUEST'] = 'Bitte gib eine Datei an.'; +$lang['L_CSV_NODATA'] = 'Keine Daten zum Importieren gefunden!'; +$lang['L_SQLLIB_GENERALFUNCTIONS'] = 'allgemeine Funktionen'; +$lang['L_SQLLIB_RESETAUTO'] = 'Auto-Wert zurücksetzen'; +$lang['L_SQLLIB_BOARDS'] = 'Boards'; +$lang['L_SQLLIB_DEACTIVATEBOARD'] = 'Board deaktivieren'; +$lang['L_SQLLIB_ACTIVATEBOARD'] = 'Board aktivieren'; +$lang['L_SQL_NOTABLESSELECTED'] = 'Es sind keine Tabellen ausgewählt!'; +$lang['L_TOOLS'] = 'Tools'; +$lang['L_TOOLS_TOOLBOX'] = 'Datenbank auswählen / Datenbankfunktionen / Im- und Export '; +$lang['L_SQL_OPENFILE'] = 'SQL-Datei öffnen'; +$lang['L_SQL_OPENFILE_BUTTON'] = 'Hochaden'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Maximale Dateigröße'; +$lang['L_SQL_SEARCH'] = 'Suche'; +$lang['L_SQL_SEARCHWORDS'] = 'Suchbegriff(e)'; +$lang['L_START_SQL_SEARCH'] = 'Suche starten'; +$lang['L_RESET_SEARCHWORDS'] = 'Eingabe zurücksetzen'; +$lang['L_SEARCH_OPTIONS'] = 'Suchoptionen'; +$lang['L_SEARCH_RESULTS'] = 'Die Suche nach "%s" in der Tabelle "%s" lieferte folgende Treffer'; +$lang['L_SEARCH_NO_RESULTS'] = 'Die Suche nach "%s" in der Tabelle "%s" liefert keine Ergebnisse!'; +$lang['L_NO_ENTRIES'] = 'Die Tabelle "%s" ist leer und hat keine Einträge.'; +$lang['L_SEARCH_ACCESS_KEYS'] = 'Blättern: vor=ALT+V, zurück=ALT+C'; +$lang['L_SEARCH_OPTIONS_OR'] = 'eine Spalte muss mindestens einen Suchbegriff enthalten (ODER-Suche)'; +$lang['L_SEARCH_OPTIONS_CONCAT'] = 'ein Datensatz muss alle Suchbegriffe enthalten, diese können aber in beliebigen Spalten sein (Rechenintensiv!)'; +$lang['L_SEARCH_OPTIONS_AND'] = 'eine Spalte muss alle Suchbegriffe enthalten (UND-Suche)'; +$lang['L_SEARCH_IN_TABLE'] = 'Suche in Tabelle'; +$lang['L_ERROR_NO_FIELDS'] = 'Fehler bei Suche: es konnte nicht ermittelt werden, welche Felder die Tabelle "%s" hat!'; +$lang['L_SQL_EDIT_TABLESTRUCTURE'] = 'Tabellenstruktur bearbeiten'; +$lang['L_DEFAULT_CHARSET'] = 'Standardzeichensatz'; +$lang['L_TITLE_KEY_PRIMARY'] = 'Primärschlüssel'; +$lang['L_TITLE_KEY_UNIQUE'] = 'Eindeutiger Schlüssel'; +$lang['L_TITLE_INDEX'] = 'Index'; +$lang['L_TITLE_KEY_FULLTEXT'] = 'Volltextschlüssel'; +$lang['L_TITLE_NOKEY'] = 'Kein Schlüssel'; +$lang['L_TITLE_SEARCH'] = 'Suche'; +$lang['L_TITLE_MYSQL_HELP'] = 'MySQL Dokumentation'; +$lang['L_TITLE_UPLOAD'] = 'SQL-Datei hochladen'; +$lang['L_PRIMARYKEY_DELETED'] = 'Primärschlüssel gelöscht'; +$lang['L_PRIMARYKEY_NOTFOUND'] = 'Primärschlüssel nicht gefunden'; +$lang['L_PRIMARYKEYS_CHANGED'] = 'Primärschlüssel geändert'; +$lang['L_PRIMARYKEYS_CHANGINGERROR'] = 'Fehler beim Ändern der Primärschlüssel'; +$lang['L_SQL_VIEW_COMPACT'] = 'Ansicht: kompakt'; +$lang['L_SQL_VIEW_STANDARD'] = 'Ansicht: normal'; +$lang['L_FIELDS_OF_TABLE'] = 'Felder der Tabelle'; +$lang['L_ENGINE'] = 'Typ'; +$lang['L_USERNAME'] = 'Benutzername'; +$lang['L_PASSWORD'] = 'Kennwort'; +$lang['L_PASSWORD_REPEAT'] = 'Kennwort (Wiederholung)'; +$lang['L_INFO_SIZE'] = 'Größe'; +$lang['L_TABLE_TYPE'] = 'Typ'; +$lang['L_KEY_DELETED'] = 'Index gelöscht'; +$lang['L_KEY_DELETEERROR'] = 'Fehler beim Löschen des Index'; +$lang['L_KEY_ADDED'] = 'Index angelegt'; +$lang['L_KEY_ADDERROR'] = 'Fehler beim Anlegen des Index'; diff --git a/msd/language/el/help.html b/msd/language/el/help.html new file mode 100644 index 0000000..ea92df5 --- /dev/null +++ b/msd/language/el/help.html @@ -0,0 +1,149 @@ +
+

MyOOS [Dumper] βασισμένο στο MySQLDumper 1.24.4

+ +

Περί αυτού του έργου

+

Το MyOOS [Dumper] είναι μια βελτιωμένη έκδοση του MySQLDumper 1.24.4 (24 Ιανουαρίου 2011). Αυτή η περαιτέρω ανάπτυξη λαμβάνει υπόψη την ανάπτυξη της PHP. +

Το MyOOS [Dumper] ασχολείται κυρίως με τη σταθερότητα, την ασφάλεια και το χειρισμό. Περιλαμβάνεται όμως και ένα ελκυστικό πρότυπο, το οποίο μπορείτε να επεξεργαστείτε και να προσαρμόσετε στις δικές σας ανάγκες.

+ + +

Το MyOOS [Dumper] είναι ένα πρόγραμμα δημιουργίας αντιγράφων ασφαλείας για βάσεις δεδομένων MySQL, γραμμένο σε PHP και Perl. Με αυτό, μπορούν να δημιουργηθούν αντίγραφα ασφαλείας των δεδομένων (κατάστημα, ιστολόγιο κ.λπ.) και να επαναφερθούν, εάν χρειαστεί. Ειδικά για χώρους στο διαδίκτυο που δεν έχουν πρόσβαση στο κέλυφος, το MyOOS [Dumper] προσφέρεται ως μια λογική εναλλακτική λύση.

+ +

Η ιδέα για το MySQLDumper προήλθε από τον Daniel Schlichtholz. Άνοιξε το φόρουμ MySQLDumper το 2004, όπου οι προγραμματιστές έγραψαν νέα σενάρια και επέκτειναν τα υπάρχοντα.

+ + + +

Λίστα επιθυμιών / Μελλοντικά αξιοθέατα

. +

Έχετε προτάσεις για βελτιώσεις; Μη διστάσετε να επικοινωνήσετε με την ομάδα ανάπτυξης μέσω του φόρουμ https://foren.myoos.de/viewforum.php?f=41.

+ + +

Συνεισφορά

+

Αν θέλετε να μας βοηθήσετε να βελτιώσουμε το έργο MyOOS, καλωσορίζουμε τα pull requests σας μέσω του GitHub εδώ.

+https://github.com/r23/MyOOS-Dumper/ + + +

Οικονομική στήριξη

+

Μπορείτε να χρησιμοποιήσετε το PayPal Me
. +https://www.paypal.com/paypalme/r23de?locale.x=de_DE

+ +

ή μέσω του κωδικού QR
. +Οικονομική υποστήριξη για το MyOOS [Dumper]

+ +Στείλτε χρήματα στο έργο MyOOS.
+ +

Ελπίζουμε να σας αρέσει αυτό το έργο.

Η ομάδα MyOOS [Dumper]

+ +MyOOS [Dumper]
+ + +

Βοήθεια για το MyOOS [Dumper]

+ +

Λήψη

+

Μπορείτε πάντα να λαμβάνετε τις τελευταίες εκδόσεις από το GitHub
. +https://github.com/r23/MyOOS-Dumper/releases

+ + +

Απαιτήσεις συστήματος

+

Το σενάριο λειτουργεί σε οποιονδήποτε διακομιστή (Windows, Linux, ...)
+με PHP >= έκδοση 7.4 με υποστήριξη GZip, MySQL (έκδοση 4.1 ή νεότερη), JavaScript (πρέπει να είναι ενεργοποιημένη).

+

Αντιγράψτε το φάκελο mod από το αρχείο MyOOS σε έναν ξεχωριστό φάκελο εργασίας.

+ +

Εγκατάσταση

+Η εγκατάσταση είναι απλή. +

Από το αρχείο MyOOS, αντιγράψτε το φάκελο mod σε οποιονδήποτε φάκελο.
+Ανεβάστε όλα τα αρχεία από το φάκελο mod στο διακομιστή ιστού σας. (π.χ. στο χαμηλότερο επίπεδο στο [server web directory/]mod)
+... Έγινε!
+Μπορείτε τώρα να καλέσετε το MyOOS [Dumper] στο πρόγραμμα περιήγησης στο διαδίκτυο πηγαίνοντας στο "https://example.com/mod/"
. +για να ολοκληρώσετε την εγκατάσταση. Απλά ακολουθήστε τις οδηγίες.
+
Σημείωση:
Αν στον διακομιστή σας το σενάριο δεν επιτρέπεται να δημιουργεί καταλόγους,
+θα πρέπει να το κάνετε αυτό χειροκίνητα, καθώς το MyOOS [Dumper] αποθηκεύει τα δεδομένα σε καταλόγους. +καταλόγους.
+Το σενάριο τερματίζεται με μια κατάλληλη δήλωση!
+Αφού δημιουργήσετε τους καταλόγους (σύμφωνα με την υπόδειξη), εκτελείται κανονικά και χωρίς περιορισμούς.
+ +

Οδηγίες για το σενάριο Perl

. +Τα περισσότερα έχουν έναν κατάλογο cgi-bin όπου μπορεί να εκτελεστεί η perl.
+Αυτό είναι συνήθως προσβάσιμο από το πρόγραμμα περιήγησης μέσω του http://www.example.com/cgi-bin/.
+
+Σε αυτή την περίπτωση, ακολουθήστε τα παρακάτω βήματα:

1. + +1. Καλέστε τη σελίδα δημιουργίας αντιγράφων ασφαλείας στο MyOOS [Dumper] και κάντε κλικ στο "Backup Perl".
+2. αντιγράψτε τη διαδρομή πίσω από την καταχώρηση στο crondump.pl για το $absolute_path_of_configdir:.
+3. ανοίξτε το αρχείο "crondump.pl" στον επεξεργαστή.
+4. εισαγάγετε την αντιγραμμένη διαδρομή εκεί στο absolute_path_of_configdir (χωρίς κενά).
+5. αποθηκεύστε το crondump.pl.
+Αντιγράψτε τα crondump.pl, perltest.pl και simpletest.pl στον κατάλογο cgi-bin (λειτουργία ascii στο FTP). +7. δώστε στα αρχεία τα δικαιώματα 755.
+7b. Αν επιθυμείτε την κατάληξη cgi, αλλάξτε την κατάληξη και των 3 αρχείων από pl -> cgi (μετονομασία).
+Καλέστε τη διαμόρφωση στο MyOOS [Dumper]. +9. Επιλέξτε τη σελίδα Cronscript.
+10. αλλάξτε τη διαδρομή εκτέλεσης της Perl σε /cgi-bin/ .
+10b. Εάν τα σενάρια έχουν επέκταση .pl, αλλάξτε την επέκταση του αρχείου σε .cgi .
+11. αποθηκεύστε τη διαμόρφωση.

+ +Έγινε, τα σενάρια μπορούν τώρα να κληθούν από τη σελίδα δημιουργίας αντιγράφων ασφαλείας.

+ + +Για όσους μπορούν να εκτελέσουν την Perl σε όλους τους καταλόγους, αρκούν τα ακόλουθα βήματα:

1. + +1. Καλέστε τη σελίδα δημιουργίας αντιγράφων ασφαλείας στο MyOOS [Dumper].
+Αντιγράψτε τη διαδρομή πίσω από την καταχώρηση στο crondump.pl για το $absolute_path_of_configdir:.
+Ανοίξτε το αρχείο "crondump.pl" στον επεξεργαστή.
+4. εισαγάγετε την αντιγραμμένη διαδρομή στο absolute_path_of_configdir (χωρίς κενά).
+5. αποθηκεύστε το crondump.pl .
+6. Δώστε στα αρχεία τα δικαιώματα 755.
+6b. Αν επιθυμείτε την κατάληξη cgi, αλλάξτε την κατάληξη και των 3 αρχείων από pl -> cgi (μετονομασία).
+(ev. 10b+11 από πάνω)
+
+ +Οι χρήστες των Windows πρέπει να αλλάξουν την πρώτη γραμμή όλων των σεναρίων, εκεί βρίσκεται η διαδρομή της Perl. Παράδειγμα:
+αντί για: #!/usr/bin/perl -w
+τώρα: #!C:_usr/bin/perl.exe -w
+ +

Λειτουργία

    + +
    Μενού
    . +Στην παραπάνω λίστα επιλογής ορίζετε τη βάση δεδομένων.
    +Όλες οι ενέργειες αναφέρονται στη βάση δεδομένων που έχει οριστεί εδώ. + +
    Αρχική σελίδα
    +Εδώ μπορείτε να ενημερωθείτε για το σύστημά σας, τις διάφορες εκδόσεις που είναι εγκατεστημένες και λεπτομέρειες σχετικά με τις ρυθμισμένες βάσεις δεδομένων. +εκδόσεις που είναι εγκατεστημένες και λεπτομέρειες σχετικά με τις ρυθμισμένες βάσεις δεδομένων.
    +Αν κάνετε κλικ στο όνομα της βάσης δεδομένων, θα δείτε μια λίστα με τους πίνακες και τον αριθμό των καταχωρήσεων. +με τον αριθμό των εγγραφών, το μέγεθος και την ημερομηνία της τελευταίας ενημέρωσης. + +
    Διαμόρφωση
    +Εδώ μπορείτε να επεξεργαστείτε τη διαμόρφωσή σας, να την αποθηκεύσετε ή να επαναφέρετε την αρχική διαμόρφωση. +επαναφέρετε την αρχική διαμόρφωση. +

      +
    • Διαμορφωμένες βάσεις δεδομένων: ο κατάλογος των διαμορφωμένων βάσεων δεδομένων. Η ενεργή βάση δεδομένων παρατίθεται με επίγραμμα.
    • +
    • Πρόθεμα πίνακα: εδώ μπορείτε να καθορίσετε ένα πρόθεμα (για κάθε βάση δεδομένων). Αυτό είναι ένα φίλτρο που λαμβάνει υπόψη μόνο τους πίνακες που αρχίζουν με αυτό το πρόθεμα κατά το ντάμπινγκ (π.χ. όλους τους πίνακες που αρχίζουν με "phpBB_"). Αν θέλετε να αποθηκευτούν όλοι οι πίνακες αυτής της βάσης δεδομένων, αφήστε το πεδίο κενό.
    • . +
    • Συμπίεση GZip: Εδώ μπορείτε να ενεργοποιήσετε τη συμπίεση. Συνιστάται η ενεργοποίησή του, καθώς τα αρχεία γίνονται πολύ μικρότερα και ο αποθηκευτικός χώρος είναι πάντα περιορισμένος. +
    • Email με το αρχείο dump: Αν αυτή η επιλογή είναι ενεργοποιημένη, αποστέλλεται ένα email με το αρχείο dump ως συνημμένο μετά την ολοκλήρωση του backup (προσοχή, η συμπίεση πρέπει οπωσδήποτε να είναι ενεργοποιημένη, διαφορετικά το συνημμένο θα είναι πολύ μεγάλο και μπορεί να μην αποσταλεί!). +
    • Διεύθυνση ηλεκτρονικού ταχυδρομείου: Διεύθυνση παραλήπτη για το μήνυμα ηλεκτρονικού ταχυδρομείου.
    • +
    • Αποστολέας του email: αυτή η διεύθυνση εμφανίζεται ως αποστολέας στο email.
    • +
    • Μεταφορά FTP: Εάν αυτή η επιλογή είναι ενεργοποιημένη, το αρχείο αντιγράφου ασφαλείας θα αποσταλεί μέσω FTP μετά την ολοκλήρωση του αντιγράφου ασφαλείας.
    • +
    • Διακομιστής FTP: Η διεύθυνση του διακομιστή FTP (π.χ. ftp.mybackups.de).
    • +
    • Θύρα διακομιστή FTP: Η θύρα του διακομιστή FTP (συνήθως 21).
    • +
    • Χρήστης FTP: Το όνομα χρήστη του λογαριασμού FTP.
    • +
    • Password FTP: Ο κωδικός πρόσβασης του λογαριασμού FTP.
    • +
    • Φάκελος μεταφόρτωσης FTP: Ο κατάλογος στον οποίο πρέπει να πηγαίνει το αρχείο αντιγράφου ασφαλείας (τα δικαιώματα μεταφόρτωσης πρέπει να υπάρχουν!).
    • +
    • Αυτόματη διαγραφή αντιγράφων ασφαλείας: Εάν αυτή η επιλογή είναι ενεργοποιημένη, τα παλαιότερα αντίγραφα ασφαλείας θα διαγράφονται αυτόματα σύμφωνα με τους ακόλουθους κανόνες.
    • +
    • Αριθμός αρχείων αντιγράφων ασφαλείας: Μια τιμή > 0 διαγράφει όλα τα αρχεία αντιγράφων ασφαλείας εκτός από τον αριθμό που καθορίζεται εδώ.
    • +
    • Γλώσσα: εδώ ορίζετε τη γλώσσα για τη διεπαφή.
    • +
    + +
    Διοίκηση
    +Εδώ εκτελούνται οι πραγματικές ενέργειες.
    +Εμφανίζονται όλα τα αρχεία στον κατάλογο αντιγράφων ασφαλείας. +Για τις ενέργειες "Επαναφορά" και "Διαγραφή" πρέπει να επιλεγεί ένα αρχείο. +
      +
    • Επαναφορά: Αυτή η επιλογή ενημερώνει τη βάση δεδομένων με το επιλεγμένο αρχείο αντιγράφων ασφαλείας.
    • +
    • Διαγραφή: Αυτό σας επιτρέπει να διαγράψετε το επιλεγμένο αρχείο αντιγράφου ασφαλείας.
    • +
    • Έναρξη νέου αντιγράφου ασφαλείας: Εδώ ξεκινάτε ένα νέο αντίγραφο ασφαλείας (dump) σύμφωνα με τις παραμέτρους που έχουν οριστεί στη ρύθμιση παραμέτρων. +
    + +
    Log
    +Εδώ μπορείτε να δείτε και να διαγράψετε τις εγγραφές καταγραφής. +
    Πιστωτικές μονάδες / Βοήθεια
    +αυτή τη σελίδα. +
diff --git a/msd/language/el/lang.php b/msd/language/el/lang.php new file mode 100644 index 0000000..51f5a88 --- /dev/null +++ b/msd/language/el/lang.php @@ -0,0 +1,112 @@ +δεν υπάρχει'; +$lang['L_VOM'] = 'από'; +$lang['L_MYSQLVARS'] = 'Μεταβλητές MySQL'; +$lang['L_MYSQLSYS'] = 'Εντολές MySQL'; +$lang['L_STATUS'] = 'Κατάσταση'; +$lang['L_PROZESSE'] = 'Διαδικασίες'; +$lang['L_INFO_NOVARS'] = 'Καμία μεταβλητή διαθέσιμη'; +$lang['L_INHALT'] = 'Τιμή'; +$lang['L_INFO_NOSTATUS'] = 'Καμία κατάσταση διαθέσιμη'; +$lang['L_INFO_NOPROCESSES'] = 'Δεν εκτελείται διαδικασία'; +$lang['L_FM_FREESPACE'] = 'Ελεύθερος χώρος στο διακομιστή'; +$lang['L_LOAD_DATABASE'] = 'Επαναφόρτωση Β.Δεδομένων'; +$lang['L_HOME'] = 'Αρχική'; +$lang['L_CONFIG'] = 'Ρυθμίσεις'; +$lang['L_DUMP'] = 'Αντίγραφα ασφαλείας'; +$lang['L_RESTORE'] = 'Επαναφορά'; +$lang['L_FILE_MANAGE'] = 'Διαχείριση αρχείων'; +$lang['L_LOG'] = 'Καταγραφές'; +$lang['L_CHOOSE_DB'] = 'Επιλογή Β.Δεδομένων'; +$lang['L_CREDITS'] = 'Επαινοι / Βοήθεια'; +$lang['L_MULTI_PART'] = 'Multipart Αντίγραφα ασφαλείας'; +$lang['L_LOGFILENOTWRITABLE'] = 'Αδυναμία εγγραφής Καταγραφών !'; +$lang['L_SQL_ERROR1'] = 'Σφάλμα στο ερώτημα:'; +$lang['L_SQL_ERROR2'] = 'Η MySQL λέει:'; +$lang['L_UNKNOWN'] = 'αγνωστο'; +$lang['L_UNKNOWN_NUMBER_OF_RECORDS'] = 'άγνωστο'; +$lang['L_OK'] = 'ΟΚ'; +$lang['L_CRON_COMPLETELOG'] = 'Εξοδος Καταγραφών πλήρης'; +$lang['L_NO'] = 'όχι'; +$lang['L_CREATE_DATABASE'] = 'Δημιουργία νέας Β.Δεδομένων'; +$lang['L_EXPORTFINISHED'] = 'Εξαγωγή τελειωμένη.'; +$lang['L_SQL_BROWSER'] = 'Πλοηγός SQL'; +$lang['L_SERVER'] = 'Διακομιστής'; +$lang['L_MYSQL_CONNECTION_ENCODING'] = 'Στανταρ κωδικοποίηση του διακομιστή MySQL + + +'; +$lang['L_TITLE_SHOW_DATA'] = 'Προβολή δεδομένων'; +$lang['L_PRIMARYKEY_CONFIRMDELETE'] = 'Διαγραφή πρωτεύον κλειδιού?'; +$lang['L_SETPRIMARYKEYSFOR'] = 'Ορισμός πρωτεύοντων κλειδιών για πίνακες'; +$lang['L_PRIMARYKEY_FIELD'] = 'Πεδίο πρωτεύον κλειδιού'; +$lang['L_PRIMARYKEYS_SAVE'] = 'Αποθήκευση πρωτεύοντων κλειδιών'; +$lang['L_CANCEL'] = 'άκυρο'; +$lang['L_VISIT_HOMEPAGE'] = 'Επισκεφθείτε την Ιστοσελίδα'; +$lang['L_SECONDS'] = 'Δευτερόλεπτα'; +$lang['L_BACKUPS'] = 'Αντίγραφα Ασφαλείας'; +$lang['L_MINUTES'] = 'Minutes'; +$lang['L_PAGE_REFRESHS'] = 'Page refreshs'; +$lang['L_MINUTE'] = 'Minute'; +$lang['L_SETKEYSFOR'] = 'Set new indexes for table'; +$lang['L_KEY_CONFIRMDELETE'] = 'Really delete index?'; diff --git a/msd/language/el/lang_config_overview.php b/msd/language/el/lang_config_overview.php new file mode 100644 index 0000000..e5c5a96 --- /dev/null +++ b/msd/language/el/lang_config_overview.php @@ -0,0 +1,128 @@ +%s
στο %s'; +$lang['L_FTP'] = 'FTP'; +$lang['L_SFTP_SEND_TO'] = 'Προς %s
στο %s'; +$lang['L_SFTP'] = 'FTP'; +$lang['L_EMAIL_CC'] = 'Κοινοπ.'; +$lang['L_NAME'] = 'Ονομα'; +$lang['L_CONFIRM_CONFIGFILE_DELETE'] = 'Διαγραφή του αρχείου ρύθμισης %s?'; +$lang['L_ERROR_DELETING_CONFIGFILE'] = 'Σφάλμα: Δεν μπορεί να διαγραφεί το αρχείο ρύθμισης %s!'; +$lang['L_SUCCESS_DELETING_CONFIGFILE'] = 'Το αρχείο ρύθμισης %s διαγράφηκε επιτυχώς.'; +$lang['L_SUCCESS_CONFIGFILE_CREATED'] = 'Το αρχείο ρύθμισης %s δημιουργήθηκε επιτυχώς.'; +$lang['L_ERROR_CONFIGFILE_NAME'] = 'Το όνομα αρχείου "%s" περιέχει μη αποδεκτούς χαρακτήρες.'; +$lang['L_CREATE_CONFIGFILE'] = 'Δημιουργία νέου αρχείου ρύθμισης'; +$lang['L_ERROR_LOADING_CONFIGFILE'] = 'δεν ανοίγει το αρχείο ρύθμισης "%s".'; +$lang['L_BACKUP_DBS_PHP'] = 'ΒΔ για αντιγ. ασφαλείας (PHP)'; +$lang['L_BACKUP_DBS_PERL'] = 'ΒΔ για αντιγ. ασφαλείας (PERL)'; +$lang['L_CRON_COMMENT'] = 'Δώστε σχόλιο'; +$lang['L_AUTODETECT'] = 'αυτόματος εντοπισμός'; diff --git a/msd/language/el/lang_dump.php b/msd/language/el/lang_dump.php new file mode 100644 index 0000000..6ce9088 --- /dev/null +++ b/msd/language/el/lang_dump.php @@ -0,0 +1,58 @@ +%s` '; +$lang['L_DUMP_ENDERGEBNIS'] = 'Το αρχείο περιέχει %s πίνακες με %s εγγραφές.
'; +$lang['L_MAILERROR'] = 'Η αποστολή email απέτυχε!'; +$lang['L_EMAILBODY_ATTACH'] = 'Το συννημένο περιέχει αντίγραφο ασφαλείας της Βάσης MySQL.
Αντίγραφο της Β.Δεδομένων `%s` +

Το παρακάτω αρχείο δημιουργήθηκε:

%s

Ευχαριστίες

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_MP_NOATTACH'] = 'Ενα αντίγραφο ασφαλείας Multipart δημιουργήθηκε.
Τα Αντίγραφα ασφαλείας δεν επισυνάφθηκαν σε αυτό το email!
Αντίγραφο της Β.Δεδομένων `%s` +

Δημιουργήθηκαν τα παρακάτω αρχεία:

%s +

Ευχαριστίες

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_MP_ATTACH'] = 'Δημιουργήθηκε ένα Multipart Αντίγραφο ασφαλείας.
Τα Αντίγραφα ασφαλείας επισυνάφθηκαν σε χωριστά emails.
Αντίγραφο της Β.Δεδομένων `%s` +

Δημιουργήθηκαν τα παρακάτω αρχεία:

%s

Ευχαριστίες

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_FOOTER'] = '`

Ευχαριστώ

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_TOOBIG'] = 'Το αντίγραφο ασφαλείας έχει υπερβεί το μέγιστο μέγεθος %s και δεν επισυνάφθηκε σε αυτό το email.
Αντίγραφο της Β.Δεδομένων `%s` +

Δημιουργήθηκε το παρακάτω αρχείο:

%s +

Ευχαριστίες

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_NOATTACH'] = 'Τα αρχεία δεν επισυνάφθηκαν σε αυτό το email!
Αντίγραφο της Β.Δεδομένων `%s` +

Δημιουργήθηκε το παρακάτω αρχείο:

%s +

Ευχαριστίες

MyOOS [Dumper]
'; +$lang['L_EMAIL_ONLY_ATTACHMENT'] = ' ... συννημένο μόνο.'; +$lang['L_TABLESELECTION'] = 'Επιλογή πίνακα'; +$lang['L_SELECTALL'] = 'Επιλογή όλων'; +$lang['L_DESELECTALL'] = 'Αποεπιλογή όλων'; +$lang['L_STARTDUMP'] = 'Εκκίνηση Αντιγράφων Ασφαλείας'; +$lang['L_LASTBUFROM'] = 'Τελευταία αναβάθμιση από'; +$lang['L_NOT_SUPPORTED'] = 'Το αντίγραφο ασφαλείας δεν υποστηρίζει αυτή τη λειτουργία.'; +$lang['L_MULTIDUMP'] = 'Multidump: Το αντίγραφο ασφαλείας της Β.Δεδομένων %d ολοκληρώθηκε.'; +$lang['L_FILESENDFTP'] = 'αποστολή αρχείου μέσω FTP... παρακαλώ περιμένετε. '; +$lang['L_FTPCONNERROR'] = 'Δεν έγινε σύνδεση FTP ! ΄Σύνδεση με '; +$lang['L_FTPCONNERROR1'] = ' σαν χρήστης '; +$lang['L_FTPCONNERROR2'] = ' δεν είναι δυνατόν'; +$lang['L_FTPCONNERROR3'] = 'Η Φόρτωση FTP απέτυχε! '; +$lang['L_FTPCONNECTED1'] = 'Συνδεμένο με '; +$lang['L_FTPCONNECTED2'] = ' σε '; +$lang['L_FTPCONNECTED3'] = ' Επιτυχής μεταφορά'; +$lang['L_NR_TABLES_SELECTED'] = '- με %s επιλεγμένους πίνακες'; +$lang['L_FILESENDSFTP'] = 'αποστολή αρχείου μέσω SFTP... παρακαλώ περιμένετε. '; +$lang['L_SFTPCONNERROR'] = 'Δεν έγινε σύνδεση SFTP ! ΄Σύνδεση με '; +$lang['L_NR_TABLES_OPTIMIZED'] = '%s πίνακες έχουν βελτιστοποιηθεί.'; +$lang['L_DUMP_ERRORS'] = '

%s σφάλματα παρουσιάστηκαν: view

'; +$lang['L_FATAL_ERROR_DUMP'] = "Γενικό Σφάλμα: Η δήλωση CREATE-Statement του πίνακα '%s' στη Β.Δεδομένων '%s' δε μπορεί να διαβαστεί!"; diff --git a/msd/language/el/lang_filemanagement.php b/msd/language/el/lang_filemanagement.php new file mode 100644 index 0000000..a94b7ed --- /dev/null +++ b/msd/language/el/lang_filemanagement.php @@ -0,0 +1,79 @@ +%s"'; +$lang['L_DELETE_FILE_ERROR'] = 'Σφάλμα στη διαγραφή του αρχείου "%s"!'; +$lang['L_FM_DUMP_HEADER'] = 'Αντίγραφα Ασφαλείας'; +$lang['L_DOCRONBUTTON'] = 'Εκτέλεση του Perl Cron script'; +$lang['L_DOPERLTEST'] = 'Δοκιμή μονάδων Perl'; +$lang['L_DOSIMPLETEST'] = 'Δοκιμή Perl'; +$lang['L_PERLOUTPUT1'] = 'Εισαγωγή στο crondump.pl για absolute_path_of_configdir'; +$lang['L_PERLOUTPUT2'] = 'Δεσμός URL για τον πλοηγό ή για εξωτερική Cron job'; +$lang['L_PERLOUTPUT3'] = 'Γραμμή εντολών για το Κέλυφος ή για το Crontab'; +$lang['L_RESTORE_OF_TABLES'] = 'Επιλέξτε τους πίνακες που θα επαναφερθούν'; +$lang['L_CONVERTER'] = 'Μετατροπέας Αντιγράφων ασφαλείας'; +$lang['L_CONVERT_FILE'] = 'Αρχεία για μετατροπή'; +$lang['L_CONVERT_FILENAME'] = 'Ονομα αρχείου προορισμού (χωρίς κατάληξη)'; +$lang['L_CONVERTING'] = 'Μετατροπή'; +$lang['L_CONVERT_FILEREAD'] = "Ανάγνωση αρχείου '%s'"; +$lang['L_CONVERT_FINISHED'] = "Η μετατροπή τελείωσε, '%s' εγγράφηκε επιτυχώς."; +$lang['L_NO_MOD_BACKUPFILE'] = 'Αντίγραφα Ασφαλείας άλλων scripts'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Μέγιστο μέγεθος αρχείου'; +$lang['L_MAX_UPLOAD_SIZE_INFO'] = 'Αν το Dumpfile είναι μεγαλύτερο από το παραπάνω επιτρεπτό όριο, φορτώστε το με FTP στον κατάλογο "work/backup". +Μετά μπορείτε να το επιλέξετε για επαναφορά. '; +$lang['L_ENCODING'] = 'κωδικοποίηση'; +$lang['L_FM_CHOOSE_ENCODING'] = 'Επιλογή κωδικοποίησης για το Αντίγραφο ασφαλείας'; +$lang['L_CHOOSE_CHARSET'] = 'Το MyOOS [Dumper] δε μπόρεσε να αναγνωρίσει αυτόματα την κωδικοποίηση του αντιγράφου ασφαλείας. +
Επιλέξτε το σετ χαρακτήρων με το οποίο αποθηκέυθηκε το αντίγραφο ασφαλείας. +
Αν παρουσιαστούν προβλήματα με κάποιους χαρακτήρες μετά την επαναφορά, επαναλάβετε την επαναφορά κι επιλέξτε άλλο σετ χαρακτήρων. +
Καλή επιτυχία. ;)'; +$lang['L_DOWNLOAD_FILE'] = 'Μεταφόρτωση αρχείου'; +$lang['L_BACKUP_NOT_POSSIBLE'] = 'A backup of the system database `%s` is not possible!'; diff --git a/msd/language/el/lang_help.php b/msd/language/el/lang_help.php new file mode 100644 index 0000000..943df6d --- /dev/null +++ b/msd/language/el/lang_help.php @@ -0,0 +1,40 @@ +Η εγκατάσταση ολοκληρώθηκε --> εκκίνηση MyOOS [Dumper]
'; +$lang['L_INSTALL_TOMENU'] = 'Πίσω στο κυρίως μενού'; +$lang['L_INSTALLMENU'] = 'Κυρίως μενού'; +$lang['L_STEP'] = 'Βήμα'; +$lang['L_INSTALL'] = 'Εγκατάσταση'; +$lang['L_UNINSTALL'] = 'Απεγκατάσταση'; +$lang['L_TOOLS'] = 'Εργαλεία'; +$lang['L_EDITCONF'] = 'Επεξεργασία ρύθμισης'; +$lang['L_OSWEITER'] = 'Συνέχεια χωρίς αποθήκευση'; +$lang['L_ERRORMAN'] = 'Σφάλμα κατά την αποθήκευση της ρύθμισης!
Παρακαλώ επεξεργαστείτε το αρχείο '; +$lang['L_MANUELL'] = 'χειροκίνητα'; +$lang['L_CREATEDIRS'] = 'Δημιουργία Καταλόγων'; +$lang['L_INSTALL_CONTINUE'] = 'Συνεχίστε την εγκατάσταση'; +$lang['L_CONNECTTOMYSQL'] = 'Σύνδεση με MySQL '; +$lang['L_DBPARAMETER'] = 'Παράμετροι Β.Δεδομένων'; +$lang['L_CONFIGNOTWRITABLE'] = 'Δεν μπορώ να γράψω στο αρχείο "config.php". +Παρακαλώ χρησιμοποιήστε το πρόγραμμα FTP και αλλάξτε το chmod του αρχείου σε 0777.'; +$lang['L_DBCONNECTION'] = 'Σύνδεση Β.Δεδομένων'; +$lang['L_CONNECTIONERROR'] = 'Σφάλμα: αδυναμία σύνδεσης.'; +$lang['L_CONNECTION_OK'] = 'Η σύνδεση με τη Β.Δεδομένων είναι επιτυχής.'; +$lang['L_SAVEANDCONTINUE'] = 'Αποθήκευση και συνέχεια με την εγκατάσταση'; +$lang['L_CONFBASIC'] = 'Βασικές παράμετροι'; +$lang['L_INSTALL_STEP2FINISHED'] = 'Οι παράμετροι Β.Δεδομένων αποθηκεύτηκαν επιτυχώς.'; +$lang['L_INSTALL_STEP2_1'] = 'Συνέχεια εγκατάστασης με τις προεπιλεγμένες ρυθμίσεις'; +$lang['L_LASTSTEP'] = 'Τέλος εγκατάστασης'; +$lang['L_FTPMODE'] = 'Δημιουργία απαραίτητων καταλόγων σε ασφαλή λειτουργία'; +$lang['L_IDOMANUAL'] = 'Δημιουργώ τους καταλόγους μόνος μου'; +$lang['L_DOFROM'] = 'εκκίνηση από'; +$lang['L_FTPMODE2'] = 'Δημιουργία καταλόγων με FTP:'; +$lang['L_CONNECT'] = 'σύνδεση'; +$lang['L_DIRS_CREATED'] = 'Οι κατάλογοι δημιουργήθηκαν και δώθηκαν οι απαραίτητες προσβάσεις.'; +$lang['L_CONNECT_TO'] = 'σύνδεση με'; +$lang['L_CHANGEDIR'] = 'αλλαγή σε κατάλογο'; +$lang['L_CHANGEDIRERROR'] = 'η αλλαγή σε κατάλογο δεν έγινε'; +$lang['L_FTP_OK'] = 'Οι παράμετροι FTP είναι εντάξει'; +$lang['L_CREATEDIRS2'] = 'Δημιουργία καταλόγων'; +$lang['L_FTP_NOTCONNECTED'] = 'Η σύνδεση FTP δεν έγινε!'; +$lang['L_CONNWITH'] = 'Σύνδεση με'; +$lang['L_ASUSER'] = 'όπως χρήστη'; +$lang['L_NOTPOSSIBLE'] = 'δε γίνεται'; +$lang['L_DIRCR1'] = 'δημιουργία καταλόγου work'; +$lang['L_DIRCR2'] = 'δημιουργία καταλόγου backup'; +$lang['L_DIRCR4'] = 'δημιουργία καταλόγου log'; +$lang['L_DIRCR5'] = 'δημιουργία καταλόγου configuration'; +$lang['L_INDIR'] = 'τώρα στον κατάλογο'; +$lang['L_CHECK_DIRS'] = 'Ελεγχος καταλόγων'; +$lang['L_DISABLEDFUNCTIONS'] = 'Ανενεργές λειτουργίες'; +$lang['L_NOFTPPOSSIBLE'] = 'Δεν έχετε λειτουργίες FTP !'; +$lang['L_NOGZPOSSIBLE'] = 'Δεν έχετε λειτουργίες συμπίεσης !'; +$lang['L_UI1'] = 'Ολοι οι κατάλογοι που μπορεί να περιέχουν αντίγραφα ασφαλείας θα διαγραφούν.'; +$lang['L_UI2'] = 'Το θέλετε σίγουρα?'; +$lang['L_UI3'] = 'Οχι, ακύρωσε το αμέσως'; +$lang['L_UI4'] = 'Ναι, παρακαλώ συνέχισε'; +$lang['L_UI5'] = 'διαγραφή καταλόγων λειτουργίας'; +$lang['L_UI6'] = 'όλα διαγράφηκαν επιτυχώς.'; +$lang['L_UI7'] = 'Παρακαλώ διαγράψτε τον κατάλογο script'; +$lang['L_UI8'] = 'ένα επίπεδο πάνω'; +$lang['L_UI9'] = 'Ενα Σφάλμα δεν επιτρέπει τη διαγραφή

Σφάλμα με τον κατάλογο '; +$lang['L_IMPORT'] = 'Εισαγωγή ρυθμίσεων'; +$lang['L_IMPORT3'] = 'Οι ρυθμίσεις φορτώθηκαν ...'; +$lang['L_IMPORT4'] = 'Οι ρυθμίσεις αποθηκεύτηκαν.'; +$lang['L_IMPORT5'] = 'Εκκίνηση MyOOS [Dumper]'; +$lang['L_IMPORT6'] = 'Μενού εγκατάστασης'; +$lang['L_IMPORT7'] = 'Φόρτωση ρυθμίσεων'; +$lang['L_IMPORT8'] = 'πίσω σε φόρτωση'; +$lang['L_IMPORT9'] = 'Αυτό δεν είναι αντίγραφο ασφαλείας ρυθμίσεων !'; +$lang['L_IMPORT10'] = 'Οι ρυθμίσεις φορτώθηκαν κανονικά ...'; +$lang['L_IMPORT11'] = 'Σφάλμα: Εμφανίστηκε πρόβλημα στην εγγραφή του sql_statements'; +$lang['L_IMPORT12'] = 'Σφάλμα: Εμφανίστηκε πρόβλημα στην εγγραφή του config.php'; +$lang['L_INSTALL_HELP_PORT'] = '(άδειο = προεπιλεγμένη θύρα)'; +$lang['L_INSTALL_HELP_SOCKET'] = '(άδειο = προεπιλεγμένο Socket)'; +$lang['L_TRYAGAIN'] = 'Δοκιμάστε πάλι'; +$lang['L_SOCKET'] = 'Socket'; +$lang['L_PORT'] = 'Θύρα'; +$lang['L_FOUND_DB'] = 'βρέθηκε Β.Δ.'; +$lang['L_FM_FILEUPLOAD'] = 'Φόρτωση αρχείου'; +$lang['L_PASS'] = 'Κωδικός'; +$lang['L_NO_DB_FOUND_INFO'] = 'Η σύνδεση με τη Β.Δεδομένων έγινε επιτυχώς.
+Τα δεδομένα χρήστη είναι σωστά και έγιναν αποδεκτά από τον διακομιστή MySQL.
+Αλλά το MyOOS [Dumper] δε βρήκε καμία Β.Δεδομένων.
+Η αυτόματη αναγνώριση μέσω script μπλοκάρεται σε κάποιον διακομιστή.
+Δώστε το όνομα της βάσης σας χειροκίνητα μόλις τελειώσει η εγκατάσταση. +Καντε κλικ στο "Ρυθμίσεις" "Παράμετροι Σύνδεσης - προβολή" και δώστε εκεί το όνομα της βάσης σας.'; +$lang['L_ENTER_DB_INFO'] = 'First click the button "Connect to MySQL". Only if no database could be detected you need to provide a database name here.'; diff --git a/msd/language/el/lang_log.php b/msd/language/el/lang_log.php new file mode 100644 index 0000000..c4ba5ad --- /dev/null +++ b/msd/language/el/lang_log.php @@ -0,0 +1,9 @@ +Παρακαλώ δημιουργήστε τα 2 αρχεία χειροκίνητα με τα παρακάτω περιεχόμενα'; +$lang['L_HTACC_CHECK_ERROR'] = 'It could not be checked whether the program is protected!
The simulated external access could not be carried out.'; +$lang['L_HTACC_NOT_NEEDED'] = 'The program is protected by higher-level authorizations; local directory protection is not required.'; +$lang['L_HTACC_COMPLETE'] = 'The program is protected, the directory protection is complete.'; +$lang['L_HTACC_INCOMPLETE'] = 'The program is not protected, the directory protection is incomplete!'; +$lang['L_HTACC_PROPOSED'] = 'Προτείνεται επειγόντως'; +$lang['L_HTACC_EDIT'] = 'Επεξεργασία .htaccess'; +$lang['L_HTACCESS18'] = 'Δημιουργία .htaccess σε '; +$lang['L_HTACCESS19'] = 'Επαναφόρτωση '; +$lang['L_HTACCESS20'] = 'Εκτέλεση script'; +$lang['L_HTACCESS21'] = 'Προσθήκη handler'; +$lang['L_HTACCESS22'] = 'Κάνε εκτελέσιμο'; +$lang['L_HTACCESS23'] = 'Λίστα καταλόγου'; +$lang['L_HTACCESS24'] = 'Αρχείο σφαλμάτων'; +$lang['L_HTACCESS25'] = 'Ενεργοποίηση rewrite'; +$lang['L_HTACCESS26'] = 'Επέτρεψε / Απαγόρευσε'; +$lang['L_HTACCESS27'] = 'Ανακατεύθυνση'; +$lang['L_HTACCESS28'] = 'Καταγραφές σφαλμάτων'; +$lang['L_HTACCESS29'] = 'Περισσότερα παραδείγματα και λεπτομέρειες'; +$lang['L_HTACCESS30'] = 'Πάροχος'; +$lang['L_HTACCESS31'] = 'Γενικά'; +$lang['L_HTACCESS32'] = 'Προσοχή! Το .htaccess επηρεάζει άμεσα τη συμπεριφορά του πλοηγού.
Με λάθος περιεχόμενο, αυτές οι σελίδες δεν θα είναι προσβάσιμες πλέον.'; +$lang['L_DISABLEDFUNCTIONS'] = 'Ανενεργές λειτουργίες'; +$lang['L_NOGZPOSSIBLE'] = 'Επειδή το Zlib δεν είναι εγκατεστημένο, δε μπορείτε να κάνετε χρήση του GZip!'; +$lang['L_DELETE_HTACCESS'] = 'Κατάργηση προστασίας καταλόγου (διαγραφή .htaccess)'; +$lang['L_WRONG_RIGHTS'] = "Το αρχείο ή ο κατάλογος '%s' δεν είναι εγγράψιμος.
+Τα δικαιώματα (chmod) δε ρυθμίστηκαν σωστά ή έχουν λάθος ιδιοκτήτη.
+Δώστε τις σωστές ιδιότητες χρησιμοποιώντας το πρόγραμμα FTP.
+Το αρχείο ή ο κατάλογος πρέπει να ρυθμιστεί σε %s.
"; +$lang['L_CANT_CREATE_DIR'] = "Δε δημιουργήθηκε κατάλογος '%s'. +Δημιουργήστε τον χρησιμοποιώντας το πρόγραμμα FTP."; +$lang['L_TABLE_TYPE'] = 'Τύπος'; +$lang['L_CHECK'] = 'έλεγχος'; +$lang['L_OS'] = 'Λειτουργικό Σύστημα'; +$lang['L_MOD_VERSION'] = 'Εκδοση MyOOS [Dumper]'; +$lang['L_NEW_MOD_VERSION'] = 'Νέα έκδοση'; +$lang['L_NEW_MOD_VERSION_INFO'] = 'Μια νέα έκδοση του MyOOS [Dumper] είναι διαθέσιμη.'; +$lang['L_UPDATED_IMPORTANT'] = 'Important: Before updating, please backup your files.'; +$lang['L_UPDATE'] = 'Update now'; +$lang['L_MYSQL_VERSION'] = 'Εκδοση MySQL'; +$lang['L_PHP_VERSION'] = 'Εκδοση PHP'; +$lang['L_MAX_EXECUTION_TIME'] = 'Μεγ. χρόνος εκτέλεσης'; +$lang['L_PHP_EXTENSIONS'] = 'Επεκτάσεις PHP'; +$lang['L_MEMORY'] = 'Μνήμη'; +$lang['L_FILE_MISSING'] = 'δε βρέθηκε το αρχείο'; +$lang['L_INSTALLING_UPDATES'] = 'Εγκατάσταση ενημερώσεων'; +$lang['L_UPDATE_SUCCESSFUL'] = 'Επιτυχής ενημέρωση'; +$lang['L_UPDATE_FAILED'] = 'Αποτυχημένη ενημέρωση'; +$lang['L_UP_TO_DATE'] = 'Η τρέχουσα έκδοση είναι ενημερωμένη'; diff --git a/msd/language/el/lang_restore.php b/msd/language/el/lang_restore.php new file mode 100644 index 0000000..36424db --- /dev/null +++ b/msd/language/el/lang_restore.php @@ -0,0 +1,22 @@ +%d πίνακες.'; +$lang['L_FILE_MISSING'] = 'δε βρέθηκε το αρχείο'; +$lang['L_RESTORE_DB'] = "Β.Δεδομένων '%s' σε '%s'."; +$lang['L_RESTORE_COMPLETE'] = '%s πίνακες δημιουργήθηκαν.'; +$lang['L_RESTORE_RUN1'] = '
Μέχρι τώρα %s από %s εγγραφές έχουν προστεθεί.'; +$lang['L_RESTORE_RUN2'] = "
Τώρα ο πίνακας '%s' επαναφέρεται.

"; +$lang['L_RESTORE_COMPLETE2'] = '%s εγγραφές έχουν εισαχθεί.'; +$lang['L_RESTORE_TABLES_COMPLETED'] = 'Μέχρι τώρα %d από %d πίνακες δημιουργήθηκαν.'; +$lang['L_RESTORE_TOTAL_COMPLETE'] = '
Συγχαρητήρια.

Εγινε η επαναφορά της Β.Δεδομένων.
Ολα τα δεδομένα απο το Αντίγραφο Ασφαλείας έχουν επαναφερθεί.

Ολα ολοκληρώθηκαν. :-)'; +$lang['L_DB_SELECT_ERROR'] = '
Σφάλμα:
Επιλογή Β.Δεδομένων '; +$lang['L_DB_SELECT_ERROR2'] = ' απέτυχε!'; +$lang['L_FILE_OPEN_ERROR'] = 'Σφάλμα: δεν μπόρεσα να ανοίξω το αρχείο.'; +$lang['L_PROGRESS_OVER_ALL'] = 'Γενική πρόοδος'; +$lang['L_BACK_TO_OVERVIEW'] = 'Προεπισκόπιση Β.Δεδομένων'; +$lang['L_RESTORE_RUN0'] = '
Μέχρι τώρα %s εγγραφές έχουν προστεθεί.'; +$lang['L_UNKNOWN_SQLCOMMAND'] = 'Αγνωστη εντολή SQL'; +$lang['L_NOTICES'] = 'Σημειώσεις + + +'; diff --git a/msd/language/el/lang_sql.php b/msd/language/el/lang_sql.php new file mode 100644 index 0000000..5efcc02 --- /dev/null +++ b/msd/language/el/lang_sql.php @@ -0,0 +1,190 @@ +%s γραμμές έχουν εξαχθεί'; +$lang['L_CSV_FIELDCOUNT_NOMATCH'] = 'Η αρίθμηση των πεδίων δε ταιριάζει με αυτή των δεδομένων που θα εισαχθούν (%d αντί για %d).'; +$lang['L_CSV_FIELDSLINES'] = '%d πεδία αναγνωρίστηκαν, συνολικά %d γραμμές'; +$lang['L_CSV_ERRORCREATETABLE'] = 'Σφάλμα κατά την δημιουργία του πίνακα `%s` !'; +$lang['L_FM_UPLOADFILEREQUEST'] = 'παρακαλώ επιλέξτε ένα αρχείο.'; +$lang['L_CSV_NODATA'] = 'Δε βρέθηκαν δεδομένα για εισαγωγή!'; +$lang['L_SQLLIB_GENERALFUNCTIONS'] = 'γενικές λειτουργίες'; +$lang['L_SQLLIB_RESETAUTO'] = 'επαναφορά αυτόματης αύξησης'; +$lang['L_SQLLIB_BOARDS'] = 'Κοινότητες'; +$lang['L_SQLLIB_DEACTIVATEBOARD'] = 'απενεργοποίηση Board'; +$lang['L_SQLLIB_ACTIVATEBOARD'] = 'ενεργοποίηση Board'; +$lang['L_SQL_NOTABLESSELECTED'] = 'Δεν επιλέχθηκαν πίνακες !'; +$lang['L_TOOLS'] = 'Εργαλεία'; +$lang['L_TOOLS_TOOLBOX'] = 'Επιλογή Β.Δεδομένων / Λειτουργίες Β.Δεδομένων / Εισαγωγή - Εξαγωγή '; +$lang['L_SQL_OPENFILE'] = 'Ανοιγμα αρχείου SQL'; +$lang['L_SQL_OPENFILE_BUTTON'] = 'Φόρτωση'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Μέγιστο μέγεθος αρχείου'; +$lang['L_SQL_SEARCH'] = 'Αναζήτηση'; +$lang['L_SQL_SEARCHWORDS'] = 'Αναζήτηση λέξεων'; +$lang['L_START_SQL_SEARCH'] = 'Εκκίνηση αναζήτησης'; +$lang['L_RESET_SEARCHWORDS'] = 'Επαναφορά λέξεων αναζήτησης'; +$lang['L_SEARCH_OPTIONS'] = 'Επιλογές αναζήτησης'; +$lang['L_SEARCH_RESULTS'] = 'Η αναζήτηση για "%s" στον πίνακα "%s" φέρνει τα ακόλουθα αποτελέσματα'; +$lang['L_SEARCH_NO_RESULTS'] = 'Η αναζήτηση για "%s" στον πίνακα "%s" δεν έχει αποτελέσματα!'; +$lang['L_NO_ENTRIES'] = 'Ο πίνακας "%s" είναι άδειος και δεν έχει καταχωρήσεις.'; +$lang['L_SEARCH_ACCESS_KEYS'] = 'Πλοήγηση: εμπρός=ALT+V, πίσω=ALT+C'; +$lang['L_SEARCH_OPTIONS_OR'] = 'Η στήλη πρέπει να έχει τουλάχιστον μία από τις λέξεις αναζήτησης (OR-search)'; +$lang['L_SEARCH_OPTIONS_CONCAT'] = 'Η γραμμή πρέπει περιλαμβάνει όλες τις λέξεις αναζήτησης αλλά μπορεί να είναι σε κάποια στήλη (χρειάζεται λίγο χρόνο)'; +$lang['L_SEARCH_OPTIONS_AND'] = 'Η γραμμή πρέπει περιλαμβάνει όλες τις λέξεις αναζήτησης (AND-search)'; +$lang['L_SEARCH_IN_TABLE'] = 'Αναζήτηση στον πίνακα'; +$lang['L_ERROR_NO_FIELDS'] = 'Search error: it could not be determined which fields the table "%s" has!'; +$lang['L_SQL_EDIT_TABLESTRUCTURE'] = 'Επεξεργασία δομής πίνακα'; +$lang['L_DEFAULT_CHARSET'] = 'Προεπιλεγμένο σετ χαρακτήρων'; +$lang['L_TITLE_KEY_PRIMARY'] = 'Πρωτεύων κλειδί'; +$lang['L_TITLE_KEY_UNIQUE'] = 'Μοναδικό κλειδί'; +$lang['L_TITLE_INDEX'] = 'Ευρετήριο'; +$lang['L_TITLE_KEY_FULLTEXT'] = 'Κλειδί πλήρη κειμένου'; +$lang['L_TITLE_NOKEY'] = 'Κανένα κλειδί'; +$lang['L_TITLE_SEARCH'] = 'Αναζήτηση'; +$lang['L_TITLE_MYSQL_HELP'] = 'Τεκμηρίωση MySQl'; +$lang['L_TITLE_UPLOAD'] = 'Φόρτωση αρχείου SQL'; +$lang['L_PRIMARYKEY_DELETED'] = 'Το πρωτεύων κλειδί διαγράφηκε'; +$lang['L_PRIMARYKEY_NOTFOUND'] = 'Δε βρέθηκε πρωτεύων κλειδί'; +$lang['L_PRIMARYKEYS_CHANGED'] = 'Τα πρωτεύοντα κλειδιά άλλαξαν'; +$lang['L_PRIMARYKEYS_CHANGINGERROR'] = 'Σφάλμα στην αλλαγή πρωτεύοντων κλειδιών'; +$lang['L_SQL_VIEW_COMPACT'] = 'Προβολή: Συμπαγής'; +$lang['L_SQL_VIEW_STANDARD'] = 'Προβολή: Κανονική'; +$lang['L_FIELDS_OF_TABLE'] = 'Πεδία πινάκων'; +$lang['L_ENGINE'] = 'Μηχανή'; +$lang['L_USERNAME'] = 'Ονομα χρήστη'; +$lang['L_PASSWORD'] = 'Κωδικός'; +$lang['L_PASSWORD_REPEAT'] = 'Κωδικός (Επανάληψη)'; +$lang['L_INFO_SIZE'] = 'Μέγεθος'; +$lang['L_TABLE_TYPE'] = 'Τύπος'; +$lang['L_KEY_DELETED'] = 'Index deleted'; +$lang['L_KEY_DELETEERROR'] = 'Error deleting index'; +$lang['L_KEY_ADDED'] = 'Index added'; +$lang['L_KEY_ADDERROR'] = 'Error adding index'; diff --git a/msd/language/en/help.html b/msd/language/en/help.html new file mode 100644 index 0000000..fe88bc4 --- /dev/null +++ b/msd/language/en/help.html @@ -0,0 +1,147 @@ +
+

MyOOS [Dumper] based on MySQLDumper 1.24.4

+ +

About this project

+

MyOOS [Dumper] is an improved version of MySQLDumper 1.24.4 (January 24, 2011). This enhancement takes into account the development of PHP.

. +

Most of all stability, security and handling are the main focus of MyOOS [Dumper]. But also an attractive template is included, which can be edited and adapted to your own needs.

. + + +

MyOOS [Dumper] is a backup program for MySQL databases, written in PHP and Perl. With it, backup copies of the data (store, blog, etc.) can be created and restored if necessary. Especially for web space without shell access, MyOOS [Dumper] is a useful alternative.

. + +

The idea for MySQLDumper came from Daniel Schlichtholz. He opened the MySQLDumper forum in 2004, whereupon programmers wrote new scripts and extended existing ones.

+ + + +

Wish List / Future Attractions

. +

Do you have any suggestions for improvements? Feel free to contact the development team via the forum https://foren.myoos.de/viewforum.php?f=41.

+ + +

Contribute

+

If you would like to help us improve the MyOOS project, we welcome your pull requests via GitHub here.

+https://github.com/r23/MyOOS-Dumper/ + + +

Financial Support

. +

You can use PayPal Me
. +https://www.paypal.com/paypalme/r23de?locale.x=de_DE

+ +

or via the QR code
. +Financial support for MyOOS [Dumper]

+ +Send money to the MyOOS project.
+ +

We hope you enjoy this project.

The MyOOS [Dumper] Team

+ +MyOOS [Dumper]
+ +

MyOOS [Dumper] Help

+ +

Download

+

You can always get the latest versions via GitHub
. +https://github.com/r23/MyOOS-Dumper/releases

+ + +

System requirement

. +

The script works on any server (Windows, Linux, ...)
+with PHP >= version 7.4 with GZip support, MySQL (version 4.1 or higher), JavaScript (must be enabled)

. +

Copy the mod folder from the MyOOS archive to a separate working folder.

. + +

Installation

. +The installation process is straightforward. +

From the MyOOS archive, copy the mod folder into any folder.
+Upload all the files from the mod folder to your web server. (For example, to the lowest level in [server web directory/]mod)
+... done!
+You can now call MyOOS [Dumper] in your web browser by "https://example.com/mod/",
+to complete the installation. Just follow the instructions.
+
Note:
If on your server the script is not allowed to create directories,
+you have to do this manually, because MyOOS [Dumper] stores the data ordered in +directories.
+The script aborts with an appropriate statement!
+After you have created the directories (according to the hint), it runs normally and without restrictions.
+ +

Perl script instructions

. +Most have a cgi-bin directory where perl can be run.
+This is usually accessible by browser via http://www.example.com/cgi-bin/.
+
+For this case, please perform the following steps:

. + +1. call the Backup page in MyOOS [Dumper] and click on "Backup Perl".
+2. copy the path that is behind entry in crondump.pl for $absolute_path_of_configdir:.
+3. open the file "crondump.pl" in the editor.
+4. enter the copied path there at absolute_path_of_configdir (no spaces).
+5. save crondump.pl .
+6. copy crondump.pl, as well as perltest.pl and simpletest.pl into the cgi-bin directory (ascii mode in FTP).
+7. give the files the permissions 755.
+7b. If the ending cgi is desired, change the ending of all 3 files from pl -> cgi (rename).
+8. call the configuration in MyOOS [Dumper].
+9. select the page Cronscript.
+10. change perl execution path to /cgi-bin/ .
+10b. If the scripts have .pl, change the file extension to .cgi .
+11.Save the configuration.

+ +Done, the scripts can now be called from the backup page.

. + +For those who can run Perl in all directories, the following steps will suffice:

. + +1. call in the MyOOS [Dumper] the page Backup.
+2. copy the path that is behind entry in crondump.pl for $absolute_path_of_configdir:.
+3. open the file "crondump.pl" in the editor.
+4. enter the copied path there at absolute_path_of_configdir (no spaces).
+5. save crondump.pl .
+6. give the files the permissions 755.
+6b. If the extension cgi is desired, change the extension of all 3 files from pl -> cgi (rename).
+(ev. 10b+11 from above)
+
+ +Windowsuser have to change the first line of all scripts, there is the path of Perl. Example:
+instead of: #!/usr/bin/perl -w
+now: #!C:_usr/bin/perl.exe -w
+ +

Operation

    . + +
    Menu
    . +In the selection list above you set the database.
    +All actions refer to the database set here. + +
    Home
    +Here you can learn about your system, the different installed versions and details about the +versions and details about the configured databases.
    +If you click on the database name, you will see a list of the tables with the number of entries +with the number of entries, the size and the last update date. + +
    Configuration
    . +Here you can edit your configuration, save it or restore the initial configuration. +restore. +

      +
    • Configured databases: the listing of configured databases. The active database is listed in bold.
    • +
    • Table prefix: here you can specify (for each database) a prefix. This is a filter that will take into account for dumps only the tables that start with this prefix (for example, all tables that start with "phpBB_"). If you want all tables in this database to be saved, just leave the field empty.
    • . +
    • GZip compression: Here you can enable compression. It is recommended to enable it, because the files will be much smaller after all and disk space is always scarce.
    • . +
    • Email with Dumpfile: If this option is enabled, an email with the dump as an attachment will be sent after the backup is complete (caution, compression should absolutely be on, otherwise the attachment will be too large and may not be sent!).
    • +
    • Email address: Recipient address for the email.
    • +
    • Sender of the email: this address appears as the sender in the email.
    • +
    • FTP Transfer: If this option is enabled, the backup file will be sent via FTP after the backup is completed.
    • +
    • FTP Server: The address of the FTP server (e.g. ftp.mybackups.com).
    • +
    • FTP Server Port: The port of the FTP server (usually 21).
    • +
    • FTP User: The username of the FTP account.
    • +
    • FTP Password: The password of the FTP account.
    • +
    • FTP Upload Folder: The directory where the backup file should go (upload permissions must exist!).
    • +
    • Automatic deletion of backups: If this option is enabled, older backups will be deleted automatically according to the following rules.
    • . +
    • Number of backup files: A value > 0 deletes all backup files except for the number specified here.
    • +
    • Language: here you specify the language for the interface.
    • +
    + +
    Administration
    . +This is where the actual actions are performed.
    +It will show you all the files in the backup directory. +For the actions "Restore" and "Delete" a file must be selected. +
      +
    • Restore: This will update the database with the selected backup file.
    • +
    • Delete: This lets you delete the selected backup file.
    • +
    • Start new backup: Here you start a new backup (dump) according to the parameters set in the configuration.
    • . +
    + +
    Log
    +Here you can see and delete the log entries. +
    Credits / Help
    +this page. +
diff --git a/msd/language/en/lang.php b/msd/language/en/lang.php new file mode 100644 index 0000000..4a81438 --- /dev/null +++ b/msd/language/en/lang.php @@ -0,0 +1,109 @@ +not available'; +$lang['L_VOM'] = 'from'; +$lang['L_MYSQLVARS'] = 'MySQL Variables'; +$lang['L_MYSQLSYS'] = 'MySQL Commands'; +$lang['L_STATUS'] = 'State'; +$lang['L_PROZESSE'] = 'Processes'; +$lang['L_INFO_NOVARS'] = 'no variables available'; +$lang['L_INHALT'] = 'Value'; +$lang['L_INFO_NOSTATUS'] = 'no status available'; +$lang['L_INFO_NOPROCESSES'] = 'no running processes'; +$lang['L_FM_FREESPACE'] = 'Free Space on Server'; +$lang['L_LOAD_DATABASE'] = 'Reload databases'; +$lang['L_HOME'] = 'Home'; +$lang['L_CONFIG'] = 'Configuration'; +$lang['L_DUMP'] = 'Backup'; +$lang['L_RESTORE'] = 'Restore'; +$lang['L_FILE_MANAGE'] = 'File Administration'; +$lang['L_LOG'] = 'Log'; +$lang['L_CHOOSE_DB'] = 'Select Database'; +$lang['L_CREDITS'] = 'Credits / Help'; +$lang['L_MULTI_PART'] = 'Multipart Backup'; +$lang['L_LOGFILENOTWRITABLE'] = "Can't write logfile !"; +$lang['L_SQL_ERROR1'] = 'Error in Query:'; +$lang['L_SQL_ERROR2'] = 'MySQL says:'; +$lang['L_UNKNOWN'] = 'unknown'; +$lang['L_UNKNOWN_NUMBER_OF_RECORDS'] = 'unknown'; +$lang['L_OK'] = 'OK'; +$lang['L_CRON_COMPLETELOG'] = 'Log complete output'; +$lang['L_NO'] = 'no'; +$lang['L_CREATE_DATABASE'] = 'Create new database'; +$lang['L_EXPORTFINISHED'] = 'Export finished.'; +$lang['L_SQL_BROWSER'] = 'SQL-Browser'; +$lang['L_SERVER'] = 'Server'; +$lang['L_MYSQL_CONNECTION_ENCODING'] = 'Standard encoding of MySQL-Server'; +$lang['L_TITLE_SHOW_DATA'] = 'Show data'; +$lang['L_PRIMARYKEY_CONFIRMDELETE'] = 'Really delete primary key?'; +$lang['L_SETPRIMARYKEYSFOR'] = 'Set new primary keys for table'; +$lang['L_PRIMARYKEY_FIELD'] = 'Primary key field'; +$lang['L_PRIMARYKEYS_SAVE'] = 'Save primary keys'; +$lang['L_CANCEL'] = 'Cancel'; +$lang['L_VISIT_HOMEPAGE'] = 'Visit Homepage'; +$lang['L_SECONDS'] = 'Seconds'; +$lang['L_BACKUPS'] = 'Backups'; +$lang['L_MINUTES'] = 'Minutes'; +$lang['L_PAGE_REFRESHS'] = 'Page refreshs'; +$lang['L_MINUTE'] = 'Minute'; +$lang['L_SETKEYSFOR'] = 'Set new indexes for table'; +$lang['L_KEY_CONFIRMDELETE'] = 'Really delete index?'; diff --git a/msd/language/en/lang_config_overview.php b/msd/language/en/lang_config_overview.php new file mode 100644 index 0000000..723956a --- /dev/null +++ b/msd/language/en/lang_config_overview.php @@ -0,0 +1,127 @@ +%s
into %s'; +$lang['L_FTP'] = 'FTP'; +$lang['L_SFTP_SEND_TO'] = 'to %s
into %s'; +$lang['L_SFTP'] = 'SFTP'; +$lang['L_EMAIL_CC'] = 'CC-Receiver'; +$lang['L_NAME'] = 'Name'; +$lang['L_CONFIRM_CONFIGFILE_DELETE'] = 'Really delete the configuration file %s?'; +$lang['L_ERROR_DELETING_CONFIGFILE'] = "Error: couldn't delete configuration file %s!"; +$lang['L_SUCCESS_DELETING_CONFIGFILE'] = 'The configuration file %s has successfully been deleted.'; +$lang['L_SUCCESS_CONFIGFILE_CREATED'] = 'Configuration file %s has successfully been created.'; +$lang['L_ERROR_CONFIGFILE_NAME'] = 'Filename "%s" contains invalid characters.'; +$lang['L_CREATE_CONFIGFILE'] = 'Create a new configuration file'; +$lang['L_ERROR_LOADING_CONFIGFILE'] = "Couldn't load configfile \"%s\"."; +$lang['L_BACKUP_DBS_PHP'] = 'DBs to backup (PHP)'; +$lang['L_BACKUP_DBS_PERL'] = 'DBs to backup (PERL)'; +$lang['L_CRON_COMMENT'] = 'Enter Comment'; +$lang['L_AUTODETECT'] = 'auto detect'; diff --git a/msd/language/en/lang_dump.php b/msd/language/en/lang_dump.php new file mode 100644 index 0000000..192db4c --- /dev/null +++ b/msd/language/en/lang_dump.php @@ -0,0 +1,58 @@ +%s` '; +$lang['L_DUMP_ENDERGEBNIS'] = 'The file contains %s tables with %s records.
'; +$lang['L_MAILERROR'] = 'Sending of email failed!'; +$lang['L_EMAILBODY_ATTACH'] = 'The Attachment contains the backup of your MySQL-Database.
Backup of Database `%s` +

Following File was created:

%s

Kind regards

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_MP_NOATTACH'] = 'A Multipart Backup was created.
The Backup files are not attached to this email!
Backup of Database `%s` +

Following Files were created:

%s +

Kind regards

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_MP_ATTACH'] = 'A Multipart Backup was created.
The Backup files are attached to separate emails.
Backup of Database `%s` +

Following Files were created:

%s

Kind regards

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_FOOTER'] = '`

Kind regards

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_TOOBIG'] = 'The Backup file exceeded the maximum size of %s and was not attached to this email.
Backup of Database `%s` +

Following File was created:

%s +

Kind regards

MyOOS [Dumper]
'; +$lang['L_EMAILBODY_NOATTACH'] = 'Files are not attached to this email!
Backup of Database `%s` +

Following File was created:

%s +

Kind regards

MyOOS [Dumper]
'; +$lang['L_EMAIL_ONLY_ATTACHMENT'] = ' ... attachment only.'; +$lang['L_TABLESELECTION'] = 'Table selection'; +$lang['L_SELECTALL'] = 'Select All'; +$lang['L_DESELECTALL'] = 'Deselect all'; +$lang['L_STARTDUMP'] = 'Start Backup'; +$lang['L_LASTBUFROM'] = 'last update from'; +$lang['L_NOT_SUPPORTED'] = "This backup doesn't support this function."; +$lang['L_MULTIDUMP'] = 'Multidump: Backup of %d Databases done.'; +$lang['L_FILESENDFTP'] = 'send file via FTP... please be patient. '; +$lang['L_FTPCONNERROR'] = 'FTP connection not established! Connection with '; +$lang['L_FTPCONNERROR1'] = ' as user '; +$lang['L_FTPCONNERROR2'] = ' not possible'; +$lang['L_FTPCONNERROR3'] = 'FTP Upload failed! '; +$lang['L_FTPCONNECTED1'] = 'Connected with '; +$lang['L_FTPCONNECTED2'] = ' on '; +$lang['L_FTPCONNECTED3'] = ' transfer successful'; +$lang['L_FILESENDSFTP'] = 'send file via SFTP... please be patient. '; +$lang['L_SFTPCONNERROR'] = 'SFTP connection not established! Connection with '; +$lang['L_NR_TABLES_SELECTED'] = '- with %s selected tables'; +$lang['L_NR_TABLES_OPTIMIZED'] = '%s tables have been optimized.'; +$lang['L_DUMP_ERRORS'] = '

%s errors occured: view

'; +$lang['L_FATAL_ERROR_DUMP'] = "Fatal error: the CREATE-Statement of table '%s' in database '%s' couldn't be read!"; diff --git a/msd/language/en/lang_filemanagement.php b/msd/language/en/lang_filemanagement.php new file mode 100644 index 0000000..da34242 --- /dev/null +++ b/msd/language/en/lang_filemanagement.php @@ -0,0 +1,79 @@ +%s"'; +$lang['L_DELETE_FILE_ERROR'] = 'Error deleting file "%s"!'; +$lang['L_FM_DUMP_HEADER'] = 'Backup'; +$lang['L_DOCRONBUTTON'] = 'Run the Perl Cron script'; +$lang['L_DOPERLTEST'] = 'Test Perl Modules'; +$lang['L_DOSIMPLETEST'] = 'Test Perl'; +$lang['L_PERLOUTPUT1'] = 'Entry in crondump.pl for absolute_path_of_configdir'; +$lang['L_PERLOUTPUT2'] = 'URL for the browser or for external Cron job'; +$lang['L_PERLOUTPUT3'] = 'Commandline in the Shell or for the Crontab'; +$lang['L_RESTORE_OF_TABLES'] = 'Choose tables to be restored'; +$lang['L_CONVERTER'] = 'Backup Converter'; +$lang['L_CONVERT_FILE'] = 'File to be converted'; +$lang['L_CONVERT_FILENAME'] = 'Name of destination file (without extension)'; +$lang['L_CONVERTING'] = 'Converting'; +$lang['L_CONVERT_FILEREAD'] = "Read file '%s'"; +$lang['L_CONVERT_FINISHED'] = "Conversion finished, '%s' was written successfully."; +$lang['L_NO_MOD_BACKUPFILE'] = 'Backups of other scripts'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Maximum filesize'; +$lang['L_MAX_UPLOAD_SIZE_INFO'] = 'If your Dumpfile is bigger than the above mentioned limit, you must upload it via FTP into the directory "work/backup". +After that you can choose it to begin a restore progress. '; +$lang['L_ENCODING'] = 'encoding'; +$lang['L_FM_CHOOSE_ENCODING'] = 'Choose encoding of backup file'; +$lang['L_CHOOSE_CHARSET'] = "MyOOS [Dumper] couldn't detect the encoding of the backup file automatically. +
You must choose the charset with which this backup was saved. +
If you discover any problems with some characters after restoring, you can repeat the backup-progress and then choose another character set. +
Good luck. ;)"; +$lang['L_DOWNLOAD_FILE'] = 'Download file'; +$lang['L_BACKUP_NOT_POSSIBLE'] = 'A backup of the system database `%s` is not possible!'; diff --git a/msd/language/en/lang_help.php b/msd/language/en/lang_help.php new file mode 100644 index 0000000..5fbcc1c --- /dev/null +++ b/msd/language/en/lang_help.php @@ -0,0 +1,37 @@ +Installation completed --> start MyOOS [Dumper]
'; +$lang['L_INSTALL_TOMENU'] = 'Back to main menu'; +$lang['L_INSTALLMENU'] = 'Main menu'; +$lang['L_STEP'] = 'Step'; +$lang['L_INSTALL'] = 'Installation'; +$lang['L_UNINSTALL'] = 'Uninstall'; +$lang['L_TOOLS'] = 'Tools'; +$lang['L_EDITCONF'] = 'Edit configuration'; +$lang['L_OSWEITER'] = 'Continue without saving'; +$lang['L_ERRORMAN'] = 'Error while saving the Configuration!
Please edit the File '; +$lang['L_MANUELL'] = 'manually'; +$lang['L_CREATEDIRS'] = 'Create Directories'; +$lang['L_INSTALL_CONTINUE'] = 'Continue with installation'; +$lang['L_CONNECTTOMYSQL'] = 'Connect to MySQL '; +$lang['L_DBPARAMETER'] = 'Database Parameters'; +$lang['L_CONFIGNOTWRITABLE'] = 'I cannot write to file "config.php". +Please use your FTP program and chmod this file to 0777.'; +$lang['L_DBCONNECTION'] = 'Database Connection'; +$lang['L_CONNECTIONERROR'] = 'Error: unable to connect.'; +$lang['L_CONNECTION_OK'] = 'Database connection was established.'; +$lang['L_SAVEANDCONTINUE'] = 'Save and continue installation'; +$lang['L_CONFBASIC'] = 'Basic Parameter'; +$lang['L_INSTALL_STEP2FINISHED'] = 'Database parameters were saved successfully.'; +$lang['L_INSTALL_STEP2_1'] = 'Continue installation with the default settings'; +$lang['L_LASTSTEP'] = 'Installation Finish'; +$lang['L_FTPMODE'] = 'Create necessary directories in safe-mode'; +$lang['L_IDOMANUAL'] = 'I create the directories myself'; +$lang['L_DOFROM'] = 'starting from'; +$lang['L_FTPMODE2'] = 'Create the dirs with FTP:'; +$lang['L_CONNECT'] = 'connect'; +$lang['L_DIRS_CREATED'] = 'The directories are created and correctly permissioned.'; +$lang['L_CONNECT_TO'] = 'connect to'; +$lang['L_CHANGEDIR'] = 'change to dir'; +$lang['L_CHANGEDIRERROR'] = 'change to dir was not possible'; +$lang['L_FTP_OK'] = 'FTP parameter are ok'; +$lang['L_CREATEDIRS2'] = 'Create directories'; +$lang['L_FTP_NOTCONNECTED'] = 'FTP connection not established!'; +$lang['L_CONNWITH'] = 'Connection with'; +$lang['L_ASUSER'] = 'as user'; +$lang['L_NOTPOSSIBLE'] = 'not possible'; +$lang['L_DIRCR1'] = 'create workdir'; +$lang['L_DIRCR2'] = 'create backupdir'; +$lang['L_DIRCR4'] = 'create logdir'; +$lang['L_DIRCR5'] = 'create configurationdir'; +$lang['L_INDIR'] = 'now in dir'; +$lang['L_CHECK_DIRS'] = 'Check my directories'; +$lang['L_DISABLEDFUNCTIONS'] = 'Disabled Functions'; +$lang['L_NOFTPPOSSIBLE'] = "You don't have FTP functions !"; +$lang['L_NOGZPOSSIBLE'] = "You don't have compression functions !"; +$lang['L_UI1'] = 'All working directories which can contain backups will be deleted.'; +$lang['L_UI2'] = 'Are you sure you want that?'; +$lang['L_UI3'] = 'no, cancel immediately'; +$lang['L_UI4'] = 'yes, please continue'; +$lang['L_UI5'] = 'delete working directories'; +$lang['L_UI6'] = 'all was deleted successfully.'; +$lang['L_UI7'] = 'Please delete the script directory'; +$lang['L_UI8'] = 'one level up'; +$lang['L_UI9'] = 'An error occured, deleting was not possible

Error with directory '; +$lang['L_IMPORT'] = 'Import Configuration'; +$lang['L_IMPORT3'] = 'Configuration was loaded ...'; +$lang['L_IMPORT4'] = 'Configuration was saved.'; +$lang['L_IMPORT5'] = 'Start MyOOS [Dumper]'; +$lang['L_IMPORT6'] = 'Installation Menu'; +$lang['L_IMPORT7'] = 'Upload configuration'; +$lang['L_IMPORT8'] = 'back to upload'; +$lang['L_IMPORT9'] = 'This is not a configuration backup !'; +$lang['L_IMPORT10'] = 'Configuration was uploaded successfully ...'; +$lang['L_IMPORT11'] = 'Error: There were problems writing sql_statements'; +$lang['L_IMPORT12'] = 'Error: There were problems writing config.php'; +$lang['L_INSTALL_HELP_PORT'] = '(empty = Default Port)'; +$lang['L_INSTALL_HELP_SOCKET'] = '(empty = Default Socket)'; +$lang['L_TRYAGAIN'] = 'Try again'; +$lang['L_SOCKET'] = 'Socket'; +$lang['L_PORT'] = 'Port'; +$lang['L_FOUND_DB'] = 'found db'; +$lang['L_FM_FILEUPLOAD'] = 'Upload file'; +$lang['L_PASS'] = 'Password'; +$lang['L_NO_DB_FOUND_INFO'] = 'The connection to the database was successfully established.
+Your userdata is valid and was accepted by the MySQL-Server.
+But MyOOS [Dumper] was not able to find any database.
+The automatic detection via script is blocked on some servers.
+You must enter your database name manually after the installation is finished. +Click on "configuration" "Connection Parameter - display" and enter the database name there.'; +$lang['L_ENTER_DB_INFO'] = 'First click the button "Connect to MySQL". Only if no database could be detected you need to provide a database name here.'; diff --git a/msd/language/en/lang_log.php b/msd/language/en/lang_log.php new file mode 100644 index 0000000..f142748 --- /dev/null +++ b/msd/language/en/lang_log.php @@ -0,0 +1,7 @@ +Please create the 2 files manually with the following content'; +$lang['L_HTACC_CHECK_ERROR'] = 'It could not be checked whether the program is protected!
The simulated external access could not be carried out.'; +$lang['L_HTACC_NOT_NEEDED'] = 'The program is protected by higher-level authorizations; local directory protection is not required.'; +$lang['L_HTACC_COMPLETE'] = 'The program is protected, the directory protection is complete.'; +$lang['L_HTACC_INCOMPLETE'] = 'The program is not protected, the directory protection is incomplete!'; +$lang['L_HTACC_PROPOSED'] = 'The program is not protected, directory protection is strongly recommended!'; +$lang['L_HTACC_EDIT'] = 'Edit .htaccess'; +$lang['L_HTACCESS18'] = 'Create .htaccess in '; +$lang['L_HTACCESS19'] = 'Reload '; +$lang['L_HTACCESS20'] = 'Execute script'; +$lang['L_HTACCESS21'] = 'Add handler'; +$lang['L_HTACCESS22'] = 'Make executable'; +$lang['L_HTACCESS23'] = 'Directory Listing'; +$lang['L_HTACCESS24'] = 'Error Document'; +$lang['L_HTACCESS25'] = 'Activate rewrite'; +$lang['L_HTACCESS26'] = 'Deny / Allow'; +$lang['L_HTACCESS27'] = 'Redirect'; +$lang['L_HTACCESS28'] = 'Error Log'; +$lang['L_HTACCESS29'] = 'More examples and documentation'; +$lang['L_HTACCESS30'] = 'Provider'; +$lang['L_HTACCESS31'] = 'General'; +$lang['L_HTACCESS32'] = "Attention! The .htaccess directly affects the browser's behavior.
With incorrect content, these pages may no longer be accessible."; +$lang['L_DISABLEDFUNCTIONS'] = 'Disabled Functions'; +$lang['L_NOGZPOSSIBLE'] = 'Because Zlib is not installed, you cannot use GZip-Functions!'; +$lang['L_DELETE_HTACCESS'] = 'Remove directory protection (delete .htaccess)'; +$lang['L_WRONG_RIGHTS'] = "The file or the directory '%s' is not writable for me.
+The rights (chmod) are not set properly or it has the wrong owner.
+Pleae set the correct attributes using your FTP program.
+The file or the directory needs to be set to %s.
"; +$lang['L_CANT_CREATE_DIR'] = "Couldn' t create dir '%s'. +Please create it using your FTP program."; +$lang['L_TABLE_TYPE'] = 'Type'; +$lang['L_CHECK'] = 'check'; +$lang['L_OS'] = 'Operating system'; +$lang['L_MOD_VERSION'] = 'MyOOS [Dumper] - Version'; +$lang['L_NEW_MOD_VERSION'] = 'New Version'; +$lang['L_NEW_MOD_VERSION_INFO'] = 'There is a new version of MyOOS [Dumper] available.'; +$lang['L_UPDATED_IMPORTANT'] = 'Important: Before updating, please backup your files.'; +$lang['L_UPDATE'] = 'Update now'; +$lang['L_MYSQL_VERSION'] = 'MySQL-Version'; +$lang['L_PHP_VERSION'] = 'PHP-Version'; +$lang['L_MAX_EXECUTION_TIME'] = 'Max execution time'; +$lang['L_PHP_EXTENSIONS'] = 'PHP-Extensions'; +$lang['L_MEMORY'] = 'Memory'; +$lang['L_FILE_MISSING'] = "couldn't find file"; +$lang['L_INSTALLING_UPDATES'] = 'Installing Updates'; +$lang['L_UPDATE_SUCCESSFUL'] = 'Update successful'; +$lang['L_UPDATE_FAILED'] = 'Update failed'; +$lang['L_UP_TO_DATE'] = 'Current Version is up to date'; diff --git a/msd/language/en/lang_restore.php b/msd/language/en/lang_restore.php new file mode 100644 index 0000000..598e9a8 --- /dev/null +++ b/msd/language/en/lang_restore.php @@ -0,0 +1,19 @@ +%d tables were created.'; +$lang['L_FILE_MISSING'] = "couldn't find file"; +$lang['L_RESTORE_DB'] = "Database '%s' on '%s'."; +$lang['L_RESTORE_COMPLETE'] = '%s tables created.'; +$lang['L_RESTORE_RUN1'] = '
Up to now %s of %s records were successfully added.'; +$lang['L_RESTORE_RUN2'] = "
Now the table '%s' is restoring.

"; +$lang['L_RESTORE_COMPLETE2'] = '%s records inserted.'; +$lang['L_RESTORE_TABLES_COMPLETED'] = 'Up to now %d of %d tables were created.'; +$lang['L_RESTORE_TOTAL_COMPLETE'] = '
Congratulations.

The restoration of the database is done.
All data from the Backup file was restored.

Everything is done. :-)'; +$lang['L_DB_SELECT_ERROR'] = '
Error:
Selection of database '; +$lang['L_DB_SELECT_ERROR2'] = ' failed!'; +$lang['L_FILE_OPEN_ERROR'] = 'Error: could not open file.'; +$lang['L_PROGRESS_OVER_ALL'] = 'Overall Progress'; +$lang['L_BACK_TO_OVERVIEW'] = 'Database Overview'; +$lang['L_RESTORE_RUN0'] = '
up to now %s records were successfully added.'; +$lang['L_UNKNOWN_SQLCOMMAND'] = 'unknown SQL-Command'; +$lang['L_NOTICES'] = 'Notices'; diff --git a/msd/language/en/lang_sql.php b/msd/language/en/lang_sql.php new file mode 100644 index 0000000..10582ad --- /dev/null +++ b/msd/language/en/lang_sql.php @@ -0,0 +1,190 @@ +%s lines exported'; +$lang['L_CSV_FIELDCOUNT_NOMATCH'] = "The count of fields doesn't match with that of the data to import (%d instead of %d)."; +$lang['L_CSV_FIELDSLINES'] = '%d fields recognized, totally %d lines'; +$lang['L_CSV_ERRORCREATETABLE'] = 'Error while creating table `%s` !'; +$lang['L_FM_UPLOADFILEREQUEST'] = 'please choose a file.'; +$lang['L_CSV_NODATA'] = 'No data found for import!'; +$lang['L_SQLLIB_GENERALFUNCTIONS'] = 'general functions'; +$lang['L_SQLLIB_RESETAUTO'] = 'reset auto-increment'; +$lang['L_SQLLIB_BOARDS'] = 'Boards'; +$lang['L_SQLLIB_DEACTIVATEBOARD'] = 'deactivate Board'; +$lang['L_SQLLIB_ACTIVATEBOARD'] = 'activate Board'; +$lang['L_SQL_NOTABLESSELECTED'] = 'No tables selected !'; +$lang['L_TOOLS'] = 'Tools'; +$lang['L_TOOLS_TOOLBOX'] = 'Select Database / Datebase functions / Import - Export '; +$lang['L_SQL_OPENFILE'] = 'Open SQL-File'; +$lang['L_SQL_OPENFILE_BUTTON'] = 'Upload'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Maximum file size'; +$lang['L_SQL_SEARCH'] = 'Search'; +$lang['L_SQL_SEARCHWORDS'] = 'Searchword(s)'; +$lang['L_START_SQL_SEARCH'] = 'start search'; +$lang['L_RESET_SEARCHWORDS'] = 'reset search words'; +$lang['L_SEARCH_OPTIONS'] = 'Search options'; +$lang['L_SEARCH_RESULTS'] = 'The search for "%s" in table "%s" brings the following results'; +$lang['L_SEARCH_NO_RESULTS'] = "The search for \"%s\" in table \"%s\" doesn't bring any hits!"; +$lang['L_NO_ENTRIES'] = "Table \"%s\" is empty and doesn't have any entry."; +$lang['L_SEARCH_ACCESS_KEYS'] = 'Browse: forward=ALT+V, backwards=ALT+C'; +$lang['L_SEARCH_OPTIONS_OR'] = 'a column must have one of the search words (OR-search)'; +$lang['L_SEARCH_OPTIONS_CONCAT'] = 'a row must contain all of the search words but they can be in any column (could take some time)'; +$lang['L_SEARCH_OPTIONS_AND'] = 'a column must contain all search words (AND-search)'; +$lang['L_SEARCH_IN_TABLE'] = 'Search in table'; +$lang['L_ERROR_NO_FIELDS'] = 'Search error: it could not be determined which fields the table "%s" has!'; +$lang['L_SQL_EDIT_TABLESTRUCTURE'] = 'Edit table structure'; +$lang['L_DEFAULT_CHARSET'] = 'Default character set'; +$lang['L_TITLE_KEY_PRIMARY'] = 'Primary key'; +$lang['L_TITLE_KEY_UNIQUE'] = 'Unique key'; +$lang['L_TITLE_INDEX'] = 'Index'; +$lang['L_TITLE_KEY_FULLTEXT'] = 'Fulltext key'; +$lang['L_TITLE_NOKEY'] = 'No key'; +$lang['L_TITLE_SEARCH'] = 'Search'; +$lang['L_TITLE_MYSQL_HELP'] = 'MySQl Documentation'; +$lang['L_TITLE_UPLOAD'] = 'Upload SQL file'; +$lang['L_PRIMARYKEY_DELETED'] = 'Primary key deleted'; +$lang['L_PRIMARYKEY_NOTFOUND'] = 'Primary key not found'; +$lang['L_PRIMARYKEYS_CHANGED'] = 'Primary keys changed'; +$lang['L_PRIMARYKEYS_CHANGINGERROR'] = 'Error changing primary keys'; +$lang['L_SQL_VIEW_COMPACT'] = 'View: compact'; +$lang['L_SQL_VIEW_STANDARD'] = 'View: standard'; +$lang['L_FIELDS_OF_TABLE'] = 'Fields of table'; +$lang['L_ENGINE'] = 'Engine'; +$lang['L_USERNAME'] = 'Username'; +$lang['L_PASSWORD'] = 'Password'; +$lang['L_PASSWORD_REPEAT'] = 'Password (repeat)'; +$lang['L_INFO_SIZE'] = 'Size'; +$lang['L_TABLE_TYPE'] = 'Type'; +$lang['L_KEY_DELETED'] = 'Index deleted'; +$lang['L_KEY_DELETEERROR'] = 'Error deleting index'; +$lang['L_KEY_ADDED'] = 'Index added'; +$lang['L_KEY_ADDERROR'] = 'Error adding index'; diff --git a/msd/language/es/help.html b/msd/language/es/help.html new file mode 100644 index 0000000..5e190a9 --- /dev/null +++ b/msd/language/es/help.html @@ -0,0 +1,149 @@ +
+

MyOOS [Dumper] basado en MySQLDumper 1.24.4

+ +

Acerca de este proyecto

+

MyOOS [Dumper] es una versión mejorada de MySQLDumper 1.24.4 (24 de enero de 2011). Esta evolución tiene en cuenta el desarrollo de PHP. +

MyOOS [Dumper] se preocupa principalmente por la estabilidad, la seguridad y el manejo. Pero también se incluye una atractiva plantilla que se puede editar y adaptar a tus propias necesidades.

+ + +

MyOOS [Dumper] es un programa de copias de seguridad para bases de datos MySQL, escrito en PHP y Perl. Con él, se pueden crear copias de seguridad de los datos (tienda, blog, etc.) y también restaurarlas si es necesario. Especialmente para los espacios web sin acceso al shell, MyOOS [Dumper] se ofrece como una alternativa sensata.

+ +

La idea de MySQLDumper fue de Daniel Schlichtholz. En 2004 abrió el foro MySQLDumper, donde los programadores escribieron nuevos scripts y ampliaron los existentes.

+ + +

Lista de deseos / futuras atracciones

. +

¿Tienes alguna sugerencia de mejora? No dude en ponerse en contacto con el equipo de desarrollo a través del foro https://foren.myoos.de/viewforum.php?f=41. + + +

Contribuir

+

Si quieres ayudarnos a mejorar el proyecto MyOOS, agradecemos tus pull requests a través de GitHub aquí.

+https://github.com/r23/MyOOS-Dumper/ + + +

Ayuda financiera

+

Puede utilizar PayPal Me
. +https://www.paypal.com/paypalme/r23de?locale.x=de_DE

+ +

o a través del código QR
. +Apoyo financiero a MyOOS [Dumper]

+ +Envía dinero al proyecto MyOOS.
+ +

Esperamos que disfrutes de este proyecto.

El equipo de MyOOS [Dumper] + +MyOOS [Dumper]
+ + +MyOOS [Dumper] + +

MyOOS [Dumper] Ayuda

+ +

Descarga

+

Siempre puedes obtener las últimas versiones en GitHub
. +https://github.com/r23/MyOOS-Dumper/releases

+ + +

Requisitos del sistema

+

El script funciona en cualquier servidor (Windows, Linux, ...)
+con PHP >= versión 7.4 con soporte GZip, MySQL (versión 4.1 o superior), JavaScript (debe estar habilitado).

+

Copie la carpeta del mod del archivo MyOOS en una carpeta de trabajo separada.

+ +

Instalación

+La instalación es sencilla. +

Desde el archivo MyOOS, copia la carpeta del mod en cualquier carpeta.
+Sube todos los archivos de la carpeta del mod a tu servidor web. (por ejemplo, al nivel más bajo en [directorio web del servidor/]mod)
+... Hecho!
+Ahora puede llamar a MyOOS [Dumper] en su navegador web yendo a "https://example.com/mod/"
. +para completar la instalación. Sólo tienes que seguir las instrucciones.
+
Nota:
Si en su servidor el script no tiene permiso para crear directorios,
+tendrá que hacerlo manualmente, ya que MyOOS [Dumper] almacena los datos en directorios. +directorios.
+El script termina con una declaración apropiada!
+Después de haber creado los directorios (según la pista), se ejecuta normalmente y sin restricciones.
+ +

Instrucciones del script de Perl

. +La mayoría tiene un directorio cgi-bin donde se puede ejecutar perl.
+Suele ser accesible por el navegador a través de http://www.example.com/cgi-bin/.
+
+En este caso, por favor, lleve a cabo los siguientes pasos:

1. + +1. llama a la página de Backup en MyOOS [Dumper] y haz clic en "Backup Perl".
+2. copie la ruta detrás de la entrada en crondump.pl para $absolute_path_of_configdir:.
+3. abrir el archivo "crondump.pl" en el editor.
+4. introduzca la ruta copiada allí en absolute_path_of_configdir (sin espacios).
+5. guardar crondump.pl.
+Copie crondump.pl, perltest.pl y simpletest.pl en el directorio cgi-bin (modo ascii en FTP). +7. dar a los archivos los permisos 755.
+7b. Si se desea la terminación cgi, cambiar la terminación de los 3 archivos de pl -> cgi (renombrar).
+Llama a la configuración en MyOOS [Dumper]. +9. seleccione la página Cronscript.
+10. cambiar la ruta de ejecución de Perl a /cgi-bin/ .
+10b. Si los scripts tienen .pl, cambie la extensión del archivo a .cgi.
+11. Guarde la configuración.

+ +Hecho, los scripts ahora pueden ser llamados desde la página de respaldo.

+ + +Para aquellos que pueden ejecutar Perl en todos los directorios, los siguientes pasos son suficientes:

1. + +1. llama a la página de copia de seguridad en MyOOS [Dumper].
+Copie la ruta detrás de la entrada en crondump.pl para $absolute_path_of_configdir:.
+Abra el archivo "crondump.pl" en el editor.
+4. introduzca allí la ruta copiada en absolute_path_of_configdir (sin espacios).
+5. guardar crondump.pl.
+6. Dé a los archivos los permisos 755.
+6b. Si se desea la terminación cgi, cambiar la terminación de los 3 archivos de pl -> cgi (renombrar).
+(ev. 10b+11 de arriba)
+
+ +Los usuarios de Windows tienen que cambiar la primera línea de todos los scripts, ahí está la ruta de Perl. Ejemplo:
+en lugar de: #! /usr/bin/perl -w
+ahora: #C:_usr/bin/perl.exe -w + +

Operación

    + +
    Menú
    . +En la lista de selección anterior se establece la base de datos.
    +Todas las acciones se refieren a la base de datos establecida aquí. + +
    Inicio
    +Aquí podrá conocer su sistema, las distintas versiones instaladas y los detalles de las bases de datos configuradas. +versiones instaladas y detalles sobre las bases de datos configuradas.
    +Si hace clic en el nombre de la base de datos, verá una lista de las tablas con el número de entradas +con el número de entradas, el tamaño y la última fecha de actualización. + +
    Configuración
    +Aquí puede editar su configuración, guardarla o restaurar la configuración inicial. +restaurar la configuración inicial. +

      +
    • Bases de datos configuradas: la lista de bases de datos configuradas. La base de datos activa aparece en negrita.
    • Por lo tanto, no es necesario que el usuario se sienta cómodo. +
    • Prefijo de la tabla: aquí se puede especificar un prefijo (para cada base de datos). Se trata de un filtro que sólo tiene en cuenta las tablas que empiezan por este prefijo al realizar el volcado (por ejemplo, todas las tablas que empiezan por "phpBB_"). Si quiere que se guarden todas las tablas de esta base de datos, simplemente deje el campo vacío.
    • . +
    • Compresión GZip: Aquí puedes activar la compresión. Se recomienda activarlo, ya que los archivos se hacen mucho más pequeños y el espacio de almacenamiento es siempre escaso. +
    • Correo electrónico con el archivo de volcado: Si esta opción está activada, se enviará un correo electrónico con el volcado como archivo adjunto una vez finalizada la copia de seguridad (atención, la compresión debe estar definitivamente activada, de lo contrario, el archivo adjunto será demasiado grande y podría no enviarse). +
    • Dirección de correo electrónico: Dirección del destinatario del correo electrónico.
    • . +
    • Remitente del correo electrónico: esta dirección aparece como remitente en el correo electrónico.
    • . +
    • Transferencia FTP: Si esta opción está activada, el archivo de copia de seguridad se enviará por FTP una vez finalizada la copia de seguridad.
    • . +
    • Servidor FTP: La dirección del servidor FTP (por ejemplo, ftp.mybackups.de).
    • . +
    • Puerto del servidor FTP: El puerto del servidor FTP (normalmente el 21).
    • . +
    • Usuario FTP: El nombre de usuario de la cuenta FTP.
    • Por lo tanto, no es necesario que el usuario se sienta cómodo. +
    • Contraseña FTP: La contraseña de la cuenta FTP.
    • Por lo tanto, no es necesario que el usuario se sienta cómodo. +
    • Carpeta de carga FTP: El directorio donde debe ir el archivo de copia de seguridad (¡los permisos de carga deben existir!).
    • . +
    • Borrado automático de copias de seguridad: Si esta opción está activada, las copias de seguridad más antiguas se borrarán automáticamente según las siguientes reglas.
    • . +
    • Número de archivos de copia de seguridad: Un valor > 0 elimina todos los archivos de copia de seguridad excepto el número especificado aquí.
    • +
    • Idioma: aquí se establece el idioma de la interfaz.
    • . + + +
      Administración
      +Aquí es donde se realizan las acciones reales.
      +Se muestran todos los archivos del directorio de la copia de seguridad. +Para las acciones "Restaurar" y "Borrar" debe seleccionarse un archivo. +
        +
      • Restaurar: Esto actualiza la base de datos con el archivo de copia de seguridad seleccionado.
      • . +
      • Borrar: Permite eliminar el archivo de copia de seguridad seleccionado.
      • . +
      • Iniciar nueva copia de seguridad: Aquí puede iniciar una nueva copia de seguridad (volcado) según los parámetros establecidos en la configuración. +
      + +
      Log
      +Aquí puede ver y eliminar las entradas del registro. +
      Créditos / Ayuda
      +esta página. \ No newline at end of file diff --git a/msd/language/es/lang.php b/msd/language/es/lang.php new file mode 100644 index 0000000..abd99db --- /dev/null +++ b/msd/language/es/lang.php @@ -0,0 +1,109 @@ +no disponible'; +$lang['L_VOM'] = 'de'; +$lang['L_MYSQLVARS'] = 'Variables de MySQL'; +$lang['L_MYSQLSYS'] = 'Datos de MySQL'; +$lang['L_STATUS'] = 'Estado'; +$lang['L_PROZESSE'] = 'Proceso'; +$lang['L_INFO_NOVARS'] = 'no hay variables disponibles'; +$lang['L_INHALT'] = 'Contenido'; +$lang['L_INFO_NOSTATUS'] = 'no hay estados disponibles'; +$lang['L_INFO_NOPROCESSES'] = 'no hay procesos corriendo'; +$lang['L_FM_FREESPACE'] = 'Espacio libre en el servidor'; +$lang['L_LOAD_DATABASE'] = 'Refrescar la lista de BdD'; +$lang['L_HOME'] = 'Inicio'; +$lang['L_CONFIG'] = 'Configuración'; +$lang['L_DUMP'] = 'Copia de seguridad'; +$lang['L_RESTORE'] = 'Restaurar'; +$lang['L_FILE_MANAGE'] = 'Archivos'; +$lang['L_LOG'] = 'Log'; +$lang['L_CHOOSE_DB'] = 'elegir base de datos'; +$lang['L_CREDITS'] = 'Créditos / Ayuda'; +$lang['L_MULTI_PART'] = 'Copia de seguridad en múltiples archivos'; +$lang['L_LOGFILENOTWRITABLE'] = 'No se puede escribir en el fichero de historial (log)!'; +$lang['L_SQL_ERROR1'] = 'Error de ejecución:'; +$lang['L_SQL_ERROR2'] = 'MySQL informa:'; +$lang['L_UNKNOWN'] = 'desconocido'; +$lang['L_UNKNOWN_NUMBER_OF_RECORDS'] = 'desconocido'; +$lang['L_OK'] = 'Ok'; +$lang['L_CRON_COMPLETELOG'] = 'Registrar todas las operaciones'; +$lang['L_NO'] = 'no'; +$lang['L_CREATE_DATABASE'] = 'crear nueva base de datos'; +$lang['L_EXPORTFINISHED'] = 'Exportación finalizada.'; +$lang['L_SQL_BROWSER'] = 'Navegador-SQL'; +$lang['L_SERVER'] = 'Servidor'; +$lang['L_MYSQL_CONNECTION_ENCODING'] = 'Codificación por defecto para MySQL-Server'; +$lang['L_TITLE_SHOW_DATA'] = 'Ver datos'; +$lang['L_PRIMARYKEY_CONFIRMDELETE'] = '¿Realmente desea eliminar la clave principal?'; +$lang['L_SETPRIMARYKEYSFOR'] = 'Establecer nueva clave principal para la tabla'; +$lang['L_PRIMARYKEY_FIELD'] = 'Campo de clave principal'; +$lang['L_PRIMARYKEYS_SAVE'] = 'Guardar las claves principales'; +$lang['L_CANCEL'] = 'Cancelar'; +$lang['L_VISIT_HOMEPAGE'] = 'Visite el sitio web'; +$lang['L_SECONDS'] = 'Segundos'; +$lang['L_BACKUPS'] = 'cantidad de copias de seguridad'; +$lang['L_MINUTES'] = 'Minutes'; +$lang['L_PAGE_REFRESHS'] = 'Page refreshs'; +$lang['L_MINUTE'] = 'Minute'; +$lang['L_SETKEYSFOR'] = 'Set new indexes for table'; +$lang['L_KEY_CONFIRMDELETE'] = 'Really delete index?'; diff --git a/msd/language/es/lang_config_overview.php b/msd/language/es/lang_config_overview.php new file mode 100644 index 0000000..c3e38d8 --- /dev/null +++ b/msd/language/es/lang_config_overview.php @@ -0,0 +1,128 @@ +%s
      en %s'; +$lang['L_FTP'] = 'FTP'; +$lang['L_SFTP_SEND_TO'] = 'para %s
      en %s'; +$lang['L_SFTP'] = 'SFTP'; +$lang['L_EMAIL_CC'] = 'CC-Destinatarios'; +$lang['L_NAME'] = 'Nombre'; +$lang['L_CONFIRM_CONFIGFILE_DELETE'] = '¿Está seguro de que desea borrar el archivo de configuración %s?'; +$lang['L_ERROR_DELETING_CONFIGFILE'] = '¡Error: el archivo de configuración %s no ha podido ser eliminado!'; +$lang['L_SUCCESS_DELETING_CONFIGFILE'] = 'El archivo de configuración %s ha sido eliminado.'; +$lang['L_SUCCESS_CONFIGFILE_CREATED'] = 'El archivo de configuración %s se ha creado correctamente.'; +$lang['L_ERROR_CONFIGFILE_NAME'] = 'El nombre del archivo "%s" contiene caracteres no válidos.'; +$lang['L_CREATE_CONFIGFILE'] = 'Crear un nuevo archivo de configuración'; +$lang['L_ERROR_LOADING_CONFIGFILE'] = 'No se pudo cargar el archivo de configuración "%s".'; +$lang['L_BACKUP_DBS_PHP'] = 'BDs a copiar (PHP)'; +$lang['L_BACKUP_DBS_PERL'] = 'BDs a copiar (PERL)'; +$lang['L_CRON_COMMENT'] = 'Enter Comment'; +$lang['L_AUTODETECT'] = 'Identificar automáticamente'; diff --git a/msd/language/es/lang_dump.php b/msd/language/es/lang_dump.php new file mode 100644 index 0000000..51cfc67 --- /dev/null +++ b/msd/language/es/lang_dump.php @@ -0,0 +1,57 @@ +%s` '; +$lang['L_DUMP_ENDERGEBNIS'] = '%s Tablas con un total de %s registros, han sido guardadas con éxito.
      '; +$lang['L_MAILERROR'] = 'Se ha producido un error al intentar enviar el email!'; +$lang['L_EMAILBODY_ATTACH'] = 'En el fichero adjunto encontrará la copia de seguridad de su base de datos MySQL.
      Copia de seguridad de la base de datos `%s` +

      Se ha creado el siguiente archivo:

      %s


      Saludos de

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_MP_NOATTACH'] = 'Se ha realizado un backup de archivos múltiples.
      Los archivos no se adjuntan a este email!
      Copia de seguridad de la base de datos `%s` +

      Los siguientes archivos han sido adjuntados:

      %s +


      Saludos de

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_MP_ATTACH'] = 'Se ha realizado un backup de archivos múltiples.
      Los archivos se adjuntan a emails separados!
      Copia de seguridad de la base de datos `%s` +

      Los siguientes archivos han sido adjuntados:

      %s


      Saludos de

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_FOOTER'] = '


      Saludos de

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_TOOBIG'] = 'La copia de seguridad ha sobrepasado el tamaño máximo de %s y por lo tanto no ha sido adjuntada.
      Copia de seguridad de la base de datos `%s` +

      Se ha creado el siguiente archivo:

      %s


      Saludos de

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_NOATTACH'] = 'No se adjunta el archivo de copia de seguridad.
      Copia de seguridad de la base de datos `%s` +

      Se ha creado el siguiente archivo:

      %s


      Saludos de

      MyOOS [Dumper]
      '; +$lang['L_EMAIL_ONLY_ATTACHMENT'] = ' ... solamente el fichero adjunto'; +$lang['L_TABLESELECTION'] = 'Elección de tablas'; +$lang['L_SELECTALL'] = 'seleccionar todas +'; +$lang['L_DESELECTALL'] = 'seleccionar todas'; +$lang['L_STARTDUMP'] = 'iniciar copia de seguridad'; +$lang['L_LASTBUFROM'] = 'última actualización el'; +$lang['L_NOT_SUPPORTED'] = 'Esta copia de seguridad no comprende esta función.'; +$lang['L_MULTIDUMP'] = 'Copia múltiple, copia de seguridad de %d bases de datos resalizada.'; +$lang['L_FILESENDFTP'] = 'envío del archivo vía FTP... tenga un poco de paciencia, por favor. '; +$lang['L_FTPCONNERROR'] = 'Conexión no establecida! Conectarse a '; +$lang['L_FTPCONNERROR1'] = ' con el usuario '; +$lang['L_FTPCONNERROR2'] = ' ha sido imposible'; +$lang['L_FTPCONNERROR3'] = 'El envío por FTP ha fallado! '; +$lang['L_FTPCONNECTED1'] = 'Conectado con '; +$lang['L_FTPCONNECTED2'] = ' en '; +$lang['L_FTPCONNECTED3'] = ' escritos'; +$lang['L_FILESENDSFTP'] = 'envío del archivo vía SFTP... tenga un poco de paciencia, por favor. '; +$lang['L_SFTPCONNERROR'] = 'Conexión no establecida! Conectarse a '; +$lang['L_NR_TABLES_SELECTED'] = '- con %s tablas seleccionadas'; +$lang['L_NR_TABLES_OPTIMIZED'] = '%s tablas optimizadas.'; +$lang['L_DUMP_ERRORS'] = '

      Ha(n) ocurrido %s error(es): visualizar

      '; +$lang['L_FATAL_ERROR_DUMP'] = "¡Error fatal: las instrucciones para crear la tabla '%s' en la base de datos '%s' no se pueden leer!"; diff --git a/msd/language/es/lang_filemanagement.php b/msd/language/es/lang_filemanagement.php new file mode 100644 index 0000000..61529f0 --- /dev/null +++ b/msd/language/es/lang_filemanagement.php @@ -0,0 +1,79 @@ +%s
      "'; +$lang['L_DELETE_FILE_ERROR'] = '¡No fue posible borrar el archivo "%s"!'; +$lang['L_FM_DUMP_HEADER'] = 'Copia de seguridad'; +$lang['L_DOCRONBUTTON'] = 'Ejecutar Cronscript Perl'; +$lang['L_DOPERLTEST'] = 'Comprobar Módulos Perl'; +$lang['L_DOSIMPLETEST'] = 'Comprobar Perl'; +$lang['L_PERLOUTPUT1'] = 'Línea a escribir en crondump.pl para absolute_path_of_configdir'; +$lang['L_PERLOUTPUT2'] = 'Ejecutar desde el navegador o desde un Cronjob externo al servidor'; +$lang['L_PERLOUTPUT3'] = 'Ejecutar desde Shell o como entrada en Crontab'; +$lang['L_RESTORE_OF_TABLES'] = 'Elija las tablas a restaurar'; +$lang['L_CONVERTER'] = 'Copia de seguridad-Conversor'; +$lang['L_CONVERT_FILE'] = 'archivo que se convertirá'; +$lang['L_CONVERT_FILENAME'] = 'Nombre del archivo de destino (sin extensión)'; +$lang['L_CONVERTING'] = 'La conversión'; +$lang['L_CONVERT_FILEREAD'] = "Leyendo el archivo '%s'"; +$lang['L_CONVERT_FINISHED'] = "Conversión finalizada: '%s' se ha guardado correctamente."; +$lang['L_NO_MOD_BACKUPFILE'] = 'Copias de seguridad de otros programas'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Tamaño máximo del fichero'; +$lang['L_MAX_UPLOAD_SIZE_INFO'] = 'Si el archivo de copia de seguridad es mayor que el límite fijado, entonces debe cargarlo a través de FTP en la carpeta "work/backup". +Después ese archivo se mostrará aquí, y podrá ser elegido para restaurar.'; +$lang['L_ENCODING'] = 'Codificación'; +$lang['L_FM_CHOOSE_ENCODING'] = 'Seleccione la codificación de la copia de seguridad'; +$lang['L_CHOOSE_CHARSET'] = 'MyOOS [Dumper] no pudo detectar la codificación de los archivos de la copia de seguridad de forma automática.
      +Usted debe elegir el conjunto de caracteres con el que se guardó la copia de seguridad.
      +Si usted descubre algún problema con algunos caracteres después de la restauración, puede repetir la restauración de la copia de seguridad con otro conjunto de caracteres.
      +Buena suerte. ;)'; +$lang['L_DOWNLOAD_FILE'] = 'Descargos ficheros'; +$lang['L_BACKUP_NOT_POSSIBLE'] = 'A backup of the system database `%s` is not possible!'; diff --git a/msd/language/es/lang_help.php b/msd/language/es/lang_help.php new file mode 100644 index 0000000..ed8178c --- /dev/null +++ b/msd/language/es/lang_help.php @@ -0,0 +1,36 @@ +La Instalación ha terminado --> MyOOS [Dumper]
      '; +$lang['L_INSTALL_TOMENU'] = 'al menú principal'; +$lang['L_INSTALLMENU'] = 'Menú principal'; +$lang['L_STEP'] = 'Paso'; +$lang['L_INSTALL'] = 'Instalación'; +$lang['L_UNINSTALL'] = 'Desinstalación'; +$lang['L_TOOLS'] = 'Herramientas'; +$lang['L_EDITCONF'] = 'modificar configuración'; +$lang['L_OSWEITER'] = 'seguir sin guardar'; +$lang['L_ERRORMAN'] = 'Error al escribir la configuración!
      Por favor, edite Vd. el fichero '; +$lang['L_MANUELL'] = 'manualmente'; +$lang['L_CREATEDIRS'] = 'Directorios creados'; +$lang['L_INSTALL_CONTINUE'] = 'seguir con la instalación'; +$lang['L_CONNECTTOMYSQL'] = ' conectarse a MySQL '; +$lang['L_DBPARAMETER'] = 'Parámetros de la base de datos'; +$lang['L_CONFIGNOTWRITABLE'] = 'No se ha podido modificar el fichero "config.php". +Por favor, utilice su programa de FTP y ejecute chmod de dicho fichero a 0777.'; +$lang['L_DBCONNECTION'] = 'Conexión a la base de datos'; +$lang['L_CONNECTIONERROR'] = 'Error: no se pudo realizar la conexión.'; +$lang['L_CONNECTION_OK'] = 'La conexión a la base de datos ha sido realizada con éxito.'; +$lang['L_SAVEANDCONTINUE'] = 'guardar y seguir con la instalación'; +$lang['L_CONFBASIC'] = 'Propiedades básicas'; +$lang['L_INSTALL_STEP2FINISHED'] = 'Los datos de acceso a la base de datos han sido guardados.'; +$lang['L_INSTALL_STEP2_1'] = 'seguir con propiedades estándar'; +$lang['L_LASTSTEP'] = 'Fin de la instalación'; +$lang['L_FTPMODE'] = 'Crear directorios por FTP (en modo seguro)'; +$lang['L_IDOMANUAL'] = 'Crearé los directorios manualmente'; +$lang['L_DOFROM'] = 'hacer desde'; +$lang['L_FTPMODE2'] = 'Creación de los directorios por FTP:'; +$lang['L_CONNECT'] = 'conectar'; +$lang['L_DIRS_CREATED'] = 'Los directorios han sido correctamente creados.'; +$lang['L_CONNECT_TO'] = 'conectar a'; +$lang['L_CHANGEDIR'] = 'cambiar al directorio'; +$lang['L_CHANGEDIRERROR'] = 'No ha sido posible realizar el cambio de directorio'; +$lang['L_FTP_OK'] = 'Los parámetros de FTP son correctos'; +$lang['L_CREATEDIRS2'] = 'Crear directorios'; +$lang['L_FTP_NOTCONNECTED'] = 'Conexión por FTP no realizada!'; +$lang['L_CONNWITH'] = 'Conectar con'; +$lang['L_ASUSER'] = 'como usuario'; +$lang['L_NOTPOSSIBLE'] = 'imposible'; +$lang['L_DIRCR1'] = 'cree el directorio de trabajo '; +$lang['L_DIRCR2'] = 'cree el directorio de copias de seguridad '; +$lang['L_DIRCR4'] = 'cree el directorio de informes '; +$lang['L_DIRCR5'] = 'cree el directorio de configuración '; +$lang['L_INDIR'] = 'está en el directorio'; +$lang['L_CHECK_DIRS'] = 'comprobar'; +$lang['L_DISABLEDFUNCTIONS'] = 'Funciones deshabilitadas'; +$lang['L_NOFTPPOSSIBLE'] = 'Las funciones de FTP no están disponibles!'; +$lang['L_NOGZPOSSIBLE'] = 'Las funciones de compressión no están disponibles!'; +$lang['L_UI1'] = 'Se van a eliminar todos los directorios de trabajo, incluídos aquellos que contengan copias de seguridad.'; +$lang['L_UI2'] = 'Está seguro de que desea realizar la operación ?'; +$lang['L_UI3'] = 'no, abortar inmediatamente'; +$lang['L_UI4'] = 'si, deseo continuar'; +$lang['L_UI5'] = 'eliminar directorio de trabajo'; +$lang['L_UI6'] = 'todo ha sido eliminado satisfactoriamente.'; +$lang['L_UI7'] = 'Por favor, elimine el directorio de los scripts'; +$lang['L_UI8'] = 'subir un directorio'; +$lang['L_UI9'] = 'Se ha producido un error, no ha sido posible eliminarlo

      Error en el directorio '; +$lang['L_IMPORT'] = 'importar configuración'; +$lang['L_IMPORT3'] = 'La configuración ha sido cargada ...'; +$lang['L_IMPORT4'] = 'La configuración ha sido guardada.'; +$lang['L_IMPORT5'] = 'Iniciar MyOOS [Dumper]'; +$lang['L_IMPORT6'] = 'Menú de instalación'; +$lang['L_IMPORT7'] = 'subir configuración'; +$lang['L_IMPORT8'] = 'volver a subir'; +$lang['L_IMPORT9'] = 'Este no es ningun fichero de configuración !'; +$lang['L_IMPORT10'] = 'La configuración ha sido cargada con éxito ...'; +$lang['L_IMPORT11'] = 'ERROR: Ha habido problemas al guardar sql_statements'; +$lang['L_IMPORT12'] = 'ERROR: Ha habido problemas al guardar config.php'; +$lang['L_INSTALL_HELP_PORT'] = '(vacío = Puerto estándar)'; +$lang['L_INSTALL_HELP_SOCKET'] = '(vacío = Socket estándar)'; +$lang['L_TRYAGAIN'] = 'intentar nuevamente'; +$lang['L_SOCKET'] = 'Socket'; +$lang['L_PORT'] = 'Puerto'; +$lang['L_FOUND_DB'] = 'Encontrada BdD:'; +$lang['L_FM_FILEUPLOAD'] = 'Subir archivo'; +$lang['L_PASS'] = 'Password'; +$lang['L_NO_DB_FOUND_INFO'] = 'The connection to the database was successfully established.
      +Your userdata is valid and was accepted by the MySQL-Server.
      +But MyOOS [Dumper] was not able to find any database.
      +The automatic detection via script is blocked on some server.
      +You must enter your databasename manually after the installation is finished. +Click on "configuration" "Connection Parameter - display" and enter the databasename there.'; +$lang['L_ENTER_DB_INFO'] = 'First click the button "Connect to MySQL". Only if no database could be detected you need to provide a database name here.'; diff --git a/msd/language/es/lang_log.php b/msd/language/es/lang_log.php new file mode 100644 index 0000000..a8627b2 --- /dev/null +++ b/msd/language/es/lang_log.php @@ -0,0 +1,7 @@ +Por favor, coloque en él el siguiente archivo, con el contenido especificado'; +$lang['L_HTACC_CHECK_ERROR'] = 'It could not be checked whether the program is protected!
      The simulated external access could not be carried out.'; +$lang['L_HTACC_NOT_NEEDED'] = 'The program is protected by higher-level authorizations; local directory protection is not required.'; +$lang['L_HTACC_COMPLETE'] = 'The program is protected, the directory protection is complete.'; +$lang['L_HTACC_INCOMPLETE'] = 'The program is not protected, the directory protection is incomplete!'; +$lang['L_HTACC_PROPOSED'] = 'The program is not protected, directory protection is strongly recommended!'; +$lang['L_HTACC_EDIT'] = 'editar .htaccess'; +$lang['L_HTACCESS18'] = 'crear .htaccess en '; +$lang['L_HTACCESS19'] = 'cargar de nuevo '; +$lang['L_HTACCESS20'] = 'Ejecutar script'; +$lang['L_HTACCESS21'] = 'Escriba el proveedor'; +$lang['L_HTACCESS22'] = 'Permitir ejecución'; +$lang['L_HTACCESS23'] = 'Listado de directorios'; +$lang['L_HTACCESS24'] = 'Documentos de error'; +$lang['L_HTACCESS25'] = 'Activar la reescritura'; +$lang['L_HTACCESS26'] = 'Denegar / Permitir'; +$lang['L_HTACCESS27'] = 'Redirecccionar'; +$lang['L_HTACCESS28'] = 'Historial de errores'; +$lang['L_HTACCESS29'] = 'otros ejemplos y documentación'; +$lang['L_HTACCESS30'] = 'Proveedor'; +$lang['L_HTACCESS31'] = 'conjunto'; +$lang['L_HTACCESS32'] = 'Alerta! El fichero .htaccess influye directamente el comportamiento de los navegadores.
      Si lo crea de forma incorrecta, estas páginas no serán accesibles.'; +$lang['L_DISABLEDFUNCTIONS'] = 'Funciones deshabilitadas'; +$lang['L_NOGZPOSSIBLE'] = 'Dado que Zlib no está instalado, no puede usar las funciones de compresión GZip!'; +$lang['L_DELETE_HTACCESS'] = 'Remove directory protection (delete .htaccess)'; +$lang['L_WRONG_RIGHTS'] = "El archivo o directorio '%s' no tiene permisos de escritura para mi.
      +Los permisos (chmod) están mal configurados o el propietario no es correcto.
      +Por favor, compruebe los atributos utilizando su software de FTP.
      +El archivo o directorio debe ser configurado a %s."; +$lang['L_CANT_CREATE_DIR'] = "No se puede crear el directorio '%s'. +Cree este directorio manualmente utilizando un programa de FTP."; +$lang['L_TABLE_TYPE'] = 'Tipo'; +$lang['L_CHECK'] = 'comprobar'; +$lang['L_OS'] = 'Operating system'; +$lang['L_MOD_VERSION'] = 'MyOOS [Dumper]-Versión'; +$lang['L_NEW_MOD_VERSION'] = 'Nueva versión'; +$lang['L_NEW_MOD_VERSION_INFO'] = 'Hay una nueva versión de MyOOS [Dumper] disponible'; +$lang['L_UPDATED_IMPORTANT'] = 'Importante: Antes de actualizar, haga una copia de seguridad de sus archivos'; +$lang['L_UPDATE'] = 'Actualice ahora'; +$lang['L_MYSQL_VERSION'] = 'MySQL-Versión'; +$lang['L_PHP_VERSION'] = 'PHP-Versión'; +$lang['L_MAX_EXECUTION_TIME'] = 'El máximo de ejecución'; +$lang['L_PHP_EXTENSIONS'] = 'Extensiones de PHP'; +$lang['L_MEMORY'] = 'Memoria'; +$lang['L_FILE_MISSING'] = 'no se encuentra el fichero'; +$lang['L_INSTALLING_UPDATES'] = 'Instalando actualizaciones'; +$lang['L_UPDATE_SUCCESSFUL'] = 'Actualización exitosa'; +$lang['L_UPDATE_FAILED'] = 'Actualización fallida'; +$lang['L_UP_TO_DATE'] = 'La versión actual está actualizada'; diff --git a/msd/language/es/lang_restore.php b/msd/language/es/lang_restore.php new file mode 100644 index 0000000..85e4e1a --- /dev/null +++ b/msd/language/es/lang_restore.php @@ -0,0 +1,19 @@ +%d tablas.'; +$lang['L_FILE_MISSING'] = 'no se encuentra el fichero'; +$lang['L_RESTORE_DB'] = "la base de datos '%s' en '%s'."; +$lang['L_RESTORE_COMPLETE'] = '%s Las tablas han sido importadas.'; +$lang['L_RESTORE_RUN1'] = '
      Hasta ahora se han importado %s de %s registros'; +$lang['L_RESTORE_RUN2'] = "
      Se está llenando de datos la tabla '%s'.

      "; +$lang['L_RESTORE_COMPLETE2'] = '%s registros insertados.'; +$lang['L_RESTORE_TABLES_COMPLETED'] = 'Hasta el momento, se han recuperado %d de %d tablas.'; +$lang['L_RESTORE_TOTAL_COMPLETE'] = '
      Felicidades.

      La base de datos ha sido completamente restaurada.
      Todos los datos de la copia de seguridad han sido importados con éxito.

      He terminado. :-)'; +$lang['L_DB_SELECT_ERROR'] = "
      Error:
      La selección de la base de datos '"; +$lang['L_DB_SELECT_ERROR2'] = "' ha fallado!"; +$lang['L_FILE_OPEN_ERROR'] = 'Error: no he podido abrir el fichero.'; +$lang['L_PROGRESS_OVER_ALL'] = 'Progreso total'; +$lang['L_BACK_TO_OVERVIEW'] = 'vista de base de datos'; +$lang['L_RESTORE_RUN0'] = 'Hasta el momento, se han recuperado %s de tablas.'; +$lang['L_UNKNOWN_SQLCOMMAND'] = 'comando SQL desconocido'; +$lang['L_NOTICES'] = 'Avisos'; diff --git a/msd/language/es/lang_sql.php b/msd/language/es/lang_sql.php new file mode 100644 index 0000000..70cea22 --- /dev/null +++ b/msd/language/es/lang_sql.php @@ -0,0 +1,190 @@ +%s
      líneas exportadas'; +$lang['L_CSV_FIELDCOUNT_NOMATCH'] = 'El número de campos no coincide con el de los datos a importar (%d en vez de %d).'; +$lang['L_CSV_FIELDSLINES'] = '%d campos reconocidos, totalizando %d líneas'; +$lang['L_CSV_ERRORCREATETABLE'] = '¡Error al crear la tabla `%s`!'; +$lang['L_FM_UPLOADFILEREQUEST'] = 'Por favor, elija un archivo.'; +$lang['L_CSV_NODATA'] = '¡No se han encontrado datos que importar!'; +$lang['L_SQLLIB_GENERALFUNCTIONS'] = 'funciones generales'; +$lang['L_SQLLIB_RESETAUTO'] = 'reinicializar autoincremento'; +$lang['L_SQLLIB_BOARDS'] = 'Foros'; +$lang['L_SQLLIB_DEACTIVATEBOARD'] = 'desactivar foro'; +$lang['L_SQLLIB_ACTIVATEBOARD'] = 'activar foro'; +$lang['L_SQL_NOTABLESSELECTED'] = '¡No se han seleccionado tablas!'; +$lang['L_TOOLS'] = 'Herramientas'; +$lang['L_TOOLS_TOOLBOX'] = 'Elección de base de datos / Funciones de base de datos / Im- y Exportar '; +$lang['L_SQL_OPENFILE'] = 'Abrir archivo SQL'; +$lang['L_SQL_OPENFILE_BUTTON'] = 'Subir'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Tamaño máximo del fichero'; +$lang['L_SQL_SEARCH'] = 'Búsqueda'; +$lang['L_SQL_SEARCHWORDS'] = 'Palabra(s) de búsqueda'; +$lang['L_START_SQL_SEARCH'] = 'Iniciar búsqueda'; +$lang['L_RESET_SEARCHWORDS'] = 'Reinicializar criterios de búsqueda'; +$lang['L_SEARCH_OPTIONS'] = 'Opciones de búsqueda'; +$lang['L_SEARCH_RESULTS'] = 'La búsqueda para "%s" en la tabla "%s" produjo los siguientes resultados'; +$lang['L_SEARCH_NO_RESULTS'] = '¡La búsqueda para "%s" en la tabla "%s" no produjo ningún resultado!'; +$lang['L_NO_ENTRIES'] = 'La tabla "%s" está vacía y no contiene ninguna entrada.'; +$lang['L_SEARCH_ACCESS_KEYS'] = 'Navegar: Adelante=ALT+V, Atrás=ALT+C'; +$lang['L_SEARCH_OPTIONS_OR'] = 'Una columna debe contener al menos un criterio de búsqueda (O-Búsqueda)'; +$lang['L_SEARCH_OPTIONS_CONCAT'] = 'Una línea debe contener todos los términos de búsqueda, pero estos puede ser en cualquiera de las columnas (¡podría tardar!)'; +$lang['L_SEARCH_OPTIONS_AND'] = 'una columna debe contener todos los términos de búsqueda (Y-Búsqueda)'; +$lang['L_SEARCH_IN_TABLE'] = 'Buscar en la tabla'; +$lang['L_ERROR_NO_FIELDS'] = 'Search error: it could not be determined which fields the table "%s" has!'; +$lang['L_SQL_EDIT_TABLESTRUCTURE'] = 'Modificar la estructura de la tabla'; +$lang['L_DEFAULT_CHARSET'] = 'Conjunto de caracteres por defecto'; +$lang['L_TITLE_KEY_PRIMARY'] = 'Clave principal'; +$lang['L_TITLE_KEY_UNIQUE'] = 'Clave única'; +$lang['L_TITLE_INDEX'] = 'Índice'; +$lang['L_TITLE_KEY_FULLTEXT'] = 'Clave texto completo'; +$lang['L_TITLE_NOKEY'] = 'No hay clave'; +$lang['L_TITLE_SEARCH'] = 'Búsqueda'; +$lang['L_TITLE_MYSQL_HELP'] = 'Documentación de MySQL'; +$lang['L_TITLE_UPLOAD'] = 'Subir archivo SQL'; +$lang['L_PRIMARYKEY_DELETED'] = 'Clave principal eliminada'; +$lang['L_PRIMARYKEY_NOTFOUND'] = 'Clave principal no encontrada'; +$lang['L_PRIMARYKEYS_CHANGED'] = 'Clave principal cambiada'; +$lang['L_PRIMARYKEYS_CHANGINGERROR'] = 'Error al cambiar la clave principal'; +$lang['L_SQL_VIEW_COMPACT'] = 'Ver: Compacto'; +$lang['L_SQL_VIEW_STANDARD'] = 'Ver: Normal'; +$lang['L_FIELDS_OF_TABLE'] = 'Campos de la tabla'; +$lang['L_ENGINE'] = 'Máquina'; +$lang['L_USERNAME'] = 'Nombre de usuario'; +$lang['L_PASSWORD'] = 'Contraseña'; +$lang['L_PASSWORD_REPEAT'] = 'Contraseña (repetición)'; +$lang['L_INFO_SIZE'] = 'Tamaño'; +$lang['L_TABLE_TYPE'] = 'Tipo'; +$lang['L_KEY_DELETED'] = 'Index deleted'; +$lang['L_KEY_DELETEERROR'] = 'Error deleting index'; +$lang['L_KEY_ADDED'] = 'Index added'; +$lang['L_KEY_ADDERROR'] = 'Error adding index'; diff --git a/msd/language/flags/ar.gif b/msd/language/flags/ar.gif new file mode 100644 index 0000000000000000000000000000000000000000..0f97d30a59e1190c8316e37785776a0d726ba716 GIT binary patch literal 226 zcmZ?wbhEHbG-8ls*v!E29|&g7JOc#A#%CCSh+*c;nQ3VZX$(LX5TgUdpDbV@9S{jJ zgMp>L;Djf;rRM9iyZ?8(_M~(+X58vbtzKntYTFa(`*(ceEB3!Mnf;!vgtyl9^2X^Z zk}MZilz6R;V)b=WWOz}v|G|a|o)dW|Uu?N?CUp1ZH`{Au=HK4?b+_&M>FJ!zY)#EA zt!?ccZ1o&my?y-?CQh2n)jehUj7igG&Y3ZL?t+Q)7cQAEcy$Mb|U0T)RM! G!5RShhFk6c literal 0 HcmV?d00001 diff --git a/msd/language/flags/ch.gif b/msd/language/flags/ch.gif new file mode 100644 index 0000000000000000000000000000000000000000..672c9769dd6033a658bfb52bc68ab7cf666f37fc GIT binary patch literal 1765 zcmW+#c~Fx_6yJgZ)u`0WhzAH7uR*IF*CqsFs6k8Z4C;*5Rtt#2tmgm$JdjHSYb}yb zL5o|!3oRaqN7O+P6_i6=f+M1UfglJbD9Yi%A>F<&``?@Q-tT(f=ezhv59QjqXd?QI z5EmcgoN`V$$DAXvVvIA&8Q~0bhQuaioKj8+r}=`#t37WF(lGLMJc0%QOqb3RX~$aMhGLA5hRL& zSxgxw3}c27XoOXUC_{uH%n-1GY5)V#pb}(34~PL9paBb(k!VP);SowPrAYJ#v``U3 z38n;zc0mO+F`*b!jKDpJf?0|PMVKP62YG-@*oHX3KZpQ_pc=qHG^m78&;i7N4bXsv z#N@;_)bKDNNDLDE0WDM*6O15NunQ`nNf0Ioa)hKI0MG^F5Hz@jC?OA!3EL0{Xad9F z5L5#gh=xs&1v-EjumKv0ClaTj4K+Lj7lRMBRM6!w&Z(`LU0$ZWsbAT&Bc;E@BYKPk09b)&$-r=Q#q(wTWLh4jA z-sPs#*fv+UZrPNE$+DWMGfZ=E!bI1L-_ExDvhvAr#|Lgx3*Dw1ojmQmN>kvWwzQd_ zHM}UPd1S7xFkZV5>tGZ3jih2vV)g{5ON$rkZEf`uN%5$WXQZR^+}!GXH1~WL)y2ik z0&XP!3&??^M+a{_HykUDcaTmCDW84fPQ2YY{Sy~`$p}zwKVvH0 zocrsnmnG3Y!haKb&o3>xyr61j9yMB?cI~>V+xhv0;&G!czoY+JR?+kJTZ@<8mTNsC zzFOuT*l_b*z}ALSDZFQ8c3)7;r_OelQM&$u@aEd0NPD#PR(wlM-0+{XTq9a*v}IM1 zmaa(u2&3EILA!5nd~Jw(a=JP-esI$C#??lLKab8I7(3%cR7>*pAkV=sWR3Un#4#%Z zT*fQ16~n;lu<$|KL)wO9dzm79=f^+OO#ab1+P0^98&yn+ZC&ih zXBqWgF%OqldG=4KCz)ZIXoKpvXB%_p_eI?kjwa;)Fyn;A(_!4Pz1wmG?Y^B!BeQgd z;H8SRm9*t_poU*B$;;Gys=bgfdiAIs{cm$NC~`~2O+9tDJ-zC1MgDR@75h#(Q=br; zQ6GG+CV0uooOQ$VE(!0FbZ7dU3(F3FdDSWSn;*93{wox>?keo|IFOWF`z*z9wmB*$ z>}2^nX{dUjwT5__j>xL=*4X7{C_LNEsd973`HrHqZIKt{Wq-U0E9f_-RF<|oON3*^ zYxN@(vMo7tJ>8$DbQGB-o4e%o8`AyCCbXpcRl23H^(xDeTd9xT!;(gp)i$yo51}n5 zqH-)R)D}3JG7ZHJ<=F7RJyX*4*c`N6ua>7@NM!*+_6YSrQ+icpzj4;4ikYg!?;Ksz zQ!5)?Gar7@Rw~}-ji~MkslHY#f1M;4>Wr~N?$;%=xB8Y|zpEyn^9?&%l3S#5Q|8{n zf@Z-x^io5oYLcKdJFiumVtqz@G+46qcIr#A+kMTM!}E2L@Zzbqj>|@6d%g8N6d2jD zIV4Hh?EB&Nr#4@+?BD{aMOq{;-!Axd*PjXYQX9)dd+Jkqj;6k;NziXohj<6Ze{Omd zhWGW%^Pb;txu_lJXb*PoAD^qXbm$UN2JDpGdjD`!9CZxz{aE8*Sjko#{6@3Hqh zifFvmY4_^rwg^eTP22^)&V6^|=kcpM^r$bxhbwMcM0aH2q6LDtbJI56TOqx+=kZ1&Va2-cqWAv~EHFsZmg+xoJMUzU)%*)DGCkHtt>3()?DX5J{SOR$z4T_E Wf3cZ1&Va2-cqWAv~EHFsZmg+xoJMUzU)%*)DGCkHtt>3()?DX5J{SOR$z4T_E Wf3chlRb zg?^sG6-0^vEPMbGYb9EkhoZ(AP?CI@zW^e25=@T(I)w^FivT8g4@r#x8*%^t|NH#? z{Qds$^!WVz{UTSE`uqJVUYotm;2BDd`uhA!b*TbCh?%U+U4XC}MT%pGweCmr@GX2mc8BP?-fanuff><{{KW|o)bffh^EK$_W50dv2~TcC|sIghO~B( zyEtf}{QdoNmA$ja+*p9Gu*BJlr^vO&-62hoIbxliwa~=T;>yGBauj{pDvA^8LW004ggEC2ui z03HAj000O7fFTxQa7AVV0s&-OGjR-Ykdcy+HwtAK6%_^xD_3n71{M!&C3dE#sHtuN z4Gj}9IcP`@86yia1PFAyyuH0xQVdX=gPXIW1I6 zK1T`^e&y!p=N}RU1pr1~U{g$@{W%FvYK=D zd9Ni^XY-VIe~@GStJtrecf)qy!tx)%8kJRXtp#=UjSV3k%0<;8&H5@+rq*^$nyKBJ UHfz?L#CbCpELo~3$iQF?0K71AF#rGn literal 0 HcmV?d00001 diff --git a/msd/language/flags/es.gif b/msd/language/flags/es.gif new file mode 100644 index 0000000000000000000000000000000000000000..1b4643a80aff02d9f98594fec3400f442ea8aa35 GIT binary patch literal 141 zcmZ?wbhEHbG-8lr*vtR||AFW~5D^7*KsrEr9ax+UPI#`~Yw`MQwOE2iUrOe@#mm>_ zyk^VLIsYj8e#N@?HtzoqEGY2MSyXjmNzut1tN8(Q3%%Axt-rBGOyX_T{s$janHa1A D90O2Z literal 0 HcmV?d00001 diff --git a/msd/language/flags/fr.gif b/msd/language/flags/fr.gif new file mode 100644 index 0000000000000000000000000000000000000000..db75fc710d225daad599ec1db7d305e333bcd897 GIT binary patch literal 140 zcmZ?wbh9u|lw%NL*v!Dd!Laoh1Ht2f&XIHXEXcVM; z2z?YB@%?N8P_8NILLoJh!t7{&QsTH97mP4hn% I!NOn-06|etiU0rr literal 0 HcmV?d00001 diff --git a/msd/language/flags/index.htm b/msd/language/flags/index.htm new file mode 100644 index 0000000..e0cc5f3 --- /dev/null +++ b/msd/language/flags/index.htm @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/msd/language/flags/it.gif b/msd/language/flags/it.gif new file mode 100644 index 0000000000000000000000000000000000000000..fc5a2cc591e922016057996dddb786255e73d653 GIT binary patch literal 140 zcmZ?wbh9u|lw%NL*v!Dd(8BPaf#E;VKnJLW0i>6K#ZlmdXX{>z7iU+pM`#qJdoO|3j;{J|ld^e=;b?r~qI~l#N4k_xUsbvil#MT|WN$E4%j52Xpg( zpFZ)gc)+mW?t2y055NBMFMsgC)Qn-#z4scL|NsAIAOsYDvVfK7fJl&?46LaS7WSlM zYB{W8ayQsppmRQ7;G%^btKa+o2NLuRIwbl}EV*Q%p)>EoijrSC0qbt8DLehPYJY2pMSBX>TKTb`wv+dtN~!BJ)Zyo literal 0 HcmV?d00001 diff --git a/msd/language/flags/pl.gif b/msd/language/flags/pl.gif new file mode 100644 index 0000000000000000000000000000000000000000..def82d6f48a6d7aa23764e8e20a59a3d4e356352 GIT binary patch literal 1121 zcmV-n1fKgxNk%w1VLSjQ0M!5h{4hr8|NZ~^?fWz}{WU4u|NN|tc=7-K*NTk({_+1R zC;#*Q+IfVzjg991>aR*s`|0fVY=8gi_{~F5|Ni*@{QcBwbe$q8|KIKZ`2YVbIkHz_ z{p;WN|NpJ0ue54ykdK1zEF5$=E&lWJ@&EF9rj!0cHOe_jyf?|G~!hH#Gk-F#a?&|1mcIGBp1) zGynhpA^8LV00000EC2ui06YLE000L6z@KnPEdD4IiR9oobR>&Pi$ba<5}Z*Wa5yX$ zg~GRMESt*|nzDOnEhM9(gX9o!kd8pX3l=ZPXMtu10bfEH2n>lI77UIN5eyaxBNPx{ zTNxQ09fF=kgkLlj5H1_1j4}@!6Dq441{oEY0h*kjxIbGz8DAKy55X)fI1?-|!4?(* z0yH!>HWktpxu3h53m6*@3>*k77ZDdQ5eOGD3=|2@(9_h_f}ET*5WfuuF(3#o1_m7W zF*YC|5x~X|X+jXf3*^KJg%R2i2mleofCvEw?imp9;R6$c3KcFiBkh2V1Ol{i0O7y^ zh!q7&I9MTb0|PP@H2vrRXk?lHk|$Gc_z)uu2@oX;w47;^%R!wy5BMZmfyBuKzbs(j z-~oh$03NQ4Nbw*-FHM@Lg_-)Z>eXKlFeHHBVL^ug2}U%Kpu(&Kw6cDzWm_bz4LTeq z0H|>RfrJK-Azx6@cN8^Fv7N|3To(~hMs99mR9Nz zrviC~5U7(cXyD|NSV}1+qKNw7>8GHE>VpDKVrl9QbH0!RmnM+<0uMZBpa2hnW~!;E zqH?P1poI2H=&r!->g%v+2CGA#$R?|7pPEi->a(><3FfL;)(Rz*1T3rVw%TeM<+IQd zpo6qCP%G}W;$q2uEwZq->+ZX-B1`7H$?o7o4?1X|LAyQdK&H0v1}t#E^3FRzz4zw3 zFRcCkyX?U2{weXq6#q#kyayv|uMY0|TQIEpYEbdWB#(UYyBYVnamOj29CFDL_aO7k nG}mnN%{b?*^Ugf??DNk+2QBo_L>F!J(MTt)^wLcG90UM6K}Y-7 literal 0 HcmV?d00001 diff --git a/msd/language/flags/pt.gif b/msd/language/flags/pt.gif new file mode 100644 index 0000000000000000000000000000000000000000..e32766f8fc1e81da4f36891439308a51dbb50b9d GIT binary patch literal 3316 zcmV)zK@LxW!e+Ja6E1<$9 z&Lx10`CwcnTfZhx6S3Zz0Qgx+`=q$ zZ=CWZBfHuHQJo99J_6?X0^J-UnVc!r7e6+P4({SQ8hilLMj+oG5a1sWEtUY>ArLW> z0Nx-FA^8LV00000EC2ui0DAyZ000L6K$~zFC>oE*q*8cfFa&~t?Jy`41ejEPo?kByR(jaNOF zd1GaMoJfFegHMHXc%`Ovm<4)FMn0~uKCQ5`w61`GK7;~>m%YBfz`?@9#Kc!W$Um8^ zMYXcE(4m1mxuQ{X#o5~1+EzWuJ()<*W7@bU8V^!4XvXw!lU9U7Fu%xO+ch(wsIsne%W->tMK6{=Ny3zhlQ z2#cs!qPdDPP?XkDu}is$9!=Wp4@_b*ALVQ}uKcs!(WNWAj{P*S?bGehop+ajC*ENU*zgiqyag0oItent z+yMu`5Wol{AV-A)94e6iKz{mg7+qUwZ8soc1@1H6Zs-xzU{?=TQ^te`2!Y`)8*=D^ z3($Qi-3$txAS8)T<#eK3->Hb8g6A>l*lX1k!5&EK*dTxj7>;0^8x$@uzyL9XNdgH7 zJkX(tGt^MSJlXwnBw9&^x1xEx3H1s{Q6BZ+AO~ESr3p9)2Lk~cbkM;EAsCRub!RF7 zotkX==gXUC#d)A^OxBs6D_ag{9uE&5=L3ZY5I}$f5~L~t3m&w}0t*x*8fFaF;n#ya zZKgu$RVm7OSa}Qf#wmTCN*UahO676@4H~eJYO5YdFo6RburNUf6j1O%4iD4{-L3KD z8cqSbLS^ZA2?lHb=dchuVMI*EHJ5<}6HKtd1~54B!V407(0~C3WYBB}EI0rr31%uV zZh;Bxa~c-qzSZlcE=DZC>butB{JWI$oQ2ka|B2o1#W-Z!t>Eb^( zDeU7jJviaL4y-Uh0~Un;0SO!=;GqE;I6#4z*2REm2wzTM!TrFaKU)D08E(DeO2QP- zT*N(4wTC%`x`CnScfSx&paC2>feR2|01vn?Wl|fRwmN`158TfJ75H4%X2TNdgvWpX zgI&6`x2|fOZ7=3=!UA4(gH*9#006^44r%~`27us)6{tYWe)z)&C=UZ5AigVVsjc`Oq_5w4aLF<0&7*OCa{33T@3_5tN;cukVZAK5d#T8fYipc z0&RW&U;-gPpOB=pwJN3%i@$2z_B>*oTjauJ8*qRF{l%dK~}R%o(2I6vCbcF6+jNGP*VgnAhHON3k}>LY0=810$i|z z1o%>tBDeqqE)W59mQqM3NI(o4K&EIJfCN9_hjpTvN}jD!kS)|^Aw|Ih7`%!A3pkbK zI>doH{$tTmh~Vq8W_ziq!Pg*tBT2)uhyHFwlSnpJjoV zb}I)Z5J3tea8pW(K&xL>0m2?|RcZC~WKT>$xAy5M1sqkCfD~XW3HTBy9Ka~KsFnph zSveXkAOs=sT~=G5&Ikk(0ufjzVjG}=6@=iC8t~+^{FZ>B5*0{B?PtVRDZA~})r(Hh zYpXKgw!elTd?f%T1X`e4cB=CNDHwnTH1Jp@4ggvnpnw4o@KK_M)UAj^D)z*1&9c3M zi`}~`8+@<<3OL}UG$8E)ZaLc$sGtQcKmkM%i(M3`U;xf#fdLwjftOk^x1Y2BEJz*o zR)5B|VoW_QUe9sYLBQaCFJ*xS{F(t!=5nIi1wdlkDFW@PpaGzCKn`Ni0a`w{Uoi*= zf2IQ@1OykZNu^kEXV*nQ_`n7RE!e3nU^%ir0B@F*BqiG^$qK|V2P6PN4nAN29il3a zm9@e?G27nFzL%BQj0r5D;I4Eu3!&u|pLHG(;|fT!wU;cx4-zmY5?~-^^^`!&4%AzH zR9Lg+`e%IWvQ5vt#s)`ozySgUg9UJ42JPtOlV2-Iv2th!^R&PVHV^{?c=Dd*yaY&5 zInCk5Zp^$XGZbKe0|Fe>I5t>74Q7ylA8?=nGvq>xWNnmJn(@Pc=Bpf zt{F&03g%sv6wy#{52%jA02owD%^T3b2O37snKl3ekxW5=zZ-)e2+0K?$N&Xuct0A` zii(w0T>*43Gm`$;qy&t0HD;|81Qu*x6QFFvIFJJj=nVxo6+sIYPy!6pY;z+*0x&KU zcrr2VY74iTKsIg{htr1ktgT?Xn{6)18!``MAb4s$?Wx8$fF?for8lq|nJ$v=j`Q2v z8D<=;g+ubkREP!zLlp~da6?Y2u}rjjz(<$2vU)N7xfTe))vQDRow{Y>8y?57p+oK@ zZ43M}6xt{Y2ykA1k$_uMrd65VK0X6bpnT7LmBGklVfDG7(ucW3*xeiS>;<{0W*0c9 za8YX*IN%2DTfhMvgM+vYAjS#!Aqf(ZgC3UG19+?~0;XBuuB)-U0 zQr4+nrnDzTx&b9fUJv6~X}qy7Ra#Gg7!Xit>Nsi#sMm+;HH>Z76J2A*3QiGh%`Xc| z9tZl8feaofwUiA2;^E${`V=z09-xU0see}cTf7$XS=jCfUkVLK>J$fqyaua z0sAG2OlDFM<<}CfvwT9=dQBx{0ijJA!vF+81E6O=<#T`kGT?Ieb1m6oZ+-Ve4#5&? z;U5P0E1eZkO+^b+a#tiHFA%p^5}1W*7UCNL^qLIR1nIc_yN9AHtR_yCO9 zTXeV(2NH>h@e+#RN&s;VW(FaCNCoHzbcU8TK_(Obynzk{u@t%JgA~Jy7vqaZ0gUQ^ zi^GVA)ntr=G>b44Io9A8&FGA)2aQ1}jmlV!%y^Bi_Fmd3i~k@H)Myaa2#(=cc&Wh^ z$LNdZ$avoP7wUK`xp=#J4?j?*ZQ{Ya1h*c|~0i*-Sc=1`FQ zD2xdSkm4wi+enbzc#soGk?i=2=Mjtf$c-u$kq5Dn1G11G*^nWLks}$5C25kBfs!V8 zkq?=X5($%%A(IQ}kT3y}E}4!vSs4|{ki7_xKDmqt8I(wJZDr7uPWhBj$qe$LAfwP6 ys&kcCnUz|(m0a1CUdfe2^pp{jiBVaWQpp?6b>{vEMRD8U|4W~;s1Xi`VW;vr4)a%00q7?=zxSl zW-zmK3!Ly=y*Kp5Sxd#(9iB<<62}u8o^otCH1nKR^rS^T?_d9&wc@6NgUp8+#X zA=Thp%vfV$>*-+P?7|wQ*&ArD*buGQYFRPWW6lg+t7*D(4de}`ORQZdBFJD30GP>5 AKL7v# literal 0 HcmV?d00001 diff --git a/msd/language/flags/sw.gif b/msd/language/flags/sw.gif new file mode 100644 index 0000000000000000000000000000000000000000..a82090e74711fbc1b9fa8527cdfe4e675f57adb7 GIT binary patch literal 117 zcmZ?wbhEHbRA3Nc_{abT$#edn12J?!1W25b$*-k<LyePyJ0j|@Ca0_ec}Q!QUP zK9;pmmjwhhLC|xOf~pHzN~KqEav zGe|p-HqHWk6ah&g1&{zD0puT*{g%!?%aoDiQ$V7x@COShiZRA}zagp_Mf^TAC3)f$ ziJ>*WSkGG)J^=hoQ?a*aB;AC-V}1lB{z<@_aM@EHr^W}qDDX&<;&w|MV48qwlUb{Y zev0PDNBWb3A$GT0XBZr~gL3fI?GAJ=&@yvy>N$3{;;pN1{29sm>T!zu42lNkhq+FS zy-0$DW#t-8AtfONc%$n9$BoP8xPgsP3E*)q*SYwa_yf^62 zJLj-ShYy`6BZA=a1;O7*Eo;rGj26&4+P7BhKI`#$tl@2S8=H>O^x6$u$f5=plYM6K zv2`n!o|N|~CkS64=w!K-OExX9<+2LzU6a8}T{c^<*Y)>Rrde{1Kei2ohc8~@GlWqk m+)Z8ah4O4SH~-x`Qo7m`GWdf}Nyb#sGt|{zMiyNFDESZTkJN+! literal 0 HcmV?d00001 diff --git a/msd/language/flags/vn.gif b/msd/language/flags/vn.gif new file mode 100644 index 0000000000000000000000000000000000000000..7cdc6270e40585149a6260fe82434a80a459c33d GIT binary patch literal 207 zcmZ?wbhEHbG-8lrSj52a{|v+bnZ{?*jL#SwpJ6a&fC0syEMTS%hy&`y| zE2q_aEndfp{!j3zOv>y#yt3^{*REJD;U)v&wcHHl>-el^o-XHl!(_ZDbGtW}JwKzc zLc@l6vl|Ofy_MT2wbbpE?roPxAGZB5U22~4ng7hB^WlZ>etx;PW~WDaO)ytWlUfR+ zQIvJHYORDr*F?7xnF-=iGL!SBMJmioTxj0EcuDwj+hr@KuU@lAXyc~M+?)*702?(= A?*IS* literal 0 HcmV?d00001 diff --git a/msd/language/fr/help.html b/msd/language/fr/help.html new file mode 100644 index 0000000..5cd7e38 --- /dev/null +++ b/msd/language/fr/help.html @@ -0,0 +1,148 @@ +
      +

      MyOOS [Dumper] basé sur MySQLDumper 1.24.4

      + +

      A propos de ce projet

      +

      MyOOS [Dumper] est une version améliorée de MySQLDumper 1.24.4 (24 janvier 2011). Cette évolution prend en compte le développement du PHP. +

      MyOOS [Dumper] se préoccupe avant tout de stabilité, de sécurité et de maniabilité. Mais un modèle attrayant est également inclus, qui peut être modifié et adapté à vos propres besoins.

      + + +

      MyOOS [Dumper] est un programme de sauvegarde pour les bases de données MySQL, écrit en PHP et Perl. Il permet de créer des copies de sauvegarde des données (boutique, blog, etc.) et de les restaurer si nécessaire. En particulier pour les espaces web sans accès au shell, MyOOS [Dumper] s'offre comme une alternative judicieuse.

      . + +

      L'idée de MySQLDumper vient de Daniel Schlichtholz. Il a ouvert le forum MySQLDumper en 2004, à la suite de quoi les programmeurs ont écrit de nouveaux scripts et étendu les scripts existants.

      + + +

      Liste de souhaits / Attractions futures

      . +

      Avez-vous des suggestions d'amélioration ? N'hésitez pas à contacter l'équipe de développement via le forum https://foren.myoos.de/viewforum.php?f=41.

      . + + +

      Contribuer

      . +

      Si vous souhaitez nous aider à améliorer le projet MyOOS, nous accueillons vos demandes de pull via GitHub ici.

      . +https://github.com/r23/MyOOS-Dumper/ + + +

      Soutien financier

      +

      Vous pouvez utiliser PayPal Me
      . +https://www.paypal.com/paypalme/r23de?locale.x=de_DE

      . + +

      ou via le code QR
      . +Soutien financier à MyOOS [Dumper]

      + +Envoyez de l'argent au projet MyOOS.
      + +

      Nous espérons que ce projet vous plaira.

      L'équipe MyOOS [Dumper]

      . + +MyOOS [Dumper]
      +MyOOS [Dumper]
      + + +

      MyOOS [Dumper] Aide

      + +

      Téléchargement

      +

      Vous pouvez toujours obtenir les dernières versions sur GitHub
      . +https://github.com/r23/MyOOS-Dumper/releases

      . + + +

      Système requis

      +

      Le script fonctionne sur n'importe quel serveur (Windows, Linux, ...)
      . +avec PHP >= version 7.4 avec support GZip, MySQL (version 4.1 ou supérieure), JavaScript (doit être activé).

      +

      Copier le dossier mod de l'archive MyOOS dans un dossier de travail séparé.

      . + +

      Installation

      +L'installation est simple. +

      À partir de l'archive MyOOS, copiez le dossier du mod dans n'importe quel dossier.
      +Téléchargez tous les fichiers du dossier mod sur votre serveur Web. (par exemple, au niveau le plus bas de [répertoire web du serveur/]mod)
      . +... fait!
      +Vous pouvez maintenant appeler MyOOS [Dumper] dans votre navigateur web en allant sur "https://example.com/mod/"
      . +pour terminer l'installation. Il suffit de suivre les instructions.
      +
      Note:
      Si sur votre serveur le script n'est pas autorisé à créer des répertoires,
      . +vous devrez le faire manuellement, car MyOOS [Dumper] stocke les données dans des répertoires. +répertoires.
      +Le script se termine par une déclaration appropriée !
      +Une fois que vous avez créé les répertoires (selon l'indication), il fonctionne normalement et sans restrictions.
      + +

      Instructions pour le script Perl

      +La plupart ont un répertoire cgi-bin où Perl peut être exécuté.
      +Il est généralement accessible par le navigateur via http://www.example.com/cgi-bin/.
      +
      +Dans ce cas, veuillez effectuer les étapes suivantes :

      1. + +1. Appelez la page de sauvegarde dans MyOOS [Dumper] et cliquez sur "Backup Perl".
      +2. copiez le chemin derrière l'entrée dans crondump.pl pour $absolute_path_of_configdir :.
      +3. ouvrez le fichier "crondump.pl" dans l'éditeur.
      +4. saisissez le chemin copié à cet endroit à absolute_path_of_configdir (sans espace).
      +5. sauvegarder crondump.pl.
      +Copiez crondump.pl, perltest.pl et simpletest.pl dans le répertoire cgi-bin (mode ascii en FTP). +7. donnez aux fichiers les permissions 755.
      +7b. Si la terminaison cgi est souhaitée, changez la terminaison des 3 fichiers de pl -> cgi (renommer).
      +Appelez la configuration dans MyOOS [Dumper]. +9. sélectionnez la page Cronscript.
      +10. changer le chemin d'exécution de Perl en /cgi-bin/ .
      +10b. Si les scripts ont un .pl, changez l'extension du fichier en .cgi .
      +11. Enregistrez la configuration.

      + +C'est fait, les scripts peuvent maintenant être appelés à partir de la page de sauvegarde.

      . + +Pour ceux qui peuvent exécuter Perl dans tous les répertoires, les étapes suivantes sont suffisantes:

      . + +1. Appelez la page Sauvegarde dans MyOOS [Dumper].
      +2. copiez le chemin derrière l'entrée dans crondump.pl pour $absolute_path_of_configdir :.
      +Ouvrez le fichier "crondump.pl" dans l'éditeur.
      +4. entrez le chemin copié à cet endroit à absolute_path_of_configdir (sans espace).
      +5. sauvegarder crondump.pl .
      +6. donnez aux fichiers les permissions 755.
      +6b. Si la terminaison cgi est souhaitée, changez la terminaison des 3 fichiers de pl -> cgi (renommer).
      +(ev. 10b+11 du dessus)
      +
      + +Les utilisateurs de Windows doivent modifier la première ligne de tous les scripts, où se trouve le chemin de Perl. Exemple :
      +au lieu de : #!/usr/bin/perl -w
      +maintenant : #!C:_usr/bin/perl.exe -w
      + +

      Opération

        . + +
        Menu
        . +Dans la liste de sélection ci-dessus, vous définissez la base de données.
        +Toutes les actions se réfèrent à la base de données définie ici. + +
        Accueil
        +Vous y trouverez des informations sur votre système, les différentes versions installées et les détails des bases de données configurées. +versions installées et des détails sur les bases de données configurées.
        +Si vous cliquez sur le nom de la base de données, vous verrez une liste des tables avec le nombre d'entrées. +avec le nombre d'entrées, la taille et la date de la dernière mise à jour. + +
        Configuration
        +Vous pouvez y modifier votre configuration, l'enregistrer ou restaurer la configuration initiale. +pour revenir à la configuration initiale. +

          +
        • Bases de données configurées: la liste des bases de données configurées. La base de données active est indiquée en gras.
        • +
        • Préfixe de table: ici vous pouvez spécifier un préfixe (pour chaque base de données). Il s'agit d'un filtre qui ne prend en compte que les tables commençant par ce préfixe lors du vidage (par exemple, toutes les tables commençant par "phpBB_"). Si vous voulez que toutes les tables de cette base de données soient enregistrées, laissez le champ vide.
        • . +
        • Compression GZip: Ici, vous pouvez activer la compression. Il est recommandé de l'activer, car les fichiers deviennent beaucoup plus petits et l'espace de stockage est toujours rare. +
        • Email avec fichier dump: Si cette option est activée, un email avec le dump en pièce jointe est envoyé après la fin de la sauvegarde (attention, la compression doit absolument être activée, sinon la pièce jointe sera trop grande et risque de ne pas être envoyée !) +
        • Adresse e-mail:Adresse du destinataire de l'e-mail.
        • +
        • Envoyeur du courriel: cette adresse apparaît comme l'expéditeur dans le courriel.
        • +
        • Transfert FTP : Si cette option est activée, le fichier de sauvegarde sera envoyé par FTP une fois la sauvegarde terminée.
        • . +
        • Serveur FTP : L'adresse du serveur FTP (par exemple ftp.mybackups.de).
        • +
        • Port du serveur FTP : Le port du serveur FTP (généralement 21).
        • +
        • Utilisateur FTP : Le nom d'utilisateur du compte FTP.
        • +
        • Mot de passe FTP : Le mot de passe du compte FTP.
        • +
        • Dossier de téléchargement FTP : Le répertoire où le fichier de sauvegarde doit aller (les autorisations de téléchargement doivent exister !).
        • . +
        • Suppression automatique des sauvegardes:Si cette option est activée, les anciennes sauvegardes seront supprimées automatiquement selon les règles suivantes.
        • +
        • Nombre de fichiers de sauvegarde : Une valeur > 0 supprime tous les fichiers de sauvegarde sauf le nombre spécifié ici.
        • . +
        • Langue: ici vous définissez la langue de l'interface.
        • +
        + +
        Administration
        +C'est là que les actions réelles sont effectuées. +Tous les fichiers du répertoire de sauvegarde sont affichés. +Pour les actions "Restaurer" et "Supprimer", un fichier doit être sélectionné. +
          +
        • Restauration: Ceci met à jour la base de données avec le fichier de sauvegarde sélectionné.
        • +
        • Supprimer: Cela vous permet de supprimer le fichier de sauvegarde sélectionné.
        • . +
        • Démarrer une nouvelle sauvegarde : Vous pouvez ici démarrer une nouvelle sauvegarde (dump) selon les paramètres définis dans la configuration. +
        + +
        Log
        +Ici, vous pouvez voir et supprimer les entrées du journal. +
        Crédits / Aide
        +cette page. +
      diff --git a/msd/language/fr/lang.php b/msd/language/fr/lang.php new file mode 100644 index 0000000..a399baf --- /dev/null +++ b/msd/language/fr/lang.php @@ -0,0 +1,109 @@ +indisponible'; +$lang['L_VOM'] = 'de'; +$lang['L_MYSQLVARS'] = 'Variables MySQL'; +$lang['L_MYSQLSYS'] = 'Ordre MySQL'; +$lang['L_STATUS'] = 'État'; +$lang['L_PROZESSE'] = 'Processus'; +$lang['L_INFO_NOVARS'] = 'aucunes variables disponibles'; +$lang['L_INHALT'] = 'Contenu'; +$lang['L_INFO_NOSTATUS'] = 'aucun état disponible'; +$lang['L_INFO_NOPROCESSES'] = 'aucun processus en cours'; +$lang['L_FM_FREESPACE'] = 'Espace libre sur le serveur'; +$lang['L_LOAD_DATABASE'] = 'Rafraîchir bases de données'; +$lang['L_HOME'] = "Page d'accueil"; +$lang['L_CONFIG'] = 'Configuration'; +$lang['L_DUMP'] = 'Sauvegarde'; +$lang['L_RESTORE'] = 'Restauration'; +$lang['L_FILE_MANAGE'] = 'Administration'; +$lang['L_LOG'] = 'Journal'; +$lang['L_CHOOSE_DB'] = 'Choisir la base de données'; +$lang['L_CREDITS'] = 'Crédits / Aide'; +$lang['L_MULTI_PART'] = 'Sauvegarde en plusieurs parties'; +$lang['L_LOGFILENOTWRITABLE'] = 'Écriture du fichier journal impossible!'; +$lang['L_SQL_ERROR1'] = 'Erreur de demande:'; +$lang['L_SQL_ERROR2'] = 'MySQL déclare:'; +$lang['L_UNKNOWN'] = 'inconnu'; +$lang['L_UNKNOWN_NUMBER_OF_RECORDS'] = 'inconnu'; +$lang['L_OK'] = 'OK'; +$lang['L_CRON_COMPLETELOG'] = 'Sauvegarder toutes les sorties dans le journal'; +$lang['L_NO'] = 'non'; +$lang['L_CREATE_DATABASE'] = 'Créer une nouvelle base de données'; +$lang['L_EXPORTFINISHED'] = 'Exportation terminée.'; +$lang['L_SQL_BROWSER'] = 'Navigateur-SQL'; +$lang['L_SERVER'] = 'Serveur'; +$lang['L_MYSQL_CONNECTION_ENCODING'] = 'Encodage standard du serveur MySql'; +$lang['L_TITLE_SHOW_DATA'] = 'Afficher les données'; +$lang['L_PRIMARYKEY_CONFIRMDELETE'] = 'Really delete primary key?'; +$lang['L_SETPRIMARYKEYSFOR'] = 'Set new primary keys for table'; +$lang['L_PRIMARYKEY_FIELD'] = 'Primary key field'; +$lang['L_PRIMARYKEYS_SAVE'] = 'Save primary keys'; +$lang['L_CANCEL'] = 'Cancel'; +$lang['L_VISIT_HOMEPAGE'] = 'Visit Homepage'; +$lang['L_SECONDS'] = 'Seconds'; +$lang['L_BACKUPS'] = 'Nombres de sauvegardes'; +$lang['L_MINUTES'] = 'Minutes'; +$lang['L_PAGE_REFRESHS'] = 'Page refreshs'; +$lang['L_MINUTE'] = 'Minute'; +$lang['L_SETKEYSFOR'] = 'Set new indexes for table'; +$lang['L_KEY_CONFIRMDELETE'] = 'Really delete index?'; diff --git a/msd/language/fr/lang_config_overview.php b/msd/language/fr/lang_config_overview.php new file mode 100644 index 0000000..2c92a8a --- /dev/null +++ b/msd/language/fr/lang_config_overview.php @@ -0,0 +1,130 @@ +%s
      dans %s'; +$lang['L_FTP'] = 'FTP'; +$lang['L_SFTP_SEND_TO'] = 'vers %s
      dans %s'; +$lang['L_SFTP'] = 'SFTP'; +$lang['L_EMAIL_CC'] = 'Récepteur-CC'; +$lang['L_NAME'] = 'Nom'; +$lang['L_CONFIRM_CONFIGFILE_DELETE'] = 'Etes vous sûr de vouloir supprimer le fichier de configuration %s ?'; +$lang['L_ERROR_DELETING_CONFIGFILE'] = 'Erreur: Le fichier de configuration %s ne peut pas être supprimé !'; +$lang['L_SUCCESS_DELETING_CONFIGFILE'] = 'Le fichier de configuration %s a été supprimé avec succès.'; +$lang['L_SUCCESS_CONFIGFILE_CREATED'] = 'Le fichier de configuration %s a été créé avec succès.'; +$lang['L_ERROR_CONFIGFILE_NAME'] = 'Le nom du fichier "%s" contient des caractères invalides.'; +$lang['L_CREATE_CONFIGFILE'] = 'Créer un nouveau fichier de configuration'; +$lang['L_ERROR_LOADING_CONFIGFILE'] = 'Impossible de lire le fichier de configuration "%s".'; +$lang['L_BACKUP_DBS_PHP'] = 'Sauvegarde DBs +(PHP)'; +$lang['L_BACKUP_DBS_PERL'] = 'Sauvegarde Dbs +(PERL)'; +$lang['L_CRON_COMMENT'] = 'Ajouter un commentaire'; +$lang['L_AUTODETECT'] = 'auto detect'; diff --git a/msd/language/fr/lang_dump.php b/msd/language/fr/lang_dump.php new file mode 100644 index 0000000..e54613a --- /dev/null +++ b/msd/language/fr/lang_dump.php @@ -0,0 +1,57 @@ +%s`."; +$lang['L_DUMP_ENDERGEBNIS'] = '%s table(s) avec en tout %s enregistrement(s) a/ont été sauvegardée(s).
      '; +$lang['L_MAILERROR'] = "Malheureusement une erreur est apparue lors de l'envoie par courriel!"; +$lang['L_EMAILBODY_ATTACH'] = 'Dans le fichier joint vous trouverez une sauvegarde de votre base de données MySQL.
      Copie de sauvegarde de la base de données `%s` +

      Les fichiers suivants ont été créés:

      %s

      Cordialement

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_MP_NOATTACH'] = 'Une sauvegarde en plusieurs parties a été créé.
      Les sauvegardes ne sont pas envoyées en pièces jointes!
      Copie de sauvegarde de la base de données `%s` +

      Les fichiers suivants ont été créés:

      %s


      Cordialemene

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_MP_ATTACH'] = 'Une sauvegarde en plusieurs parties a été créé.
      Les sauvegardes sont envoyées en pièces jointes!
      Copie de sauvegarde de la base de données `%s` +

      Les fichiers suivants ont été créés:

      %s


      Cordialemene

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_FOOTER'] = '


      Cordialement

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_TOOBIG'] = "La copie de sauvegarde dépasse la taille maximale de %s. Pour cette raison elle n'a pas été envoyée en pièces jointes.
      Copie de sauvegarde de la base de données `%s` +

      Les fichiers suivants ont été créés:

      %s +

      Cordialement

      MyOOS [Dumper]
      "; +$lang['L_EMAILBODY_NOATTACH'] = "La copie de sauvegarde n'est pas jointe.
      Copie de sauvegarde de la base de données `%s` +

      Les fichiers suivants ont été créés:

      %s +

      Cordialement

      MyOOS [Dumper]
      "; +$lang['L_EMAIL_ONLY_ATTACHMENT'] = ' ... seulement la pièce jointe'; +$lang['L_TABLESELECTION'] = 'Sélection de la table'; +$lang['L_SELECTALL'] = 'Tout sélectionner'; +$lang['L_DESELECTALL'] = 'Tout désélectionner'; +$lang['L_STARTDUMP'] = 'Démarrer la sauvegarde'; +$lang['L_LASTBUFROM'] = 'dernière sauvegarde du'; +$lang['L_NOT_SUPPORTED'] = 'Cette sauvegarde ne supporte pas cette fonction.'; +$lang['L_MULTIDUMP'] = 'Sauvegarde en plusieurs parties: Les bases de données %d ont été sauvegardées.'; +$lang['L_FILESENDFTP'] = 'Les fichiers sont envoyés par FTP... Veuillez patienter. '; +$lang['L_FTPCONNERROR'] = "Aucune connexion FTP n'a pu être établie! Connecter avec "; +$lang['L_FTPCONNERROR1'] = ' comme utilisateur '; +$lang['L_FTPCONNERROR2'] = ' impossible'; +$lang['L_FTPCONNERROR3'] = 'Téléchargement vers le serveur FTP est erroné! '; +$lang['L_FTPCONNECTED1'] = 'Connecté avec '; +$lang['L_FTPCONNECTED2'] = ' sur '; +$lang['L_FTPCONNECTED3'] = ' a été écrit'; +$lang['L_FILESENDSFTP'] = 'Les fichiers sont envoyés par SFTP... Veuillez patienter. '; +$lang['L_SFTPCONNERROR'] = "Aucune connexion SFTP n'a pu être établie! Connecter avec "; +$lang['L_NR_TABLES_SELECTED'] = '- avec %s des tables sélectionnées'; +$lang['L_NR_TABLES_OPTIMIZED'] = '%s tables ont été optimisées.'; +$lang['L_DUMP_ERRORS'] = '

      %s erreurs rencontrées: verdere

      '; +$lang['L_FATAL_ERROR_DUMP'] = "Erreur fatale: Le rapport de création de la table '%s' de la base de données '%s' ne peut pas être lu !"; diff --git a/msd/language/fr/lang_filemanagement.php b/msd/language/fr/lang_filemanagement.php new file mode 100644 index 0000000..8b13659 --- /dev/null +++ b/msd/language/fr/lang_filemanagement.php @@ -0,0 +1,75 @@ +%s"'; +$lang['L_DELETE_FILE_ERROR'] = 'Error deleting file "%s"!'; +$lang['L_FM_DUMP_HEADER'] = 'Copie de sauvegarde'; +$lang['L_DOCRONBUTTON'] = 'Exécuter le script Cron'; +$lang['L_DOPERLTEST'] = 'Tester le module Perl'; +$lang['L_DOSIMPLETEST'] = 'Tester Perl'; +$lang['L_PERLOUTPUT1'] = 'Saisie dans crondump.pl pour absolute_path_of_configdir'; +$lang['L_PERLOUTPUT2'] = 'Saisie dans un navigateur ou pour exécuter un script Cron externe'; +$lang['L_PERLOUTPUT3'] = "Saisie pour 'Shell' ou pour la crontab"; +$lang['L_RESTORE_OF_TABLES'] = 'Choisissez les tables à restaurer'; +$lang['L_CONVERTER'] = 'Convertir la copie de sauvegarde'; +$lang['L_CONVERT_FILE'] = 'fichier qui doit être converti'; +$lang['L_CONVERT_FILENAME'] = 'Nom du fichier final (sans extensions)'; +$lang['L_CONVERTING'] = 'En cours de convertion'; +$lang['L_CONVERT_FILEREAD'] = "Lire fichier '%s'"; +$lang['L_CONVERT_FINISHED'] = "Convertion terminée, fichier '%s' créé avec succès."; +$lang['L_NO_MOD_BACKUPFILE'] = "Copies de sécurités d'autres programmes"; +$lang['L_MAX_UPLOAD_SIZE'] = 'Taille maximale du fichier'; +$lang['L_MAX_UPLOAD_SIZE_INFO'] = "Si votre fichier Dump est plus grand que la taille mentionnée plus haut, vous devez alors l'envoyer sur le serveur dans le répertoire en utilisant votre programme FTP."; +$lang['L_ENCODING'] = 'encodage'; +$lang['L_FM_CHOOSE_ENCODING'] = "Choisissez le type d'encodage de la sauvegarde"; +$lang['L_CHOOSE_CHARSET'] = "MyOOS [Dumper] n'a pas pu détecter automatiquement le type d'encodage de la sauvegarde
      Vous devez choisir le jeux de caractères qui a été utilisé pour la sauvegarde
      Si vous découvrez des problèmes avec quelques caractères suite à la restauration, vous pouvez répéter l'opération en choisissant un autre jeux de caractères.
      Bonne chance ;-)"; +$lang['L_DOWNLOAD_FILE'] = 'Download file'; +$lang['L_BACKUP_NOT_POSSIBLE'] = 'A backup of the system database `%s` is not possible!'; diff --git a/msd/language/fr/lang_help.php b/msd/language/fr/lang_help.php new file mode 100644 index 0000000..a0ce454 --- /dev/null +++ b/msd/language/fr/lang_help.php @@ -0,0 +1,41 @@ +Installation terminée --> start MyOOS [Dumper]
      '; +$lang['L_INSTALL_TOMENU'] = 'Retour au menu principal'; +$lang['L_INSTALLMENU'] = 'Menu principal'; +$lang['L_STEP'] = 'Étape'; +$lang['L_INSTALL'] = 'Installation'; +$lang['L_UNINSTALL'] = 'Désinstallation'; +$lang['L_TOOLS'] = 'Outils'; +$lang['L_EDITCONF'] = 'Éditer la configuration'; +$lang['L_OSWEITER'] = 'continuer sans sauvegarde'; +$lang['L_ERRORMAN'] = "Erreur lors de l'écriture de la configuration!
      Veuillez éditer le fichier "; +$lang['L_MANUELL'] = 'manuellement'; +$lang['L_CREATEDIRS'] = 'créer répertoire'; +$lang['L_INSTALL_CONTINUE'] = "continuer avec l'installation"; +$lang['L_CONNECTTOMYSQL'] = ' connecter à MySQL '; +$lang['L_DBPARAMETER'] = 'Paramètre de la base de données'; +$lang['L_CONFIGNOTWRITABLE'] = "Impossible d'écrire le fichier \"config.php\". +Veuillez utiliser votre programme FTP et chmoder ce fichier en 0777."; +$lang['L_DBCONNECTION'] = 'Connexion base de données'; +$lang['L_CONNECTIONERROR'] = "Erreur: aucune connexion n'a pu être créée."; +$lang['L_CONNECTION_OK'] = 'Connexion avec la base de données a été établie.'; +$lang['L_SAVEANDCONTINUE'] = "sauvegarder et continuer l'installation"; +$lang['L_CONFBASIC'] = 'Configuration de base'; +$lang['L_INSTALL_STEP2FINISHED'] = 'Configuration de la base de données a été sauvegardée.'; +$lang['L_INSTALL_STEP2_1'] = "Continuer l'installation avec la configuration standart"; +$lang['L_LASTSTEP'] = "Terminer l'installation"; +$lang['L_IDOMANUAL'] = 'Créer un répertoire manuellement'; +$lang['L_DOFROM'] = 'terminer par'; +$lang['L_FTPMODE2'] = 'Créer un répertoire par FTP:'; +$lang['L_CONNECT'] = 'connecter'; +$lang['L_DIRS_CREATED'] = 'Les répertoires ont été créés avec succès.'; +$lang['L_CONNECT_TO'] = 'connexion vers'; +$lang['L_CHANGEDIR'] = 'muter vers le répertoire'; +$lang['L_CHANGEDIRERROR'] = 'Mutation dans le répertoire non possible'; +$lang['L_FTP_OK'] = 'Paramètres FTP sont ok'; +$lang['L_CREATEDIRS2'] = 'Créer répertoires'; +$lang['L_FTP_NOTCONNECTED'] = 'Connexion Ftp non établie!'; +$lang['L_CONNWITH'] = 'Connexion avec'; +$lang['L_ASUSER'] = 'comme utilisateur'; +$lang['L_NOTPOSSIBLE'] = 'impossible'; +$lang['L_DIRCR1'] = 'créer répertoire de travail'; +$lang['L_DIRCR2'] = 'créer répertoire de sauvegarde'; +$lang['L_DIRCR4'] = 'créer répertoire du journal'; +$lang['L_DIRCR5'] = 'créer répertoire de configuration'; +$lang['L_INDIR'] = 'je suis dans le répertoire'; +$lang['L_CHECK_DIRS'] = 'vérifier'; +$lang['L_DISABLEDFUNCTIONS'] = 'Fonctions désactivées'; +$lang['L_NOFTPPOSSIBLE'] = "Il n'y a pas de fonction FTP à disposition!"; +$lang['L_NOGZPOSSIBLE'] = "Il n'y a pas de fonction de compression à disposition!"; +$lang['L_UI1'] = 'Supprimer tous les répertoires de travail avec les sauvegardes incluses.'; +$lang['L_UI2'] = "Êtes-vous sûr d´exécuter l'opération ?"; +$lang['L_UI3'] = 'Non, arrêter immédiatement'; +$lang['L_UI4'] = 'Oui, veuillez continuer'; +$lang['L_UI5'] = 'Supprimer répertoire de travail'; +$lang['L_UI6'] = 'tout a été supprimé avec succès.'; +$lang['L_UI7'] = 'Veuillez supprimer le répertoire de script'; +$lang['L_UI8'] = 'un niveau supérieur'; +$lang['L_UI9'] = 'Une erreur est apparue, suppression impossible

      Erreur dans le répertoire '; +$lang['L_IMPORT'] = 'Importer la configuration'; +$lang['L_IMPORT3'] = 'La configuration a été chargée...'; +$lang['L_IMPORT4'] = 'La configuration a été sauvegardée.'; +$lang['L_IMPORT5'] = 'Démarrer MyOOS [Dumper]'; +$lang['L_IMPORT6'] = "Menu d'installation;"; +$lang['L_IMPORT7'] = 'Télécharger vers le serveur la configuration'; +$lang['L_IMPORT8'] = 'retourner vers le téléchargement'; +$lang['L_IMPORT9'] = "Ceci n'est pas une sauvegarde de la configuration !"; +$lang['L_IMPORT10'] = 'La configuration a été téléchargée vers le serveur avec succès ...'; +$lang['L_IMPORT11'] = "Erreur: Il y a des problèmes d'écriture pour les sql_statements"; +$lang['L_IMPORT12'] = "Erreur: Il y a des problèmes d'écriture pour le fichier config.php"; +$lang['L_INSTALL_HELP_PORT'] = '(vide = Port standart)'; +$lang['L_INSTALL_HELP_SOCKET'] = '(vide = Socket standart)'; +$lang['L_TRYAGAIN'] = 'Réessayer'; +$lang['L_SOCKET'] = 'Socket'; +$lang['L_PORT'] = 'Port'; +$lang['L_FOUND_DB'] = 'Base de données trouvée'; +$lang['L_FM_FILEUPLOAD'] = 'Télécharger un fichier vers le serveur'; +$lang['L_PASS'] = 'Mot de passe'; +$lang['L_NO_DB_FOUND_INFO'] = "La connexion avec la base de données a été établie avec succès.
      +Vos données utilisateur sont valides et ont été acceptées par le serveur MySQL.
      +Mais, MyOOS [Dumper] n'a pas été capable de trouver une base de données.
      +La détection automatique via le script est dans quelques cas bloquée par certains serveurs.
      +Vous devez alors entrer manuellement le nom de votre base de données après la fin de l'installation. +Cliquer sur \"configuration\" \"Affichage des paramêtres de connexion\" et entrer le nom de votre base de données."; +$lang['L_ENTER_DB_INFO'] = 'First click the button "Connect to MySQL". Only if no database could be detected you need to provide a database name here.'; diff --git a/msd/language/fr/lang_log.php b/msd/language/fr/lang_log.php new file mode 100644 index 0000000..f0ce520 --- /dev/null +++ b/msd/language/fr/lang_log.php @@ -0,0 +1,7 @@ +Veuillez créer manuellement un fichier avec les informations suivantes'; +$lang['L_HTACC_CHECK_ERROR'] = 'It could not be checked whether the program is protected!
      The simulated external access could not be carried out.'; +$lang['L_HTACC_NOT_NEEDED'] = 'The program is protected by higher-level authorizations; local directory protection is not required.'; +$lang['L_HTACC_COMPLETE'] = 'The program is protected, the directory protection is complete.'; +$lang['L_HTACC_INCOMPLETE'] = 'The program is not protected, the directory protection is incomplete!'; +$lang['L_HTACC_PROPOSED'] = 'The program is not protected, directory protection is strongly recommended!'; +$lang['L_HTACC_EDIT'] = 'Éditer .htaccess'; +$lang['L_HTACCESS18'] = 'Créer .htaccess dans '; +$lang['L_HTACCESS19'] = 'Réinitialiser '; +$lang['L_HTACCESS20'] = 'Exécuter le script'; +$lang['L_HTACCESS21'] = 'Ajouter le fournisseur'; +$lang['L_HTACCESS22'] = "Permettre l'exécution"; +$lang['L_HTACCESS23'] = 'Listes des répertoires'; +$lang['L_HTACCESS24'] = 'Document des erreurs'; +$lang['L_HTACCESS25'] = "Activer 'Rewrite'"; +$lang['L_HTACCESS26'] = 'Refusé / Accepté'; +$lang['L_HTACCESS27'] = 'Redirection'; +$lang['L_HTACCESS28'] = "Journal d'erreur"; +$lang['L_HTACCESS29'] = 'autres exemples et documentations'; +$lang['L_HTACCESS30'] = 'Fournisseur'; +$lang['L_HTACCESS31'] = 'Général'; +$lang['L_HTACCESS32'] = "Attention! Le fichier .htaccess a une influence directe sur le navigateur.
      Lors d'un mauvais emploi les pages ne sont plus accessibles."; +$lang['L_DISABLEDFUNCTIONS'] = 'Fonctions désactivées'; +$lang['L_NOGZPOSSIBLE'] = "Comme Zlib n'est pas installé, vous ne pouvez pas utiliser les fonctions GZip!"; +$lang['L_DELETE_HTACCESS'] = 'Supprimer la protection des répertoires (suppression .htaccess)'; +$lang['L_WRONG_RIGHTS'] = "Impossible d'écrire le fichier ou le répertoire '%s' .
      Les chmods ne sont mal configurés ou le propriétaire n'est pas bon.
      Veuillez vérifier les attributs en utilisant votre logiciel FTP.
      Le fichier ou le répertoire doivent être sur %s."; +$lang['L_CANT_CREATE_DIR'] = "Impossible de créer le répertoire '%s'. Veuillez créer ce répertoire manuellement avec votre logiciel FTP."; +$lang['L_TABLE_TYPE'] = 'Type'; +$lang['L_CHECK'] = 'check'; +$lang['L_OS'] = 'Operating system'; +$lang['L_MOD_VERSION'] = 'MyOOS [Dumper] - Version'; +$lang['L_NEW_MOD_VERSION'] = 'Nouvelle version' ; +$lang['L_NEW_MOD_VERSION_INFO'] = 'Il y a une nouvelle version de MyOOS [Dumper] disponible'; +$lang['L_UPDATED_IMPORTANT'] = 'Important : veuillez sauvegarder vos fichiers avant la mise à jour.'; +$lang['L_UPDATE'] = 'Mettre à jour maintenant'; +$lang['L_MYSQL_VERSION'] = 'MySQL-Version'; +$lang['L_PHP_VERSION'] = 'PHP-Version'; +$lang['L_MAX_EXECUTION_TIME'] = 'Max execution time'; +$lang['L_PHP_EXTENSIONS'] = 'PHP-Extensions'; +$lang['L_MEMORY'] = 'Memory'; +$lang['L_FILE_MISSING'] = "le fichier n'a pas été trouvé"; +$lang['L_INSTALLING_UPDATES'] = 'Installation des mises à jour' ; +$lang['L_UPDATE_SUCCESSFUL'] = 'Mise à jour réussie' ; +$lang['L_UPDATE_FAILED'] = 'Update failed' ; +$lang['L_UP_TO_DATE'] = 'La version actuelle est à jour' ; diff --git a/msd/language/fr/lang_restore.php b/msd/language/fr/lang_restore.php new file mode 100644 index 0000000..7471d47 --- /dev/null +++ b/msd/language/fr/lang_restore.php @@ -0,0 +1,19 @@ +%d tables ont été créés."; +$lang['L_FILE_MISSING'] = "le fichier n'a pas été trouvé"; +$lang['L_RESTORE_DB'] = "Base de données '%s' sur le serveur '%s'."; +$lang['L_RESTORE_COMPLETE'] = '%s tables ont été créés.'; +$lang['L_RESTORE_RUN1'] = "
      Jusqu'à présent %s de %s enregistrements ont été enregistrés."; +$lang['L_RESTORE_RUN2'] = "
      En ce moment la table '%s' avec ces enregistrements est en cours de traitement.

      "; +$lang['L_RESTORE_COMPLETE2'] = '%s enregistrements ont été enregistrés.'; +$lang['L_RESTORE_TABLES_COMPLETED'] = "Jusqu'à présent %d de %d tables ont été créés."; +$lang['L_RESTORE_TOTAL_COMPLETE'] = '
      Toutes nos félicitations.

      La base de données a été restaurée complètement.
      Tous les fichiers de la copie de sauvegarde ont été enregistrés avec succès dans la base de données.

      Restauration terminée. :-)'; +$lang['L_DB_SELECT_ERROR'] = "
      Erreur:
      Choix de la base de données '"; +$lang['L_DB_SELECT_ERROR2'] = "' échoué!"; +$lang['L_FILE_OPEN_ERROR'] = "Erreur: Le fichier n'a pas pu être ouvert."; +$lang['L_PROGRESS_OVER_ALL'] = 'Progression totale'; +$lang['L_BACK_TO_OVERVIEW'] = 'Aperçu général des bases de données'; +$lang['L_RESTORE_RUN0'] = "
      Jusqu'à présent %s enregistrements ont été enregistrés avec succès."; +$lang['L_UNKNOWN_SQLCOMMAND'] = 'Commande SQL inconnue'; +$lang['L_NOTICES'] = 'Notices'; diff --git a/msd/language/fr/lang_sql.php b/msd/language/fr/lang_sql.php new file mode 100644 index 0000000..5670acd --- /dev/null +++ b/msd/language/fr/lang_sql.php @@ -0,0 +1,190 @@ +%s lignes exportées'; +$lang['L_CSV_FIELDCOUNT_NOMATCH'] = 'Le nombre de tables ne correspondent pas au nombre de données à importer (%d à la place de %d).'; +$lang['L_CSV_FIELDSLINES'] = '%d champs calculer, en tout %d lignes'; +$lang['L_CSV_ERRORCREATETABLE'] = 'Erreur lors de la création de la table `%s` !'; +$lang['L_FM_UPLOADFILEREQUEST'] = 'Veuillez entrer un fichier.'; +$lang['L_CSV_NODATA'] = 'Aucun fichier à importer a été trouvé!'; +$lang['L_SQLLIB_GENERALFUNCTIONS'] = 'Fonctions générales'; +$lang['L_SQLLIB_RESETAUTO'] = 'Remettre valeur par défaut'; +$lang['L_SQLLIB_BOARDS'] = 'Boards'; +$lang['L_SQLLIB_DEACTIVATEBOARD'] = 'Désactiver Board'; +$lang['L_SQLLIB_ACTIVATEBOARD'] = 'Activer Board'; +$lang['L_SQL_NOTABLESSELECTED'] = "Aucune table n'est sélectionnée !"; +$lang['L_TOOLS'] = 'Outils'; +$lang['L_TOOLS_TOOLBOX'] = 'Sélectionner la base de données / Fonctions de la base de données / Import et export'; +$lang['L_SQL_OPENFILE'] = 'Ouvrir le fichier SQL'; +$lang['L_SQL_OPENFILE_BUTTON'] = 'Envoyer'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Taille maximale du fichier'; +$lang['L_SQL_SEARCH'] = 'Rechercher'; +$lang['L_SQL_SEARCHWORDS'] = 'rechercher le(s) mot(s)'; +$lang['L_START_SQL_SEARCH'] = 'Commencer la recherche'; +$lang['L_RESET_SEARCHWORDS'] = 'Purge des critères de recherche'; +$lang['L_SEARCH_OPTIONS'] = 'Options de recherches'; +$lang['L_SEARCH_RESULTS'] = 'La recherche pour "%s" dans la table "%s" a donné les résultats suivants.'; +$lang['L_SEARCH_NO_RESULTS'] = 'Aucun résultat pour la recherche de "%s" dans la table "%s" !'; +$lang['L_NO_ENTRIES'] = 'La table "%s" est vide ou ne contient aucune entrée.'; +$lang['L_SEARCH_ACCESS_KEYS'] = "Parcourir: Vers l'avant:ALT+V, Vers l'arrière:ALT+C"; +$lang['L_SEARCH_OPTIONS_OR'] = 'une colonne doit contenir au moins un critère de recherche (OU-Recherche)'; +$lang['L_SEARCH_OPTIONS_CONCAT'] = "une ligne doit contenir tous les critères de recherche, mais ceux-ci peuvent toutefois être indiqués dans n'importe quelle colonne (Peut prendre du temps de calcul !)"; +$lang['L_SEARCH_OPTIONS_AND'] = 'une colonne doit contenir tous les critères de recherche (ET-Recherche)'; +$lang['L_SEARCH_IN_TABLE'] = 'Rechercher dans la table'; +$lang['L_ERROR_NO_FIELDS'] = 'Search error: it could not be determined which fields the table "%s" has!'; +$lang['L_SQL_EDIT_TABLESTRUCTURE'] = 'Modifier la structure des tables'; +$lang['L_DEFAULT_CHARSET'] = 'Jeux de caractères par défaut'; +$lang['L_TITLE_KEY_PRIMARY'] = 'Clé primaire'; +$lang['L_TITLE_KEY_UNIQUE'] = 'Clé unique'; +$lang['L_TITLE_INDEX'] = 'Index'; +$lang['L_TITLE_KEY_FULLTEXT'] = 'Clé texte plein'; +$lang['L_TITLE_NOKEY'] = 'Aucune clé'; +$lang['L_TITLE_SEARCH'] = 'Rechercher'; +$lang['L_TITLE_MYSQL_HELP'] = 'Documentation MySQL'; +$lang['L_TITLE_UPLOAD'] = 'Envoyer un fichier SQL'; +$lang['L_PRIMARYKEY_DELETED'] = 'Primary key deleted'; +$lang['L_PRIMARYKEY_NOTFOUND'] = 'Primary key not found'; +$lang['L_PRIMARYKEYS_CHANGED'] = 'Primary keys changed'; +$lang['L_PRIMARYKEYS_CHANGINGERROR'] = 'Error changing primary keys'; +$lang['L_SQL_VIEW_COMPACT'] = 'View: compact'; +$lang['L_SQL_VIEW_STANDARD'] = 'View: standard'; +$lang['L_FIELDS_OF_TABLE'] = 'Fields of table'; +$lang['L_ENGINE'] = 'Engine'; +$lang['L_USERNAME'] = 'Username'; +$lang['L_PASSWORD'] = 'Password'; +$lang['L_PASSWORD_REPEAT'] = 'Password (repeat)'; +$lang['L_INFO_SIZE'] = 'Taille'; +$lang['L_TABLE_TYPE'] = 'Type'; +$lang['L_KEY_DELETED'] = 'Index deleted'; +$lang['L_KEY_DELETEERROR'] = 'Error deleting index'; +$lang['L_KEY_ADDED'] = 'Index added'; +$lang['L_KEY_ADDERROR'] = 'Error adding index'; diff --git a/msd/language/it/help.html b/msd/language/it/help.html new file mode 100644 index 0000000..0590ed4 --- /dev/null +++ b/msd/language/it/help.html @@ -0,0 +1,149 @@ +
      +

      MyOOS [Dumper] basato su MySQLDumper 1.24.4

      + +

      Su questo progetto

      +

      MyOOS [Dumper] è una versione migliorata di MySQLDumper 1.24.4 (24 gennaio 2011). Questo ulteriore sviluppo tiene conto dello sviluppo di PHP. +

      MyOOS [Dumper] si occupa principalmente di stabilità, sicurezza e gestione. Ma è anche incluso un modello attraente, che può essere modificato e adattato alle proprie esigenze.

      + + +

      MyOOS [Dumper] è un programma di backup per database MySQL, scritto in PHP e Perl. Con esso, le copie di backup dei dati (negozio, blog, ecc.) possono essere create e anche ripristinate se necessario. Specialmente per gli spazi web senza accesso alla shell, MyOOS [Dumper] si offre come un'alternativa sensata.

      + +

      L'idea di MySQLDumper è venuta a Daniel Schlichtholz. Ha aperto il forum MySQLDumper nel 2004, dove i programmatori hanno scritto nuovi script ed esteso quelli esistenti.

      + + + +

      Lista dei desideri / Attrazioni future

      . +

      Hai qualche suggerimento per migliorare? Sentitevi liberi di contattare il team di sviluppo tramite il forum https://foren.myoos.de/viewforum.php?f=41.

      + + +

      Contribuire

      +

      Se vuoi aiutarci a migliorare il progetto MyOOS, accogliamo le tue richieste di pull via GitHub qui.

      +https://github.com/r23/MyOOS-Dumper/ + + +

      Sostegno finanziario

      +

      Puoi usare PayPal Me
      . +https://www.paypal.com/paypalme/r23de?locale.x=de_DE

      + +

      o tramite il codice QR
      . +Supporto finanziario per MyOOS [Dumper]

      + +Invia denaro al progetto MyOOS.
      + +

      Speriamo che questo progetto ti piaccia.

      La squadra di MyOOS [Dumper]

      + +MyOOS [Dumper]
      + +

      Aiuto di MyOOS [Dumper]

      + +

      Scaricare

      +

      Puoi sempre ottenere le ultime versioni da GitHub
      . +https://github.com/r23/MyOOS-Dumper/releases

      + + +

      Requisiti di sistema

      +

      Lo script funziona su qualsiasi server (Windows, Linux, ...)
      +con PHP >= versione 7.4 con supporto GZip, MySQL (versione 4.1 o superiore), JavaScript (deve essere abilitato).

      +

      Copia la cartella mod dall'archivio MyOOS in una cartella di lavoro separata.

      + +

      Installazione

      +L'installazione è semplice. +

      Dall'archivio MyOOS, copiate la cartella mod in qualsiasi cartella.
      +Carica tutti i file dalla cartella mod al tuo server web. (per esempio al livello più basso in [directory web del server/]mod)
      . +... fatto!
      +Ora puoi richiamare MyOOS [Dumper] nel tuo browser web andando su "https://example.com/mod/"
      . +per completare l'installazione. Basta seguire le istruzioni. +
      Nota:
      Se sul tuo server lo script non è autorizzato a creare directory,
      +dovrai farlo manualmente, poiché MyOOS [Dumper] memorizza i dati in directory. +directory.
      +Lo script termina con una dichiarazione appropriata! +Dopo aver creato le directory (secondo il suggerimento), funziona normalmente e senza restrizioni. + +

      Istruzioni per lo script Perl

      . +La maggior parte ha una directory cgi-bin dove il perl può essere eseguito.
      +Questo è di solito accessibile dal browser tramite http://www.example.com/cgi-bin/.
      +
      +In questo caso, eseguite i seguenti passi:

      1. + +1. richiamare la pagina del Backup in MyOOS [Dumper] e cliccare su "Backup Perl".
      +2. copiare il percorso dietro la voce in crondump.pl per $absolute_path_of_configdir:.
      +3. aprire il file "crondump.pl" nell'editor.
      +4. inserire il percorso copiato lì a absolute_path_of_configdir (senza spazi).
      +5. salvare crondump.pl.
      +Copia crondump.pl, perltest.pl e simpletest.pl nella directory cgi-bin (modalità ascii in FTP). +7. dare ai file i permessi 755.
      +7b. Se si desidera il finale cgi, cambiare il finale di tutti e 3 i file da pl -> cgi (rinominare).
      +Richiamate la configurazione in MyOOS [Dumper]. +9. selezionare la pagina Cronscript.
      +10. cambiare il percorso di esecuzione di Perl in /cgi-bin/ .
      +10b. Se gli script hanno .pl, cambia l'estensione del file in .cgi .
      +11. salvare la configurazione.

      + +Fatto, gli script possono ora essere chiamati dalla pagina di backup.

      + +Per coloro che possono eseguire Perl in tutte le directory, i seguenti passi sono sufficienti:

      1. + +1. richiamare la pagina Backup in MyOOS [Dumper].
      +Copiare il percorso dietro la voce in crondump.pl per $absolute_path_of_configdir:.
      +Aprite il file "crondump.pl" nell'editor.
      +4. inserire il percorso copiato lì a absolute_path_of_configdir (senza spazi).
      +5. salvare crondump.pl .
      +6. Date ai file i permessi 755.
      +6b. Se si desidera il finale cgi, cambiare il finale di tutti e 3 i file da pl -> cgi (rinominare).
      +(ev. 10b+11 da sopra)
      +
      + +Gli utenti di Windows devono cambiare la prima riga di tutti gli script, c'è il percorso di Perl. Esempio:
      +invece di: #!/usr/bin/perl -w
      +ora: #!C:_usr/bin/perl.exe -w
      + +

      Operazione

        + +
        Menu
        . +Nell'elenco di selezione di cui sopra si imposta il database.
        +Tutte le azioni si riferiscono al database impostato qui. + +
        Casa
        +Qui puoi trovare informazioni sul tuo sistema, le varie versioni installate e i dettagli dei database configurati. +versioni installate e dettagli sui database configurati.
        +Se cliccate sul nome del database, vedrete un elenco delle tabelle con il numero di voci +con il numero di voci, la dimensione e la data dell'ultimo aggiornamento. + +
        Configurazione
        +Qui puoi modificare la tua configurazione, salvarla o ripristinare la configurazione iniziale. +ripristinare la configurazione iniziale. +

          +
        • Basi di dati configurate: la lista delle basi di dati configurate. Il database attivo è elencato in grigio.
        • +
        • Table Prefix: qui puoi specificare un prefisso (per ogni database). Questo è un filtro che considera solo le tabelle che iniziano con questo prefisso quando si fa il dumping (ad esempio tutte le tabelle che iniziano con "phpBB_"). Se vuoi che tutte le tabelle di questo database siano salvate, lascia semplicemente il campo vuoto. +
        • compressione GZip: Qui puoi attivare la compressione. Si raccomanda di attivarlo, perché i file diventano molto più piccoli e lo spazio di archiviazione è sempre scarso. +
        • Email con file di dump: Se questa opzione è attivata, un'email con il dump come allegato viene inviata dopo che il backup è stato completato (attenzione, la compressione dovrebbe essere assolutamente attiva, altrimenti l'allegato sarà troppo grande e potrebbe non essere inviato!) +
        • Indirizzo e-mail: Indirizzo del destinatario dell'e-mail. +
        • Mittente dell'email: questo indirizzo appare come mittente nell'email.
        • +
        • Trasferimento FTP: Se questa opzione è attivata, il file di backup sarà inviato via FTP dopo che il backup è stato completato.
        • +
        • ServerFTP: L'indirizzo del server FTP (es. ftp.mybackups.de).
        • +
        • Porta del server FTP: La porta del server FTP (di solito 21).
        • +
        • Utente FTP: Il nome utente dell'account FTP.
        • +
        • Password FTP: La password dell'account FTP.
        • +
        • Cartella di caricamentoFTP: La directory dove il file di backup dovrebbe andare (i permessi di caricamento devono esistere!).
        • +
        • Eliminazione automatica dei backup: Se questa opzione è attivata, i vecchi backup saranno eliminati automaticamente secondo le seguenti regole.
        • +
        • Numero di file di backup: Un valore > 0 cancella tutti i file di backup tranne il numero specificato qui.
        • +
        • Lingua: qui si imposta la lingua per l'interfaccia.
        • +
        + +
        Amministrazione
        +Qui è dove vengono eseguite le azioni effettive.
        +Vengono visualizzati tutti i file nella directory di backup. +Per le azioni "Restore" e "Delete" deve essere selezionato un file. +
          +
        • Ripristino: Questo aggiorna il database con il file di backup selezionato.
        • +
        • Elimina: Questo ti permette di eliminare il file di backup selezionato.
        • +
        • Avvia nuovo backup: Qui si avvia un nuovo backup (dump) secondo i parametri impostati nella configurazione. +
        + +
        Log
        +Qui potete vedere e cancellare le voci di registro. +
        Crediti / Aiuto
        +questa pagina. +
      + +Tradotto con www.DeepL.com/Translator (versione gratuita) \ No newline at end of file diff --git a/msd/language/it/lang.php b/msd/language/it/lang.php new file mode 100644 index 0000000..2cd576b --- /dev/null +++ b/msd/language/it/lang.php @@ -0,0 +1,109 @@ +non disponibile'; +$lang['L_VOM'] = 'dal'; +$lang['L_MYSQLVARS'] = 'Variabili Mysql'; +$lang['L_MYSQLSYS'] = 'Comandi Mysql'; +$lang['L_STATUS'] = 'Stato'; +$lang['L_PROZESSE'] = 'Processi'; +$lang['L_INFO_NOVARS'] = 'nessuna variabile disponibile'; +$lang['L_INHALT'] = 'Contenuto'; +$lang['L_INFO_NOSTATUS'] = 'stato non disponibile'; +$lang['L_INFO_NOPROCESSES'] = 'nessun processo in corso'; +$lang['L_FM_FREESPACE'] = 'Memoria disponibile sul server'; +$lang['L_LOAD_DATABASE'] = 'Ricarica database'; +$lang['L_HOME'] = 'Pagina iniziale'; +$lang['L_CONFIG'] = 'Configurazione'; +$lang['L_DUMP'] = 'Backup'; +$lang['L_RESTORE'] = 'Ripristina'; +$lang['L_FILE_MANAGE'] = 'Amministrazione file'; +$lang['L_LOG'] = 'Log'; +$lang['L_CHOOSE_DB'] = 'Seleziona database'; +$lang['L_CREDITS'] = 'Crediti/Aiuto'; +$lang['L_MULTI_PART'] = 'Backup in più parti'; +$lang['L_LOGFILENOTWRITABLE'] = 'Il logfile non può essere scritto!'; +$lang['L_SQL_ERROR1'] = 'Errore nella richiesta:'; +$lang['L_SQL_ERROR2'] = 'MySQL dice:'; +$lang['L_UNKNOWN'] = 'sconosciuto'; +$lang['L_UNKNOWN_NUMBER_OF_RECORDS'] = 'sconosciuto'; +$lang['L_OK'] = 'OK.'; +$lang['L_CRON_COMPLETELOG'] = 'Salvare tutte le operazioni'; +$lang['L_NO'] = 'no'; +$lang['L_CREATE_DATABASE'] = 'Crea nuovo database'; +$lang['L_EXPORTFINISHED'] = 'Esportazione completata.'; +$lang['L_SQL_BROWSER'] = 'SQL-Browser'; +$lang['L_SERVER'] = 'Server'; +$lang['L_MYSQL_CONNECTION_ENCODING'] = 'Codifica standard dei MySQL-Servers'; +$lang['L_TITLE_SHOW_DATA'] = 'Visualizza dati'; +$lang['L_PRIMARYKEY_CONFIRMDELETE'] = 'Vuoi cancellare veramente la chiave primaria?'; +$lang['L_SETPRIMARYKEYSFOR'] = 'Inserisci nuova chiave primaria per la tabella'; +$lang['L_PRIMARYKEY_FIELD'] = 'Campo chiave primaria'; +$lang['L_PRIMARYKEYS_SAVE'] = 'Salva chiave primaria'; +$lang['L_CANCEL'] = 'Cancella'; +$lang['L_VISIT_HOMEPAGE'] = 'Visit Homepage'; +$lang['L_SECONDS'] = 'Seconds'; +$lang['L_BACKUPS'] = 'Backups'; +$lang['L_MINUTES'] = 'Minutes'; +$lang['L_PAGE_REFRESHS'] = 'Page refreshs'; +$lang['L_MINUTE'] = 'Minute'; +$lang['L_SETKEYSFOR'] = 'Set new indexes for table'; +$lang['L_KEY_CONFIRMDELETE'] = 'Really delete index?'; diff --git a/msd/language/it/lang_config_overview.php b/msd/language/it/lang_config_overview.php new file mode 100644 index 0000000..50df579 --- /dev/null +++ b/msd/language/it/lang_config_overview.php @@ -0,0 +1,126 @@ +%s
      in %s'; +$lang['L_FTP'] = 'FTP'; +$lang['L_SFTP_SEND_TO'] = 'a %s
      in %s'; +$lang['L_SFTP'] = 'SFTP'; +$lang['L_EMAIL_CC'] = "Destinatario dell'e-mail in CC"; +$lang['L_NAME'] = 'Nome'; +$lang['L_CONFIRM_CONFIGFILE_DELETE'] = 'Sicuro di voler cancellare il file di configurazione %s ?'; +$lang['L_ERROR_DELETING_CONFIGFILE'] = 'Errore: il file di configurazione %s non può essere cancellato!'; +$lang['L_SUCCESS_DELETING_CONFIGFILE'] = 'Il file di configurazione %s è stato cancellato con successo.'; +$lang['L_SUCCESS_CONFIGFILE_CREATED'] = 'Il file di configurazione %s è stato creato con successo.'; +$lang['L_ERROR_CONFIGFILE_NAME'] = 'Il nome del file "%s" contiene dei caratteri non ammessi.'; +$lang['L_CREATE_CONFIGFILE'] = 'Crea un nuovo file di configurazione '; +$lang['L_ERROR_LOADING_CONFIGFILE'] = 'Il file di configurazione "%s" non può essere caricato.'; +$lang['L_BACKUP_DBS_PHP'] = 'database di cui fare il backup (PHP)'; +$lang['L_BACKUP_DBS_PERL'] = 'database di cui fare il backup (PERL)'; +$lang['L_CRON_COMMENT'] = 'Inserisci commento'; +$lang['L_AUTODETECT'] = 'riconoscimento automatico'; diff --git a/msd/language/it/lang_dump.php b/msd/language/it/lang_dump.php new file mode 100644 index 0000000..47fc76b --- /dev/null +++ b/msd/language/it/lang_dump.php @@ -0,0 +1,57 @@ +%s` nel database.'; +$lang['L_DUMP_ENDERGEBNIS'] = 'Sono state salvate %s tabelle con %s record.
      '; +$lang['L_MAILERROR'] = 'Spiacente, nell`inviare l`e-mail si è verificato un errore!'; +$lang['L_EMAILBODY_ATTACH'] = 'Nell`allegato trovi il backup del tuo database MySQL.
      Backup del database `%s` +

      Il seguente file è stato creato:

      %s

      Buona giornata

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_MP_NOATTACH'] = 'È stato creato un backup multipart.
      Il backup non viene spedito come allegato!
      Backup del database `%s` +

      I seguenti file sono stati creati:

      %s


      Buona giornata

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_MP_ATTACH'] = 'È stato creato un backup multipart.
      Il backup viene spedito con e-mail separate, con allegati!
      Backup del database`%s` +

      I seguenti file sono stati creati:

      %s


      Buona giornata

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_FOOTER'] = '`

      Buona giornata

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_TOOBIG'] = 'Il backup supera la grandezza massima di %s perciò i file non sono stati allegati.
      Backup del database `%s` +

      I seguenti file sono stati creati:

      %s +

      Buona giornata

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_NOATTACH'] = 'È stato creato un backup.
      Il backup non viene spedito come allegato!
      Backup del database `%s` +

      I seguenti file sono stati creati:

      %s +

      Buona giornata

      MyOOS [Dumper]
      '; +$lang['L_EMAIL_ONLY_ATTACHMENT'] = 'Allegati del backup'; +$lang['L_TABLESELECTION'] = 'Seleziona tabelle'; +$lang['L_SELECTALL'] = 'seleziona tutto'; +$lang['L_DESELECTALL'] = 'selezionare tutto'; +$lang['L_STARTDUMP'] = 'Fai partire il backup'; +$lang['L_LASTBUFROM'] = 'ultimo update dal'; +$lang['L_NOT_SUPPORTED'] = 'Questo backup non supporta questa funzione.'; +$lang['L_MULTIDUMP'] = 'Multidump: Sono stati salvati %d database.'; +$lang['L_FILESENDFTP'] = 'Invio del file via FTP in corso... un attimo di pazienza prego. '; +$lang['L_FTPCONNERROR'] = 'Connessione FTP non riuscita! Connessione con '; +$lang['L_FTPCONNERROR1'] = 'come utente '; +$lang['L_FTPCONNERROR2'] = 'non possibile'; +$lang['L_FTPCONNERROR3'] = 'FTP-Upload errato! '; +$lang['L_FTPCONNECTED1'] = 'Connesso con '; +$lang['L_FTPCONNECTED2'] = 'sul '; +$lang['L_FTPCONNECTED3'] = 'trasferimento completato con successo'; +$lang['L_FILESENDSFTP'] = 'Invio del file via SFTP in corso... un attimo di pazienza prego. '; +$lang['L_SFTPCONNERROR'] = 'Connessione SFTP non riuscita! Connessione con '; +$lang['L_NR_TABLES_SELECTED'] = '- con %s tabelle selezionate'; +$lang['L_NR_TABLES_OPTIMIZED'] = '%s tabelle sono state ottimizzate.'; +$lang['L_DUMP_ERRORS'] = '

      %s errori riscontrati: controllare gli errori

      '; +$lang['L_FATAL_ERROR_DUMP'] = "Errore fatale: l'istruzione di creazione della tabella '%s' nel database '%s' non è leggibile!
      Controlla se ci sono dei errori nella tabella."; diff --git a/msd/language/it/lang_filemanagement.php b/msd/language/it/lang_filemanagement.php new file mode 100644 index 0000000..08fb461 --- /dev/null +++ b/msd/language/it/lang_filemanagement.php @@ -0,0 +1,75 @@ +%s"'; +$lang['L_DELETE_FILE_ERROR'] = 'Non è stato possibile cancellare il file "%s"!'; +$lang['L_FM_DUMP_HEADER'] = 'Backup'; +$lang['L_DOCRONBUTTON'] = 'Esegui Cronscript Perl'; +$lang['L_DOPERLTEST'] = 'Prova il modulo Perl'; +$lang['L_DOSIMPLETEST'] = 'Prova Perl'; +$lang['L_PERLOUTPUT1'] = 'Registrazione in crondump.pl per absolute_path_of_configdir'; +$lang['L_PERLOUTPUT2'] = 'Url per il Browser oppure per Cronjob esterno'; +$lang['L_PERLOUTPUT3'] = 'Linea di commandi nella Shell oppure per il Crontab'; +$lang['L_RESTORE_OF_TABLES'] = 'Ripristino di tabelle specifiche'; +$lang['L_CONVERTER'] = 'Convertitore Backup'; +$lang['L_CONVERT_FILE'] = 'file da convertire'; +$lang['L_CONVERT_FILENAME'] = 'Nome del file di destinazione (senza estensione)'; +$lang['L_CONVERTING'] = 'Conversione'; +$lang['L_CONVERT_FILEREAD'] = "Leggi file '%s'"; +$lang['L_CONVERT_FINISHED'] = "Conversione eseguita, '%s' sono stati scritti con successo."; +$lang['L_NO_MOD_BACKUPFILE'] = 'Backup di altri programmi'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Grandezza massima del file'; +$lang['L_MAX_UPLOAD_SIZE_INFO'] = 'Se il tuo file di backup è piu grande del limite impostato, allora lo devi caricare tramite FTP nella cartella "work/backup". Dopo verrà visualizzato e potrà essere scelto per il ripristino.'; +$lang['L_ENCODING'] = 'codifica'; +$lang['L_FM_CHOOSE_ENCODING'] = 'Scegli la codifica del file di backup'; +$lang['L_CHOOSE_CHARSET'] = 'MyOOS [Dumper] non ha rilevato automaticamente il codice del seti di caratteri utilizzato nel file di backup creato in precedenza.
      Devi inserire manualmente il set di caratteri standard con cui è stato salvato questo backup.
      Dopo aver fatto questo, MyOOS [Dumper] effettuerà la connessione verso il MySQL-Server contenente il set di caretteri scelto e avvierà il ripristino dei dati. Se dopo il ripristono si presentassero problemi nella visualizzazione dei caratteri speciali, sarà opportuno ripetere la procedura di ripistino scegliendo un altro set di caratteri.
      Buona fortuna.;)'; +$lang['L_DOWNLOAD_FILE'] = 'Scarica file'; +$lang['L_BACKUP_NOT_POSSIBLE'] = 'A backup of the system database `%s` is not possible!'; diff --git a/msd/language/it/lang_help.php b/msd/language/it/lang_help.php new file mode 100644 index 0000000..515066a --- /dev/null +++ b/msd/language/it/lang_help.php @@ -0,0 +1,30 @@ +L`installazione è finita --> fai partire MyOOS [Dumper]
      '; +$lang['L_INSTALL_TOMENU'] = 'Ritorna al Menu principale'; +$lang['L_INSTALLMENU'] = 'Menu principale'; +$lang['L_STEP'] = 'Passo'; +$lang['L_INSTALL'] = 'Installazione'; +$lang['L_UNINSTALL'] = 'Disinstallare'; +$lang['L_TOOLS'] = 'Tools'; +$lang['L_EDITCONF'] = 'Modifica la configurazione'; +$lang['L_OSWEITER'] = 'continuare senza salvare'; +$lang['L_ERRORMAN'] = 'Errore nel salvataggio della configurazione!
      edita il file per favore'; +$lang['L_MANUELL'] = 'manualmente'; +$lang['L_CREATEDIRS'] = 'crea directory'; +$lang['L_INSTALL_CONTINUE'] = 'continua con la installazione'; +$lang['L_CONNECTTOMYSQL'] = 'connessione a MySQL '; +$lang['L_DBPARAMETER'] = 'Parametri database'; +$lang['L_CONFIGNOTWRITABLE'] = 'Il file "config.php" non è scrivibile. Metti per favore i permessi per scrivere (ad esempio: 0777), cosi MyOOS [Dumper] può salvare la tua configurazione. '; +$lang['L_DBCONNECTION'] = 'Connessione database'; +$lang['L_CONNECTIONERROR'] = 'Errore: impossibile connettersi.'; +$lang['L_CONNECTION_OK'] = 'Connessione al database effettuata.'; +$lang['L_SAVEANDCONTINUE'] = 'Salvare e continuare l`installazione'; +$lang['L_CONFBASIC'] = 'Configurazione di base'; +$lang['L_INSTALL_STEP2FINISHED'] = 'I parametri del database sono stati salvati.'; +$lang['L_INSTALL_STEP2_1'] = 'Continuare la installazione con i parametri standard'; +$lang['L_LASTSTEP'] = 'Fine della installazione'; +$lang['L_FTPMODE'] = 'Creazione directory tramite FTP (safe-mode)'; +$lang['L_IDOMANUAL'] = 'Creare le directory manualmente'; +$lang['L_DOFROM'] = 'partendo dal'; +$lang['L_FTPMODE2'] = 'Creare le directory tramite FTP:'; +$lang['L_CONNECT'] = 'connetti'; +$lang['L_DIRS_CREATED'] = 'Le directory sono state create correttamente con i permessi appropriati.'; +$lang['L_CONNECT_TO'] = 'connetti a'; +$lang['L_CHANGEDIR'] = 'cambia directory'; +$lang['L_CHANGEDIRERROR'] = 'Impossibile cambiare directory'; +$lang['L_FTP_OK'] = 'I parametri FTP sono corretti'; +$lang['L_CREATEDIRS2'] = 'Creare directory'; +$lang['L_FTP_NOTCONNECTED'] = 'Connessione FTP fallita!'; +$lang['L_CONNWITH'] = 'Connessione con'; +$lang['L_ASUSER'] = 'come utente'; +$lang['L_NOTPOSSIBLE'] = 'impossibile'; +$lang['L_DIRCR1'] = 'crea directory di lavoro'; +$lang['L_DIRCR2'] = 'crea directory di backup'; +$lang['L_DIRCR4'] = 'crea directory di log'; +$lang['L_DIRCR5'] = 'crea elenco di configurazione'; +$lang['L_INDIR'] = 'sono nella directory'; +$lang['L_CHECK_DIRS'] = 'verifica directory'; +$lang['L_DISABLEDFUNCTIONS'] = 'Funzioni disabilitate'; +$lang['L_NOFTPPOSSIBLE'] = 'Funzioni FTP non disponibili!'; +$lang['L_NOGZPOSSIBLE'] = 'Funzione di compressione non disponibile!'; +$lang['L_UI1'] = 'Tutte le directory di lavoro inclusi gli eventuali backups presenti verranno cancellati.'; +$lang['L_UI2'] = 'Sei sicuro?'; +$lang['L_UI3'] = 'no'; +$lang['L_UI4'] = 'si'; +$lang['L_UI5'] = 'cancella directory di lavoro'; +$lang['L_UI6'] = 'è stato cancellato tutto con successo.'; +$lang['L_UI7'] = 'cancella la direcotry degli script'; +$lang['L_UI8'] = 'livello precedente'; +$lang['L_UI9'] = 'Si è verificato un errore, non è stato possibile cancellare

      Errore nella directory'; +$lang['L_IMPORT'] = 'Importare configurazione'; +$lang['L_IMPORT3'] = 'La configurazione è stata caricata ...'; +$lang['L_IMPORT4'] = 'La configurazione è stata salvata.'; +$lang['L_IMPORT5'] = 'Eseguire MyOOS [Dumper]'; +$lang['L_IMPORT6'] = 'Menu di installazione'; +$lang['L_IMPORT7'] = 'Caricare configurazione'; +$lang['L_IMPORT8'] = 'torna al caricamento (upload)'; +$lang['L_IMPORT9'] = 'Questo non è un backup di configurazione!'; +$lang['L_IMPORT10'] = 'La configurazione è stata ripristinata con successo ...'; +$lang['L_IMPORT11'] = 'Errore: Si sono verificati degli errori nel salvataggio delle istruzioni sql'; +$lang['L_IMPORT12'] = 'Errore: Si sono verificati degli errori nel salvataggio di config.php'; +$lang['L_INSTALL_HELP_PORT'] = '(vuoto = porta-standard)'; +$lang['L_INSTALL_HELP_SOCKET'] = '(vuoto = Socket-standard)'; +$lang['L_TRYAGAIN'] = 'riprova'; +$lang['L_SOCKET'] = 'Socket'; +$lang['L_PORT'] = 'Porta'; +$lang['L_FOUND_DB'] = 'database trovato:'; +$lang['L_FM_FILEUPLOAD'] = 'Carica il file'; +$lang['L_PASS'] = 'Password'; +$lang['L_NO_DB_FOUND_INFO'] = 'La connesione con il database è stata effettuata con successo.
      I tuoi dati di connessione sono stati accettati dal MySQL-Server.
      Purtroppo MyOOS [Dumper] non ha trovato alcun database.
      Il riconoscimento automatico per il programma è purtroppo bloccato dal provider.
      Appena hai finito l`installazione devi andare nel menu "Configurazione" "visualizza parametri di connessione" e inserire il nome del tuo database.'; +$lang['L_ENTER_DB_INFO'] = 'First click the button "Connect to MySQL". Only if no database could be detected you need to provide a database name here.'; diff --git a/msd/language/it/lang_log.php b/msd/language/it/lang_log.php new file mode 100644 index 0000000..37cd58c --- /dev/null +++ b/msd/language/it/lang_log.php @@ -0,0 +1,7 @@ +Crea per favore questi file manualmente con il seguente contenuto'; +$lang['L_HTACC_CHECK_ERROR'] = 'It could not be checked whether the program is protected!
      The simulated external access could not be carried out.'; +$lang['L_HTACC_NOT_NEEDED'] = 'The program is protected by higher-level authorizations; local directory protection is not required.'; +$lang['L_HTACC_COMPLETE'] = 'The program is protected, the directory protection is complete.'; +$lang['L_HTACC_INCOMPLETE'] = 'The program is not protected, the directory protection is incomplete!'; +$lang['L_HTACC_PROPOSED'] = 'The program is not protected, directory protection is strongly recommended!'; +$lang['L_HTACC_EDIT'] = 'edit .htaccess'; +$lang['L_HTACCESS18'] = 'crea .htaccess in '; +$lang['L_HTACCESS19'] = 'ricarica '; +$lang['L_HTACCESS20'] = 'Esegui script'; +$lang['L_HTACCESS21'] = 'aggiungi fornitore(provider)'; +$lang['L_HTACCESS22'] = 'Rendi eseguibile'; +$lang['L_HTACCESS23'] = 'Lista directory'; +$lang['L_HTACCESS24'] = 'Documento errori'; +$lang['L_HTACCESS25'] = 'Attivare rewrite'; +$lang['L_HTACCESS26'] = 'Negare / Permettere'; +$lang['L_HTACCESS27'] = 'Redirect'; +$lang['L_HTACCESS28'] = 'Log degli errori'; +$lang['L_HTACCESS29'] = 'Altri esempi e documentazioni'; +$lang['L_HTACCESS30'] = 'Provider'; +$lang['L_HTACCESS31'] = 'Generale'; +$lang['L_HTACCESS32'] = 'Attenzione! .htaccess ha effetto direttamente sul browser.
      Se viene usato in maniera scorretta le pagine non saranno piu accessibili.'; +$lang['L_DISABLEDFUNCTIONS'] = 'Funzione disabilitata'; +$lang['L_NOGZPOSSIBLE'] = 'Poiché Zlib non è installato non puoi usare la funzione GZip!'; +$lang['L_DELETE_HTACCESS'] = 'Rimuovi la protezione (cancella .htaccess)'; +$lang['L_WRONG_RIGHTS'] = "Impossibile scrivere il file o la directory '%s' .
      O ha il proprietario sbagliato (Owner) o i diritti sbagliati (Chmod).
      Vi preghiamo di modificare gli attributi correttamente con il vostro programma FTP.
      Il file o la directory ha bisogno dei diritti %s.
      "; +$lang['L_CANT_CREATE_DIR'] = "Impossibile creare la directory '%s'. Crearla manualmente con un programma di FTP."; +$lang['L_TABLE_TYPE'] = 'Tipo'; +$lang['L_CHECK'] = 'check'; +$lang['L_OS'] = 'Operating system'; +$lang['L_MOD_VERSION'] = 'MyOOS [Dumper] - versione'; +$lang['L_NEW_MOD_VERSION'] = 'Nuova versione'; +$lang['L_NEW_MOD_VERSION_INFO'] = 'È disponibile una nuova versione di MyOOS [Dumper].'; +$lang['L_UPDATED_IMPORTANT'] = 'Important: Before updating, please backup your files.'; +$lang['L_UPDATE'] = 'Update now'; +$lang['L_MYSQL_VERSION'] = 'MySQL-Version'; +$lang['L_PHP_VERSION'] = 'PHP-Version'; +$lang['L_MAX_EXECUTION_TIME'] = 'Max execution time'; +$lang['L_PHP_EXTENSIONS'] = 'PHP-Extensions'; +$lang['L_MEMORY'] = 'Memory'; +$lang['L_FILE_MISSING'] = 'impossibile trovare il file'; +$lang['L_INSTALLING_UPDATES'] = 'Installazione degli aggiornamenti'; +$lang['L_UPDATE_SUCCESSFUL'] = 'Aggiornamento riuscito'; +$lang['L_UPDATE_FAILED'] = 'Aggiornamento fallito'; +$lang['L_UP_TO_DATE'] = 'La versione corrente è aggiornata'; diff --git a/msd/language/it/lang_restore.php b/msd/language/it/lang_restore.php new file mode 100644 index 0000000..08d5553 --- /dev/null +++ b/msd/language/it/lang_restore.php @@ -0,0 +1,19 @@ +%d tabelle.'; +$lang['L_FILE_MISSING'] = 'impossibile trovare il file'; +$lang['L_RESTORE_DB'] = "Database '%s' sul Server '%s'."; +$lang['L_RESTORE_COMPLETE'] = '%s tabelle sono state create.'; +$lang['L_RESTORE_RUN1'] = '
      Finora sono stati aggiunti con successo da %s a %s record.'; +$lang['L_RESTORE_RUN2'] = "
      Sto popolando la tabella '%s'.

      "; +$lang['L_RESTORE_COMPLETE2'] = '%s record sono stati inseriti.'; +$lang['L_RESTORE_TABLES_COMPLETED'] = 'Finora sono state create da %d a %d tabelle.'; +$lang['L_RESTORE_TOTAL_COMPLETE'] = '
      Congratulazioni!

      Il database è stato ripristinato completamente.
      Tutti i file del backup sono stati inseriti con successo.

      Hai finito. :-)'; +$lang['L_DB_SELECT_ERROR'] = '
      Errore:
      Selezione del database '; +$lang['L_DB_SELECT_ERROR2'] = ' fallito!'; +$lang['L_FILE_OPEN_ERROR'] = 'Errore: impossibile aprire il file.'; +$lang['L_PROGRESS_OVER_ALL'] = 'Progresso totale'; +$lang['L_BACK_TO_OVERVIEW'] = 'Riepilogo database'; +$lang['L_RESTORE_RUN0'] = '
      Finora sono stati aggiunti con successo da %s a %s record.'; +$lang['L_UNKNOWN_SQLCOMMAND'] = 'Comando SQL sconosciuto.'; +$lang['L_NOTICES'] = 'Avvisi'; diff --git a/msd/language/it/lang_sql.php b/msd/language/it/lang_sql.php new file mode 100644 index 0000000..ea005c4 --- /dev/null +++ b/msd/language/it/lang_sql.php @@ -0,0 +1,190 @@ +%s righe da esportare'; +$lang['L_CSV_FIELDCOUNT_NOMATCH'] = 'La quantità dei campi della tabella non coincide con i dati da importare (%d al posto di %d).'; +$lang['L_CSV_FIELDSLINES'] = '%d campi trovati, totale %d righe'; +$lang['L_CSV_ERRORCREATETABLE'] = 'Errore nella creazione della tabella `%s` !'; +$lang['L_FM_UPLOADFILEREQUEST'] = 'Scegli un file.'; +$lang['L_CSV_NODATA'] = 'File da importare non trovato!'; +$lang['L_SQLLIB_GENERALFUNCTIONS'] = 'funzioni generali'; +$lang['L_SQLLIB_RESETAUTO'] = 'ripristina autoincremento'; +$lang['L_SQLLIB_BOARDS'] = 'Board'; +$lang['L_SQLLIB_DEACTIVATEBOARD'] = 'disattiva Board'; +$lang['L_SQLLIB_ACTIVATEBOARD'] = 'attivare Board'; +$lang['L_SQL_NOTABLESSELECTED'] = 'Non ci sono tabelle selezionate !'; +$lang['L_TOOLS'] = 'Tools'; +$lang['L_TOOLS_TOOLBOX'] = 'Scegliere database / Funzione database / Importa e Esporta '; +$lang['L_SQL_OPENFILE'] = 'aprire file - SQL'; +$lang['L_SQL_OPENFILE_BUTTON'] = 'Caricare'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Grandezza massima del file'; +$lang['L_SQL_SEARCH'] = 'Ricerca'; +$lang['L_SQL_SEARCHWORDS'] = 'Parola(e) di ricerca'; +$lang['L_START_SQL_SEARCH'] = 'esegui ricerca'; +$lang['L_RESET_SEARCHWORDS'] = 'resetta ricerca'; +$lang['L_SEARCH_OPTIONS'] = 'Opzione di ricerca'; +$lang['L_SEARCH_RESULTS'] = 'La ricera di "%s" nella tabella "%s" ha portato al seguente risultato'; +$lang['L_SEARCH_NO_RESULTS'] = 'La ricera "%s" nella tabella "%s" non ha prodotto risultati!'; +$lang['L_NO_ENTRIES'] = 'La tabella "%s" è vuota.'; +$lang['L_SEARCH_ACCESS_KEYS'] = 'Sfogliare: avanti=ALT+V, indietro=ALT+C'; +$lang['L_SEARCH_OPTIONS_OR'] = 'una colonna deve contenere almeno una parola di ricerca'; +$lang['L_SEARCH_OPTIONS_CONCAT'] = 'un record deve contenere tutte le parole di ricerca, però possono essere in colonne diverse (Calcolo intensivo!)'; +$lang['L_SEARCH_OPTIONS_AND'] = 'una colonna deve contenere tutte le parole di ricerca'; +$lang['L_SEARCH_IN_TABLE'] = 'cerca nelle tabelle'; +$lang['L_ERROR_NO_FIELDS'] = 'Search error: it could not be determined which fields the table "%s" has!'; +$lang['L_SQL_EDIT_TABLESTRUCTURE'] = 'Edit struttura tabelle'; +$lang['L_DEFAULT_CHARSET'] = 'Set di caretteri standard'; +$lang['L_TITLE_KEY_PRIMARY'] = 'Chiave primaria'; +$lang['L_TITLE_KEY_UNIQUE'] = 'Chiave unica'; +$lang['L_TITLE_INDEX'] = 'Indice'; +$lang['L_TITLE_KEY_FULLTEXT'] = 'Chiave testo pieno'; +$lang['L_TITLE_NOKEY'] = 'Nessuna chiave'; +$lang['L_TITLE_SEARCH'] = 'Cerca'; +$lang['L_TITLE_MYSQL_HELP'] = 'Documentazione MySQL'; +$lang['L_TITLE_UPLOAD'] = 'Carica dati-SQL'; +$lang['L_PRIMARYKEY_DELETED'] = 'Chiave primaria cancellata'; +$lang['L_PRIMARYKEY_NOTFOUND'] = 'Chiave primaria non trovata'; +$lang['L_PRIMARYKEYS_CHANGED'] = 'Chiave primaria cambiata'; +$lang['L_PRIMARYKEYS_CHANGINGERROR'] = 'Errore nel cambiare della chiave primaria'; +$lang['L_SQL_VIEW_COMPACT'] = 'Visualizza: compatto'; +$lang['L_SQL_VIEW_STANDARD'] = 'Visualizza: normale'; +$lang['L_FIELDS_OF_TABLE'] = 'Campi della tabella'; +$lang['L_ENGINE'] = 'Engine'; +$lang['L_USERNAME'] = 'Username'; +$lang['L_PASSWORD'] = 'Password'; +$lang['L_PASSWORD_REPEAT'] = 'Password (repeat)'; +$lang['L_INFO_SIZE'] = 'Grandezza'; +$lang['L_TABLE_TYPE'] = 'Tipo'; +$lang['L_KEY_DELETED'] = 'Index deleted'; +$lang['L_KEY_DELETEERROR'] = 'Error deleting index'; +$lang['L_KEY_ADDED'] = 'Index added'; +$lang['L_KEY_ADDERROR'] = 'Error adding index'; diff --git a/msd/language/lang_list.php b/msd/language/lang_list.php new file mode 100644 index 0000000..956a47d --- /dev/null +++ b/msd/language/lang_list.php @@ -0,0 +1,128 @@ + +

      MyOOS [Dumper] based on MySQLDumper 1.24.4

      + +

      About this project

      +

      MyOOS [Dumper] is an improved version of MySQLDumper 1.24.4 (January 24, 2011). This enhancement takes into account the development of PHP.

      . +

      Most of all stability, security and handling are the main focus of MyOOS [Dumper]. But also an attractive template is included, which can be edited and adapted to your own needs.

      . + + +

      MyOOS [Dumper] is a backup program for MySQL databases, written in PHP and Perl. With it, backup copies of the data (store, blog, etc.) can be created and restored if necessary. Especially for web space without shell access, MyOOS [Dumper] is a useful alternative.

      . + +

      The idea for MySQLDumper came from Daniel Schlichtholz. He opened the MySQLDumper forum in 2004, whereupon programmers wrote new scripts and extended existing ones.

      + + + +

      Wish List / Future Attractions

      . +

      Do you have any suggestions for improvements? Feel free to contact the development team via the forum https://foren.myoos.de/viewforum.php?f=41.

      + + +

      Contribute

      +

      If you would like to help us improve the MyOOS project, we welcome your pull requests via GitHub here.

      +https://github.com/r23/MyOOS-Dumper/ + + +

      Financial Support

      . +

      You can use PayPal Me
      . +https://www.paypal.com/paypalme/r23de?locale.x=de_DE

      + +

      or via the QR code
      . +Financial support for MyOOS [Dumper]

      + +Send money to the MyOOS project.
      + +

      We hope you enjoy this project.

      The MyOOS [Dumper] Team

      + +MyOOS [Dumper]
      + +

      MyOOS [Dumper] Help

      + +

      Download

      +

      You can always get the latest versions via GitHub
      . +https://github.com/r23/MyOOS-Dumper/releases

      + + +

      System requirement

      . +

      The script works on any server (Windows, Linux, ...)
      +with PHP >= version 7.4 with GZip support, MySQL (version 4.1 or higher), JavaScript (must be enabled)

      . +

      Copy the mod folder from the MyOOS archive to a separate working folder.

      . + +

      Installation

      . +The installation process is straightforward. +

      From the MyOOS archive, copy the mod folder into any folder.
      +Upload all the files from the mod folder to your web server. (For example, to the lowest level in [server web directory/]mod)
      +... done!
      +You can now call MyOOS [Dumper] in your web browser by "https://example.com/mod/",
      +to complete the installation. Just follow the instructions.
      +
      Note:
      If on your server the script is not allowed to create directories,
      +you have to do this manually, because MyOOS [Dumper] stores the data ordered in +directories.
      +The script aborts with an appropriate statement!
      +After you have created the directories (according to the hint), it runs normally and without restrictions.
      + +

      Perl script instructions

      . +Most have a cgi-bin directory where perl can be run.
      +This is usually accessible by browser via http://www.example.com/cgi-bin/.
      +
      +For this case, please perform the following steps:

      . + +1. call the Backup page in MyOOS [Dumper] and click on "Backup Perl".
      +2. copy the path that is behind entry in crondump.pl for $absolute_path_of_configdir:.
      +3. open the file "crondump.pl" in the editor.
      +4. enter the copied path there at absolute_path_of_configdir (no spaces).
      +5. save crondump.pl .
      +6. copy crondump.pl, as well as perltest.pl and simpletest.pl into the cgi-bin directory (ascii mode in FTP).
      +7. give the files the permissions 755.
      +7b. If the ending cgi is desired, change the ending of all 3 files from pl -> cgi (rename).
      +8. call the configuration in MyOOS [Dumper].
      +9. select the page Cronscript.
      +10. change perl execution path to /cgi-bin/ .
      +10b. If the scripts have .pl, change the file extension to .cgi .
      +11.Save the configuration.

      + +Done, the scripts can now be called from the backup page.

      . + +For those who can run Perl in all directories, the following steps will suffice:

      . + +1. call in the MyOOS [Dumper] the page Backup.
      +2. copy the path that is behind entry in crondump.pl for $absolute_path_of_configdir:.
      +3. open the file "crondump.pl" in the editor.
      +4. enter the copied path there at absolute_path_of_configdir (no spaces).
      +5. save crondump.pl .
      +6. give the files the permissions 755.
      +6b. If the extension cgi is desired, change the extension of all 3 files from pl -> cgi (rename).
      +(ev. 10b+11 from above)
      +
      + +Windowsuser have to change the first line of all scripts, there is the path of Perl. Example:
      +instead of: #!/usr/bin/perl -w
      +now: #!C:_usr/bin/perl.exe -w
      + +

      Operation

        . + +
        Menu
        . +In the selection list above you set the database.
        +All actions refer to the database set here. + +
        Home
        +Here you can learn about your system, the different installed versions and details about the +versions and details about the configured databases.
        +If you click on the database name, you will see a list of the tables with the number of entries +with the number of entries, the size and the last update date. + +
        Configuration
        . +Here you can edit your configuration, save it or restore the initial configuration. +restore. +

          +
        • Configured databases: the listing of configured databases. The active database is listed in bold.
        • +
        • Table prefix: here you can specify (for each database) a prefix. This is a filter that will take into account for dumps only the tables that start with this prefix (for example, all tables that start with "phpBB_"). If you want all tables in this database to be saved, just leave the field empty.
        • . +
        • GZip compression: Here you can enable compression. It is recommended to enable it, because the files will be much smaller after all and disk space is always scarce.
        • . +
        • Email with Dumpfile: If this option is enabled, an email with the dump as an attachment will be sent after the backup is complete (caution, compression should absolutely be on, otherwise the attachment will be too large and may not be sent!).
        • +
        • Email address: Recipient address for the email.
        • +
        • Sender of the email: this address appears as the sender in the email.
        • +
        • FTP Transfer: If this option is enabled, the backup file will be sent via FTP after the backup is completed.
        • +
        • FTP Server: The address of the FTP server (e.g. ftp.mybackups.com).
        • +
        • FTP Server Port: The port of the FTP server (usually 21).
        • +
        • FTP User: The username of the FTP account.
        • +
        • FTP Password: The password of the FTP account.
        • +
        • FTP Upload Folder: The directory where the backup file should go (upload permissions must exist!).
        • +
        • Automatic deletion of backups: If this option is enabled, older backups will be deleted automatically according to the following rules.
        • . +
        • Number of backup files: A value > 0 deletes all backup files except for the number specified here.
        • +
        • Language: here you specify the language for the interface.
        • +
        + +
        Administration
        . +This is where the actual actions are performed.
        +It will show you all the files in the backup directory. +For the actions "Restore" and "Delete" a file must be selected. +
          +
        • Restore: This will update the database with the selected backup file.
        • +
        • Delete: This lets you delete the selected backup file.
        • +
        • Start new backup: Here you start a new backup (dump) according to the parameters set in the configuration.
        • . +
        + +
        Log
        +Here you can see and delete the log entries. +
        Credits / Help
        +this page. +
      diff --git a/msd/language/pt_br/lang.php b/msd/language/pt_br/lang.php new file mode 100644 index 0000000..4632031 --- /dev/null +++ b/msd/language/pt_br/lang.php @@ -0,0 +1,109 @@ +não disponível'; +$lang['L_VOM'] = 'de'; +$lang['L_MYSQLVARS'] = 'Variáveis do MySQL'; +$lang['L_MYSQLSYS'] = 'Comandos do MySQL'; +$lang['L_STATUS'] = 'Estado'; +$lang['L_PROZESSE'] = 'Processos'; +$lang['L_INFO_NOVARS'] = 'nenhuma variável disponível'; +$lang['L_INHALT'] = 'Valor'; +$lang['L_INFO_NOSTATUS'] = 'nenhum estado disponível'; +$lang['L_INFO_NOPROCESSES'] = 'nenhum processo em execução'; +$lang['L_FM_FREESPACE'] = 'Espaço livre no servidor'; +$lang['L_LOAD_DATABASE'] = 'recarregar bancos de dados'; +$lang['L_HOME'] = 'Início'; +$lang['L_CONFIG'] = 'Configuração'; +$lang['L_DUMP'] = 'Criar backup'; +$lang['L_RESTORE'] = 'Restaurar'; +$lang['L_FILE_MANAGE'] = 'Administração de arquivos'; +$lang['L_LOG'] = 'Log'; +$lang['L_CHOOSE_DB'] = 'Escolher banco de dados'; +$lang['L_CREDITS'] = 'Créditos / Ajuda'; +$lang['L_MULTI_PART'] = 'Backup multi-parte'; +$lang['L_LOGFILENOTWRITABLE'] = 'Não pude criar o arquivo de log !'; +$lang['L_SQL_ERROR1'] = 'Erro na consulta:'; +$lang['L_SQL_ERROR2'] = 'MySQL diz:'; +$lang['L_UNKNOWN'] = 'desconhecido'; +$lang['L_UNKNOWN_NUMBER_OF_RECORDS'] = 'desconhecido'; +$lang['L_OK'] = 'OK'; +$lang['L_CRON_COMPLETELOG'] = 'Registrar todas as saídas'; +$lang['L_NO'] = 'não'; +$lang['L_CREATE_DATABASE'] = 'Criar novo banco de dados'; +$lang['L_EXPORTFINISHED'] = 'Exportação finalizada.'; +$lang['L_SQL_BROWSER'] = 'Navegador-SQL'; +$lang['L_SERVER'] = 'Servidor'; +$lang['L_MYSQL_CONNECTION_ENCODING'] = 'Standard encoding of MySQL-Server'; +$lang['L_TITLE_SHOW_DATA'] = 'Show data'; +$lang['L_PRIMARYKEY_CONFIRMDELETE'] = 'Really delete primary key?'; +$lang['L_SETPRIMARYKEYSFOR'] = 'Set new primary keys for table'; +$lang['L_PRIMARYKEY_FIELD'] = 'Primary key field'; +$lang['L_PRIMARYKEYS_SAVE'] = 'Save primary keys'; +$lang['L_CANCEL'] = 'Cancel'; +$lang['L_VISIT_HOMEPAGE'] = 'Visit Homepage'; +$lang['L_SECONDS'] = 'Seconds'; +$lang['L_BACKUPS'] = 'Backups'; +$lang['L_MINUTES'] = 'Minutes'; +$lang['L_PAGE_REFRESHS'] = 'Page refreshs'; +$lang['L_MINUTE'] = 'Minute'; +$lang['L_SETKEYSFOR'] = 'Set new indexes for table'; +$lang['L_KEY_CONFIRMDELETE'] = 'Really delete index?'; diff --git a/msd/language/pt_br/lang_config_overview.php b/msd/language/pt_br/lang_config_overview.php new file mode 100644 index 0000000..9a73dc9 --- /dev/null +++ b/msd/language/pt_br/lang_config_overview.php @@ -0,0 +1,128 @@ +%s
      into %s'; +$lang['L_FTP'] = 'FTP'; +$lang['L_SFTP_SEND_TO'] = 'to %s
      into %s'; +$lang['L_SFTP'] = 'SFTP'; +$lang['L_EMAIL_CC'] = 'CC-Receiver'; +$lang['L_NAME'] = 'Name'; +$lang['L_CONFIRM_CONFIGFILE_DELETE'] = 'Really delete the configuration file %s?'; +$lang['L_ERROR_DELETING_CONFIGFILE'] = "Error: couldn't delete configuration file %s!"; +$lang['L_SUCCESS_DELETING_CONFIGFILE'] = 'The configuration file %s has successfully been deleted.'; +$lang['L_SUCCESS_CONFIGFILE_CREATED'] = 'Configuration file %s has successfully been created.'; +$lang['L_ERROR_CONFIGFILE_NAME'] = 'Filename "%s" contains invalid characters.'; +$lang['L_CREATE_CONFIGFILE'] = 'Create a new configuration file'; +$lang['L_ERROR_LOADING_CONFIGFILE'] = "Couldn't load configfile \"%s\"."; +$lang['L_BACKUP_DBS_PHP'] = 'DBs to backup (PHP)'; +$lang['L_BACKUP_DBS_PERL'] = 'DBs to backup (PERL)'; +$lang['L_CRON_COMMENT'] = 'Digitar comentário'; +$lang['L_AUTODETECT'] = 'auto detect'; diff --git a/msd/language/pt_br/lang_dump.php b/msd/language/pt_br/lang_dump.php new file mode 100644 index 0000000..87db0f7 --- /dev/null +++ b/msd/language/pt_br/lang_dump.php @@ -0,0 +1,58 @@ +%s` '; +$lang['L_DUMP_ENDERGEBNIS'] = 'O arquivo contém %s tabela(s) com %s registro(s).
      '; +$lang['L_MAILERROR'] = 'O envio do email falhou!'; +$lang['L_EMAILBODY_ATTACH'] = 'O anexo contém o backup do seu banco de dados MySQL.
      Backup do banco de dados `%s` +

      O seguinte arquivo foi criado:

      %s

      Atenciosamente

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_MP_NOATTACH'] = 'Um backup Multi-parte foi criad.
      Os arquivos não estão anexados a este email!
      Backup do banco de dados `%s` +

      Os seguintes arquivos foram criados:

      %s +

      Atenciosamente

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_MP_ATTACH'] = 'Um backup Multi-parte foi criado.
      Os arquivos de backup estão anexados em emails separados.
      Backup do banco de dados `%s` +

      Os seguintes arquivos foram criados:

      %s

      Atenciosamente

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_FOOTER'] = '`

      Atenciosamente

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_TOOBIG'] = 'O arquivo de backup excedeu o tamanho máximo de %s e não foi anexado a este email.
      Backup do banco de dados `%s` +

      O seguinte arquivo foi criado:

      %s +

      Atenciosamente

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_NOATTACH'] = 'Os arquivos não estão anexados a este email!
      Backup do banco de dados `%s` +

      O seguinte arquivo foi criado:

      %s +

      Atenciosamente

      MyOOS [Dumper]
      '; +$lang['L_EMAIL_ONLY_ATTACHMENT'] = ' ... somente anexos.'; +$lang['L_TABLESELECTION'] = 'Seleção de tabela'; +$lang['L_SELECTALL'] = 'Selecionar tudo'; +$lang['L_DESELECTALL'] = 'Desselecionar tudo'; +$lang['L_STARTDUMP'] = 'Iniciar backup'; +$lang['L_LASTBUFROM'] = 'última atualização de'; +$lang['L_NOT_SUPPORTED'] = 'Este backup não suporta esta função.'; +$lang['L_MULTIDUMP'] = 'Multidump: Backup do(s) %d banco(s) de dados pronto.'; +$lang['L_FILESENDFTP'] = 'enviando o arquivo via FTP... favor ter paciente. '; +$lang['L_FTPCONNERROR'] = 'Conexão de FTP não estabelecida! Conexão com '; +$lang['L_FTPCONNERROR1'] = ' com o usuário '; +$lang['L_FTPCONNERROR2'] = ' impossível'; +$lang['L_FTPCONNERROR3'] = 'Envio por FTP falhou! '; +$lang['L_FTPCONNECTED1'] = 'Conectado com '; +$lang['L_FTPCONNECTED2'] = ' em '; +$lang['L_FTPCONNECTED3'] = ' trasnferido com sucesso'; +$lang['L_FILESENDSFTP'] = 'enviando o arquivo via SFTP... favor ter paciente. '; +$lang['L_SFTPCONNERROR'] = 'Conexão de SFTP não estabelecida! Conexão com '; +$lang['L_NR_TABLES_SELECTED'] = '- com %s tabelas selecionadas'; +$lang['L_NR_TABLES_OPTIMIZED'] = '%s tabelas foram otimizadas.'; +$lang['L_DUMP_ERRORS'] = '

      %s erros ocorreram: verdere

      '; +$lang['L_FATAL_ERROR_DUMP'] = "Fatal error: the CREATE-Statement of table '%s' in database '%s' couldn't be read!"; diff --git a/msd/language/pt_br/lang_filemanagement.php b/msd/language/pt_br/lang_filemanagement.php new file mode 100644 index 0000000..539ab4f --- /dev/null +++ b/msd/language/pt_br/lang_filemanagement.php @@ -0,0 +1,79 @@ +%s"'; +$lang['L_DELETE_FILE_ERROR'] = 'Error deleting file "%s"!'; +$lang['L_FM_DUMP_HEADER'] = 'Backup'; +$lang['L_DOCRONBUTTON'] = 'Executar o script Perl Cron'; +$lang['L_DOPERLTEST'] = 'Testar módulos Perl'; +$lang['L_DOSIMPLETEST'] = 'Testar Perl'; +$lang['L_PERLOUTPUT1'] = 'Entrada no crondump.pl para o absolute_path_of_configdir'; +$lang['L_PERLOUTPUT2'] = 'URL para o navegador ou serviço Cron externo'; +$lang['L_PERLOUTPUT3'] = 'Linha de comando no terminal para o Crontab'; +$lang['L_RESTORE_OF_TABLES'] = 'Choose tables to be restored'; +$lang['L_CONVERTER'] = 'Conversor de backup'; +$lang['L_CONVERT_FILE'] = 'Arquivo a ser convertido'; +$lang['L_CONVERT_FILENAME'] = 'Nome do arquivo de destino (sem extensão)'; +$lang['L_CONVERTING'] = 'Convertendo'; +$lang['L_CONVERT_FILEREAD'] = "Ler arquivo '%s'"; +$lang['L_CONVERT_FINISHED'] = "Conversão terminada, o arquivo '%s' foi gravado com sucesso."; +$lang['L_NO_MOD_BACKUPFILE'] = 'Backups de outros scripts'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Tamanho máximo do aqruivo'; +$lang['L_MAX_UPLOAD_SIZE_INFO'] = 'Se o seu arquivo de dump é maior que o limite mencionado acima, você deve enviá-lo via FTP para o diretório "work/backup". +Após fazer isso você poderá escolhê-lo novamente para iniciar o processo de restauração. '; +$lang['L_ENCODING'] = 'encoding'; +$lang['L_FM_CHOOSE_ENCODING'] = 'Choose encoding of backup file'; +$lang['L_CHOOSE_CHARSET'] = "MyOOS [Dumper] couldn't detect the encoding of the backup file automatically. +
      You must choose the charset with which this backup was saved. +
      If you discover any problems with some characters after restoring, you can repeat the backup-progress and then choose another character set. +
      Good luck. ;)"; +$lang['L_DOWNLOAD_FILE'] = 'Download file'; +$lang['L_BACKUP_NOT_POSSIBLE'] = 'A backup of the system database `%s` is not possible!'; diff --git a/msd/language/pt_br/lang_help.php b/msd/language/pt_br/lang_help.php new file mode 100644 index 0000000..123f6b7 --- /dev/null +++ b/msd/language/pt_br/lang_help.php @@ -0,0 +1,40 @@ +Instalação completada --> iniciar o MyOOS [Dumper]
      '; +$lang['L_INSTALL_TOMENU'] = 'Voltar ao menu principal'; +$lang['L_INSTALLMENU'] = 'Menu principal'; +$lang['L_STEP'] = 'Passo'; +$lang['L_INSTALL'] = 'Instalação'; +$lang['L_UNINSTALL'] = 'Desinstalar'; +$lang['L_TOOLS'] = 'Ferramentas'; +$lang['L_EDITCONF'] = 'Editar configuração'; +$lang['L_OSWEITER'] = 'Continuar sem salvar'; +$lang['L_ERRORMAN'] = 'Erro durante a gravação da configuração!
      Favor editar o Arquivo '; +$lang['L_MANUELL'] = 'manualmente'; +$lang['L_CREATEDIRS'] = 'Criar diretórios'; +$lang['L_INSTALL_CONTINUE'] = 'Continuar com a instalação'; +$lang['L_CONNECTTOMYSQL'] = 'Conectar ao MySQL '; +$lang['L_DBPARAMETER'] = 'Parâmetros do banco de dados'; +$lang['L_CONFIGNOTWRITABLE'] = 'Eu não consigo escrever no arquivo "config.php". +Favor usar seu programa de FTP e alterar as permissões deste arquivo para 0777.'; +$lang['L_DBCONNECTION'] = 'Conexão do banco de dados'; +$lang['L_CONNECTIONERROR'] = 'Erro: incapaz de conectar.'; +$lang['L_CONNECTION_OK'] = 'A conexão ao banco de dados foi estabelecida.'; +$lang['L_SAVEANDCONTINUE'] = 'Salvar e continuar a instalação'; +$lang['L_CONFBASIC'] = 'Parâmetros básicos'; +$lang['L_INSTALL_STEP2FINISHED'] = 'Os parâmetros do banco de dados foram salvos.'; +$lang['L_INSTALL_STEP2_1'] = 'Continuar a instalação com as opções padrão'; +$lang['L_LASTSTEP'] = 'Instalação terminada'; +$lang['L_FTPMODE'] = 'Criar os diretórios necessários em modo seguro'; +$lang['L_IDOMANUAL'] = 'Eu mesmo criarei os diretórios'; +$lang['L_DOFROM'] = 'iniciando com'; +$lang['L_FTPMODE2'] = 'Criar os diretórios pelo FTP:'; +$lang['L_CONNECT'] = 'conectar'; +$lang['L_DIRS_CREATED'] = 'Os diretórios foram criados e as permissões corretamente atribuídas.'; +$lang['L_CONNECT_TO'] = 'conectar a'; +$lang['L_CHANGEDIR'] = 'mudar para o diretório'; +$lang['L_CHANGEDIRERROR'] = 'a mudança para o diretório não foi possível'; +$lang['L_FTP_OK'] = 'os parâmetros de FTP estão ok'; +$lang['L_CREATEDIRS2'] = 'Criar diretórios'; +$lang['L_FTP_NOTCONNECTED'] = 'A conexão de FTP não foi estabelecida!'; +$lang['L_CONNWITH'] = 'Conexão com'; +$lang['L_ASUSER'] = 'como usuário'; +$lang['L_NOTPOSSIBLE'] = 'impossível'; +$lang['L_DIRCR1'] = 'criar workdir'; +$lang['L_DIRCR2'] = 'criar backupdir'; +$lang['L_DIRCR4'] = 'criar logdir'; +$lang['L_DIRCR5'] = 'criar configurationdir'; +$lang['L_INDIR'] = 'agora no diretório'; +$lang['L_CHECK_DIRS'] = 'Verificar meus diretórios'; +$lang['L_DISABLEDFUNCTIONS'] = 'Funções desativadas'; +$lang['L_NOFTPPOSSIBLE'] = 'Você não tem as funções de FTP !'; +$lang['L_NOGZPOSSIBLE'] = 'Você não tem as funções de compressão !'; +$lang['L_UI1'] = 'Todos os diretórios que podem conter backups serão excluidos.'; +$lang['L_UI2'] = 'Você tem certeza de que quer isso?'; +$lang['L_UI3'] = 'não, cancelar imediatamente'; +$lang['L_UI4'] = 'sim, favor continuar'; +$lang['L_UI5'] = 'excluir diretórios de serviço'; +$lang['L_UI6'] = 'todos foram excluidos com sucesso.'; +$lang['L_UI7'] = 'Fvor excluir o diretório de script'; +$lang['L_UI8'] = 'one level up'; +$lang['L_UI9'] = 'Um erro ocorreu, impossível excluir

      Erro com o diretório '; +$lang['L_IMPORT'] = 'Importar configuração'; +$lang['L_IMPORT3'] = 'A configuração foi carregada ...'; +$lang['L_IMPORT4'] = 'A configuração foi salva.'; +$lang['L_IMPORT5'] = 'Iniciar o MyOOS [Dumper]'; +$lang['L_IMPORT6'] = 'Menu de instalação'; +$lang['L_IMPORT7'] = 'Enviar configuração'; +$lang['L_IMPORT8'] = 'voltar ao envio'; +$lang['L_IMPORT9'] = 'Este não é um backup de configuração !'; +$lang['L_IMPORT10'] = 'A configuração foi enviada com sucesso ...'; +$lang['L_IMPORT11'] = 'Erro: Houveram problemas ao escrever os comandos sql'; +$lang['L_IMPORT12'] = 'Erro: Houveram problemas ao escrever o config.php'; +$lang['L_INSTALL_HELP_PORT'] = '(em branco = Porta padrão)'; +$lang['L_INSTALL_HELP_SOCKET'] = '(em branco = Socket padrão)'; +$lang['L_TRYAGAIN'] = 'Tentar novamente'; +$lang['L_SOCKET'] = 'Socket'; +$lang['L_PORT'] = 'Porta'; +$lang['L_FOUND_DB'] = 'bd localizado'; +$lang['L_FM_FILEUPLOAD'] = 'Enviar arquivo'; +$lang['L_PASS'] = 'Senha'; +$lang['L_NO_DB_FOUND_INFO'] = 'A conexão com o banco de dados foi estabelecida com sucesso.
      +Seus dados de usuário são válidos e foram aceitos pelo Servidor MySQL.
      +Mas o MyOOS [Dumper] não foi capaz de encontrar nenhuma base de dados.
      +A detecção automática via script é bloqueada em alguns servidores.
      +Você deve colocar seus dados do banco de dados manualmente depois de terminada a instalação. +Clique em "configurações" "Parâmetros de Conexão - exibir" e digite ali o nome do banco de dados.'; +$lang['L_ENTER_DB_INFO'] = 'First click the button "Connect to MySQL". Only if no database could be detected you need to provide a database name here.'; diff --git a/msd/language/pt_br/lang_log.php b/msd/language/pt_br/lang_log.php new file mode 100644 index 0000000..88d697c --- /dev/null +++ b/msd/language/pt_br/lang_log.php @@ -0,0 +1,7 @@ +Favor criar os 2 arquivos manualmente com o seguinte conteúdo'; +$lang['L_HTACC_CHECK_ERROR'] = 'It could not be checked whether the program is protected!
      The simulated external access could not be carried out.'; +$lang['L_HTACC_NOT_NEEDED'] = 'The program is protected by higher-level authorizations; local directory protection is not required.'; +$lang['L_HTACC_COMPLETE'] = 'The program is protected, the directory protection is complete.'; +$lang['L_HTACC_INCOMPLETE'] = 'The program is not protected, the directory protection is incomplete!'; +$lang['L_HTACC_PROPOSED'] = 'The program is not protected, directory protection is strongly recommended!'; +$lang['L_HTACC_EDIT'] = 'Editar o .htaccess'; +$lang['L_HTACCESS18'] = 'Criar o .htaccess em '; +$lang['L_HTACCESS19'] = 'Recarregar '; +$lang['L_HTACCESS20'] = 'Executar script'; +$lang['L_HTACCESS21'] = 'Adicionar handler'; +$lang['L_HTACCESS22'] = 'Tornar executável'; +$lang['L_HTACCESS23'] = 'Listar Diretórios'; +$lang['L_HTACCESS24'] = 'Documento de Erro'; +$lang['L_HTACCESS25'] = 'Ativar rewrite'; +$lang['L_HTACCESS26'] = 'Negar / Permitir'; +$lang['L_HTACCESS27'] = 'Redirecionar'; +$lang['L_HTACCESS28'] = 'Error Log'; +$lang['L_HTACCESS29'] = 'Mais exemplos e documentação'; +$lang['L_HTACCESS30'] = 'Provedor'; +$lang['L_HTACCESS31'] = 'General'; +$lang['L_HTACCESS32'] = 'Atenção! As diretivas do .htaccess afetam o comportamento do navegador.
      Com conteúdo incorreto, as páginas podem ficar inacessíveis.'; +$lang['L_DISABLEDFUNCTIONS'] = 'Desativar Funções'; +$lang['L_NOGZPOSSIBLE'] = 'Como Zlib não está instalado, você não poderá usar as funções do GZip!'; +$lang['L_DELETE_HTACCESS'] = 'Remover proteção de diretório (apagar .htaccess)'; +$lang['L_WRONG_RIGHTS'] = "O arquivo ou o diretório '%s' não tem permissão de escrita para mim.
      +As permissões (chmod) não estão configuradas apropriadamente ou não há privilégios suficientes para este usuário.
      +Por favor configure corretamente as permissões usando o programa de FTP.
      +O arquivo ou diretório necessitam de configuração para %s.
      "; +$lang['L_CANT_CREATE_DIR'] = "Não foi possível criar o diretório '%s'. +Por favor utilize seu programa de FTP."; +$lang['L_TABLE_TYPE'] = 'Type'; +$lang['L_CHECK'] = 'check'; +$lang['L_OS'] = 'Operating system'; +$lang['L_MOD_VERSION'] = 'MyOOS [Dumper] - Version'; +$lang['L_NEW_MOD_VERSION'] = 'New Version'; +$lang['L_NEW_MOD_VERSION_INFO'] = 'There is a new version of MyOOS [Dumper] available.'; +$lang['L_UPDATED_IMPORTANT'] = 'Important: Before updating, please backup your files.'; +$lang['L_UPDATE'] = 'Update now'; +$lang['L_MYSQL_VERSION'] = 'MySQL-Version'; +$lang['L_PHP_VERSION'] = 'PHP-Version'; +$lang['L_MAX_EXECUTION_TIME'] = 'Max execution time'; +$lang['L_PHP_EXTENSIONS'] = 'PHP-Extensions'; +$lang['L_MEMORY'] = 'Memory'; +$lang['L_FILE_MISSING'] = 'não pude encontrar o arquivo'; +$lang['L_INSTALLING_UPDATES'] = 'Installing Updates'; +$lang['L_UPDATE_SUCCESSFUL'] = 'Update successful'; +$lang['L_UPDATE_FAILED'] = 'Update failed'; +$lang['L_UP_TO_DATE'] = 'Current Version is up to date'; diff --git a/msd/language/pt_br/lang_restore.php b/msd/language/pt_br/lang_restore.php new file mode 100644 index 0000000..b290654 --- /dev/null +++ b/msd/language/pt_br/lang_restore.php @@ -0,0 +1,19 @@ +%d tabelas foram criadas.'; +$lang['L_FILE_MISSING'] = 'não pude encontrar o arquivo'; +$lang['L_RESTORE_DB'] = "banco de dados '%s' em '%s'."; +$lang['L_RESTORE_COMPLETE'] = '%s tabelas driadas.'; +$lang['L_RESTORE_RUN1'] = '
      Up to now %s of %s registros foram adicionados com sucesso.'; +$lang['L_RESTORE_RUN2'] = "
      Agora a tabela '%s' está sendo restaurada.

      "; +$lang['L_RESTORE_COMPLETE2'] = '%s registros inseridos.'; +$lang['L_RESTORE_TABLES_COMPLETED'] = 'Até agora %d de %d tabelas foram criadas.'; +$lang['L_RESTORE_TOTAL_COMPLETE'] = '
      Parabéns.

      A restauração do banco de dados está pronta.
      todos os dados do arquivo de backup foram restaurados.

      Está tudo pronto. :-)'; +$lang['L_DB_SELECT_ERROR'] = '
      Error:
      Seleção do banco de dados '; +$lang['L_DB_SELECT_ERROR2'] = ' falhou!'; +$lang['L_FILE_OPEN_ERROR'] = 'Erro: não pude abrir o arquivo.'; +$lang['L_PROGRESS_OVER_ALL'] = 'Progresso do todo'; +$lang['L_BACK_TO_OVERVIEW'] = 'Visão geral do banco de dados'; +$lang['L_RESTORE_RUN0'] = '
      até agora %s registros foram adicionados com sucesso.'; +$lang['L_UNKNOWN_SQLCOMMAND'] = 'comando SQL desconhecido'; +$lang['L_NOTICES'] = 'Notices'; diff --git a/msd/language/pt_br/lang_sql.php b/msd/language/pt_br/lang_sql.php new file mode 100644 index 0000000..36dc00f --- /dev/null +++ b/msd/language/pt_br/lang_sql.php @@ -0,0 +1,190 @@ +%s linhas exportadas'; +$lang['L_CSV_FIELDCOUNT_NOMATCH'] = 'A contagem de campos não confere com a dos dados a importar (%d ao invés de %d).'; +$lang['L_CSV_FIELDSLINES'] = '%d campos reconhecidos, totalizando %d linhas'; +$lang['L_CSV_ERRORCREATETABLE'] = 'Erro durante a criação da tabela `%s` !'; +$lang['L_FM_UPLOADFILEREQUEST'] = 'favor escolher um arquivo.'; +$lang['L_CSV_NODATA'] = 'Nenhum dado encontrado para importação!'; +$lang['L_SQLLIB_GENERALFUNCTIONS'] = 'funções gerais'; +$lang['L_SQLLIB_RESETAUTO'] = 'reiniciar o auto-incremento'; +$lang['L_SQLLIB_BOARDS'] = 'Quadros'; +$lang['L_SQLLIB_DEACTIVATEBOARD'] = 'desativar quadro'; +$lang['L_SQLLIB_ACTIVATEBOARD'] = 'ativar quadro'; +$lang['L_SQL_NOTABLESSELECTED'] = 'Nenhuma tabela selecionada !'; +$lang['L_TOOLS'] = 'Ferramentas'; +$lang['L_TOOLS_TOOLBOX'] = 'Selecionar banco de dados / Funções de banco de dados / Importar - Exportar '; +$lang['L_SQL_OPENFILE'] = 'Abrir arquivo SQL'; +$lang['L_SQL_OPENFILE_BUTTON'] = 'Upload'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Tamanho máximo de arquivo'; +$lang['L_SQL_SEARCH'] = 'Pesquisar'; +$lang['L_SQL_SEARCHWORDS'] = 'Pesquisar palavra(s)'; +$lang['L_START_SQL_SEARCH'] = 'iniciar pesquisa'; +$lang['L_RESET_SEARCHWORDS'] = 'reiniciar pesquisa de palavras'; +$lang['L_SEARCH_OPTIONS'] = 'Opções de pesquisa'; +$lang['L_SEARCH_RESULTS'] = 'A pesquisa por "%s" na tabela "%s" levou aos seguintes resultados'; +$lang['L_SEARCH_NO_RESULTS'] = 'A pesquisa por "%s" na tabela "%s" não trouxe nenhum resultado!'; +$lang['L_NO_ENTRIES'] = 'A tabela "%s" está vazia e não contêm nenhuma entrada.'; +$lang['L_SEARCH_ACCESS_KEYS'] = 'Navegação: para frente=ALT+V, para trás=ALT+C'; +$lang['L_SEARCH_OPTIONS_OR'] = 'a coluna deve conter uma das palavras a pesquisar (OU-pesquisar)'; +$lang['L_SEARCH_OPTIONS_CONCAT'] = 'a linha deve conter todas as palavras a pesquisar, mas elas podem estar em qualquer coluna (pode levar algum tempo)'; +$lang['L_SEARCH_OPTIONS_AND'] = 'a coluna deve conter todas as palavras a pesquisar (E-pesquisar)'; +$lang['L_SEARCH_IN_TABLE'] = 'Pesquisar na tabela'; +$lang['L_ERROR_NO_FIELDS'] = 'Search error: it could not be determined which fields the table "%s" has!'; +$lang['L_SQL_EDIT_TABLESTRUCTURE'] = 'Edit table structure'; +$lang['L_DEFAULT_CHARSET'] = 'Default character set'; +$lang['L_TITLE_KEY_PRIMARY'] = 'Primary key'; +$lang['L_TITLE_KEY_UNIQUE'] = 'Unique key'; +$lang['L_TITLE_INDEX'] = 'Index'; +$lang['L_TITLE_KEY_FULLTEXT'] = 'Fulltext key'; +$lang['L_TITLE_NOKEY'] = 'No key'; +$lang['L_TITLE_SEARCH'] = 'Search'; +$lang['L_TITLE_MYSQL_HELP'] = 'MySQl Documentation'; +$lang['L_TITLE_UPLOAD'] = 'Upload SQL file'; +$lang['L_PRIMARYKEY_DELETED'] = 'Primary key deleted'; +$lang['L_PRIMARYKEY_NOTFOUND'] = 'Primary key not found'; +$lang['L_PRIMARYKEYS_CHANGED'] = 'Primary keys changed'; +$lang['L_PRIMARYKEYS_CHANGINGERROR'] = 'Error changing primary keys'; +$lang['L_SQL_VIEW_COMPACT'] = 'View: compact'; +$lang['L_SQL_VIEW_STANDARD'] = 'View: standard'; +$lang['L_FIELDS_OF_TABLE'] = 'Fields of table'; +$lang['L_ENGINE'] = 'Engine'; +$lang['L_USERNAME'] = 'Username'; +$lang['L_PASSWORD'] = 'Password'; +$lang['L_PASSWORD_REPEAT'] = 'Password (repeat)'; +$lang['L_INFO_SIZE'] = 'tamanho'; +$lang['L_TABLE_TYPE'] = 'Type'; +$lang['L_KEY_DELETED'] = 'Index deleted'; +$lang['L_KEY_DELETEERROR'] = 'Error deleting index'; +$lang['L_KEY_ADDED'] = 'Index added'; +$lang['L_KEY_ADDERROR'] = 'Error adding index'; diff --git a/msd/language/sw/help.html b/msd/language/sw/help.html new file mode 100644 index 0000000..250ec11 --- /dev/null +++ b/msd/language/sw/help.html @@ -0,0 +1,146 @@ +
      +

      MyOOS [Dumper] based on MySQLDumper 1.24.4

      + +

      About this project

      +

      MyOOS [Dumper] is an improved version of MySQLDumper 1.24.4 (January 24, 2011). This enhancement takes into account the development of PHP.

      . +

      Most of all stability, security and handling are the main focus of MyOOS [Dumper]. But also an attractive template is included, which can be edited and adapted to your own needs.

      . + + +

      MyOOS [Dumper] is a backup program for MySQL databases, written in PHP and Perl. With it, backup copies of the data (store, blog, etc.) can be created and restored if necessary. Especially for web space without shell access, MyOOS [Dumper] is a useful alternative.

      . + +

      The idea for MySQLDumper came from Daniel Schlichtholz. He opened the MySQLDumper forum in 2004, whereupon programmers wrote new scripts and extended existing ones.

      + + +

      Wish List / Future Attractions

      . +

      Do you have any suggestions for improvements? Feel free to contact the development team via the forum https://foren.myoos.de/viewforum.php?f=41.

      + + +

      Contribute

      +

      If you would like to help us improve the MyOOS project, we welcome your pull requests via GitHub here.

      +https://github.com/r23/MyOOS-Dumper/ + + +

      Financial Support

      . +

      You can use PayPal Me
      . +https://www.paypal.com/paypalme/r23de?locale.x=de_DE

      + +

      or via the QR code
      . +Financial support for MyOOS [Dumper]

      + +Send money to the MyOOS project.
      + +

      We hope you enjoy this project.

      The MyOOS [Dumper] Team

      + +MyOOS [Dumper]
      + +

      MyOOS [Dumper] Help

      + +

      Download

      +

      You can always get the latest versions via GitHub
      . +https://github.com/r23/MyOOS-Dumper/releases

      + + +

      System requirement

      . +

      The script works on any server (Windows, Linux, ...)
      +with PHP >= version 7.4 with GZip support, MySQL (version 4.1 or higher), JavaScript (must be enabled)

      . +

      Copy the mod folder from the MyOOS archive to a separate working folder.

      . + +

      Installation

      . +The installation process is straightforward. +

      From the MyOOS archive, copy the mod folder into any folder.
      +Upload all the files from the mod folder to your web server. (For example, to the lowest level in [server web directory/]mod)
      +... done!
      +You can now call MyOOS [Dumper] in your web browser by "https://example.com/mod/",
      +to complete the installation. Just follow the instructions.
      +
      Note:
      If on your server the script is not allowed to create directories,
      +you have to do this manually, because MyOOS [Dumper] stores the data ordered in +directories.
      +The script aborts with an appropriate statement!
      +After you have created the directories (according to the hint), it runs normally and without restrictions.
      + +

      Perl script instructions

      . +Most have a cgi-bin directory where perl can be run.
      +This is usually accessible by browser via http://www.example.com/cgi-bin/.
      +
      +For this case, please perform the following steps:

      . + +1. call the Backup page in MyOOS [Dumper] and click on "Backup Perl".
      +2. copy the path that is behind entry in crondump.pl for $absolute_path_of_configdir:.
      +3. open the file "crondump.pl" in the editor.
      +4. enter the copied path there at absolute_path_of_configdir (no spaces).
      +5. save crondump.pl .
      +6. copy crondump.pl, as well as perltest.pl and simpletest.pl into the cgi-bin directory (ascii mode in FTP).
      +7. give the files the permissions 755.
      +7b. If the ending cgi is desired, change the ending of all 3 files from pl -> cgi (rename).
      +8. call the configuration in MyOOS [Dumper].
      +9. select the page Cronscript.
      +10. change perl execution path to /cgi-bin/ .
      +10b. If the scripts have .pl, change the file extension to .cgi .
      +11.Save the configuration.

      + +Done, the scripts can now be called from the backup page.

      . + +For those who can run Perl in all directories, the following steps will suffice:

      . + +1. call in the MyOOS [Dumper] the page Backup.
      +2. copy the path that is behind entry in crondump.pl for $absolute_path_of_configdir:.
      +3. open the file "crondump.pl" in the editor.
      +4. enter the copied path there at absolute_path_of_configdir (no spaces).
      +5. save crondump.pl .
      +6. give the files the permissions 755.
      +6b. If the extension cgi is desired, change the extension of all 3 files from pl -> cgi (rename).
      +(ev. 10b+11 from above)
      +
      + +Windowsuser have to change the first line of all scripts, there is the path of Perl. Example:
      +instead of: #!/usr/bin/perl -w
      +now: #!C:_usr/bin/perl.exe -w
      + +

      Operation

        . + +
        Menu
        . +In the selection list above you set the database.
        +All actions refer to the database set here. + +
        Home
        +Here you can learn about your system, the different installed versions and details about the +versions and details about the configured databases.
        +If you click on the database name, you will see a list of the tables with the number of entries +with the number of entries, the size and the last update date. + +
        Configuration
        . +Here you can edit your configuration, save it or restore the initial configuration. +restore. +

          +
        • Configured databases: the listing of configured databases. The active database is listed in bold.
        • +
        • Table prefix: here you can specify (for each database) a prefix. This is a filter that will take into account for dumps only the tables that start with this prefix (for example, all tables that start with "phpBB_"). If you want all tables in this database to be saved, just leave the field empty.
        • . +
        • GZip compression: Here you can enable compression. It is recommended to enable it, because the files will be much smaller after all and disk space is always scarce.
        • . +
        • Email with Dumpfile: If this option is enabled, an email with the dump as an attachment will be sent after the backup is complete (caution, compression should absolutely be on, otherwise the attachment will be too large and may not be sent!).
        • +
        • Email address: Recipient address for the email.
        • +
        • Sender of the email: this address appears as the sender in the email.
        • +
        • FTP Transfer: If this option is enabled, the backup file will be sent via FTP after the backup is completed.
        • +
        • FTP Server: The address of the FTP server (e.g. ftp.mybackups.com).
        • +
        • FTP Server Port: The port of the FTP server (usually 21).
        • +
        • FTP User: The username of the FTP account.
        • +
        • FTP Password: The password of the FTP account.
        • +
        • FTP Upload Folder: The directory where the backup file should go (upload permissions must exist!).
        • +
        • Automatic deletion of backups: If this option is enabled, older backups will be deleted automatically according to the following rules.
        • . +
        • Number of backup files: A value > 0 deletes all backup files except for the number specified here.
        • +
        • Language: here you specify the language for the interface.
        • +
        + +
        Administration
        . +This is where the actual actions are performed.
        +It will show you all the files in the backup directory. +For the actions "Restore" and "Delete" a file must be selected. +
          +
        • Restore: This will update the database with the selected backup file.
        • +
        • Delete: This lets you delete the selected backup file.
        • +
        • Start new backup: Here you start a new backup (dump) according to the parameters set in the configuration.
        • . +
        + +
        Log
        +Here you can see and delete the log entries. +
        Credits / Help
        +this page. +
      diff --git a/msd/language/sw/lang.php b/msd/language/sw/lang.php new file mode 100644 index 0000000..96eecb1 --- /dev/null +++ b/msd/language/sw/lang.php @@ -0,0 +1,109 @@ +existerar ej'; +$lang['L_VOM'] = 'den'; +$lang['L_MYSQLVARS'] = 'MySQL-variabler'; +$lang['L_MYSQLSYS'] = 'MySQL-kommandon'; +$lang['L_STATUS'] = 'Status'; +$lang['L_PROZESSE'] = 'Processer'; +$lang['L_INFO_NOVARS'] = 'inga variabler'; +$lang['L_INHALT'] = 'Innehåll'; +$lang['L_INFO_NOSTATUS'] = 'ingen status'; +$lang['L_INFO_NOPROCESSES'] = 'inga aktuella processer'; +$lang['L_FM_FREESPACE'] = 'Ledigt utrymme på servern'; +$lang['L_LOAD_DATABASE'] = 'Ladda om databaserna'; +$lang['L_HOME'] = 'Hem'; +$lang['L_CONFIG'] = 'Konfigurering'; +$lang['L_DUMP'] = 'Backup'; +$lang['L_RESTORE'] = 'Återställning'; +$lang['L_FILE_MANAGE'] = 'Administrering'; +$lang['L_LOG'] = 'Logg'; +$lang['L_CHOOSE_DB'] = 'Välj databas'; +$lang['L_CREDITS'] = 'Credits / Hjälp'; +$lang['L_MULTI_PART'] = 'Backup uppdelad i flera filer'; +$lang['L_LOGFILENOTWRITABLE'] = 'Loggfil kan ej skrivas!'; +$lang['L_SQL_ERROR1'] = 'Fel i förfrågningen:'; +$lang['L_SQL_ERROR2'] = 'MySQL svarar:'; +$lang['L_UNKNOWN'] = 'okänd'; +$lang['L_UNKNOWN_NUMBER_OF_RECORDS'] = 'okänt'; +$lang['L_OK'] = 'OK'; +$lang['L_CRON_COMPLETELOG'] = 'Logga hela utmatningen'; +$lang['L_NO'] = 'nej'; +$lang['L_CREATE_DATABASE'] = 'skapa ny databas'; +$lang['L_EXPORTFINISHED'] = 'Exporten avslutad.'; +$lang['L_SQL_BROWSER'] = 'SQL-läsare'; +$lang['L_SERVER'] = 'Server'; +$lang['L_MYSQL_CONNECTION_ENCODING'] = 'MySQL-serverns standardkodering'; +$lang['L_TITLE_SHOW_DATA'] = 'Visa data'; +$lang['L_PRIMARYKEY_CONFIRMDELETE'] = 'Vill du verkligen radera primärnyckeln?'; +$lang['L_SETPRIMARYKEYSFOR'] = 'Sätt nya primärnycklar för tabellen'; +$lang['L_PRIMARYKEY_FIELD'] = 'Nyckelfält'; +$lang['L_PRIMARYKEYS_SAVE'] = 'Spara primärnycklar'; +$lang['L_CANCEL'] = 'Avbryt'; +$lang['L_VISIT_HOMEPAGE'] = 'Besök hemsidan'; +$lang['L_SECONDS'] = 'sekunder'; +$lang['L_BACKUPS'] = 'backup(er)'; +$lang['L_MINUTES'] = 'minuter'; +$lang['L_PAGE_REFRESHS'] = 'sidvisningar'; +$lang['L_MINUTE'] = 'minut'; +$lang['L_SETKEYSFOR'] = 'Set new indexes for table'; +$lang['L_KEY_CONFIRMDELETE'] = 'Really delete index?'; diff --git a/msd/language/sw/lang_config_overview.php b/msd/language/sw/lang_config_overview.php new file mode 100644 index 0000000..fc01f6c --- /dev/null +++ b/msd/language/sw/lang_config_overview.php @@ -0,0 +1,126 @@ +%s
      i %s'; +$lang['L_FTP'] = 'FTP'; +$lang['L_SFTP_SEND_TO'] = 'till %s
      i %s'; +$lang['L_SFTP'] = 'SFTP'; +$lang['L_EMAIL_CC'] = 'CC-mottagare'; +$lang['L_NAME'] = 'Namn'; +$lang['L_CONFIRM_CONFIGFILE_DELETE'] = 'Ska konfigureringsfilen %s verkligen raderas?'; +$lang['L_ERROR_DELETING_CONFIGFILE'] = 'Fel: konfigureringsfilen %s kunde ej raderas!'; +$lang['L_SUCCESS_DELETING_CONFIGFILE'] = 'Konfigureringsfilen %s har raderats.'; +$lang['L_SUCCESS_CONFIGFILE_CREATED'] = 'Konfigureringsfilen %s har skapats.'; +$lang['L_ERROR_CONFIGFILE_NAME'] = 'Filnamnet "%s" innehåller ogiltiga tecken.'; +$lang['L_CREATE_CONFIGFILE'] = 'Skapa en ny konfigureringsfil'; +$lang['L_ERROR_LOADING_CONFIGFILE'] = 'Konfigureringsfilen "%s" kunde ej laddas.'; +$lang['L_BACKUP_DBS_PHP'] = 'backup av databaser (PHP)'; +$lang['L_BACKUP_DBS_PERL'] = 'backup av databaser (PERL)'; +$lang['L_CRON_COMMENT'] = 'Mata in kommentar'; +$lang['L_AUTODETECT'] = 'identifiera automatiskt'; diff --git a/msd/language/sw/lang_dump.php b/msd/language/sw/lang_dump.php new file mode 100644 index 0000000..703d6a5 --- /dev/null +++ b/msd/language/sw/lang_dump.php @@ -0,0 +1,53 @@ +%s`.'; +$lang['L_DUMP_ENDERGEBNIS'] = '%s tabeller med totalt %s dataposter har säkrats.
      '; +$lang['L_MAILERROR'] = 'Tyvärr uppträdde ett fel när epostmeddelandet skickades!'; +$lang['L_EMAILBODY_ATTACH'] = 'Här kommer backupen av din MySQLdatabas.
      Backup av databasen `%s` +

      Följande fil har skapats:

      %s

      Med vänliga hälsningar

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_MP_NOATTACH'] = 'En multipart-backup har skapats.
      Backuperna levereras EJ som bilaga i mail!
      Backup av databasen `%s` +

      Följande filer har skapats:

      %s


      Med vänliga hälsningar

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_MP_ATTACH'] = 'En multipart-backup har skapats.
      Backupen levereras i separata mail!
      Backup av databasen `%s` +

      Följande filer har skapats:

      %s


      Med vänliga hälsningar

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_FOOTER'] = '


      Med vänliga hälsningar

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_TOOBIG'] = 'Backupen överskrider den maximala storleken på %s och har därför ej bifogats.
      Backup av databasen `%s`

      Följande fil har skapats:

      %s

      Vänliga hälsningar
      Din MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_NOATTACH'] = 'Backuperna levereras EJ som bilaga i mail!
      Backup av databasen `%s`

      Följande filer har skapats:

      %s


      Med vänliga hälsningar

      MyOOS [Dumper]
      '; +$lang['L_EMAIL_ONLY_ATTACHMENT'] = '... endast bilagan'; +$lang['L_TABLESELECTION'] = 'Välj tabeller'; +$lang['L_SELECTALL'] = 'markera alla'; +$lang['L_DESELECTALL'] = 'Avmarkera alla'; +$lang['L_STARTDUMP'] = 'Starta backup'; +$lang['L_LASTBUFROM'] = 'senaste uppdatering den'; +$lang['L_NOT_SUPPORTED'] = 'Denna backup har inget stöd för den funktionen.'; +$lang['L_MULTIDUMP'] = 'Multidump: %d databaser har säkrats.'; +$lang['L_FILESENDFTP'] = 'skickar filen via FTP ... var god vänta.'; +$lang['L_FTPCONNERROR'] = 'FTP-förbindelsen kunde ej upprättas! Förbindelse med '; +$lang['L_FTPCONNERROR1'] = 'som användare'; +$lang['L_FTPCONNERROR2'] = 'ej möjligt'; +$lang['L_FTPCONNERROR3'] = 'FTP-överföringen var korrupt!'; +$lang['L_FTPCONNECTED1'] = 'Ansluten till'; +$lang['L_FTPCONNECTED2'] = 'hos'; +$lang['L_FTPCONNECTED3'] = 'överförd'; +$lang['L_FILESENDSFTP'] = 'skickar filen via SFTP ... var god vänta.'; +$lang['L_SFTPCONNERROR'] = 'SFTP-förbindelsen kunde ej upprättas! Förbindelse med '; +$lang['L_NR_TABLES_SELECTED'] = '- med %s valda tabeller'; +$lang['L_NR_TABLES_OPTIMIZED'] = '%s tabeller har optimerats.'; +$lang['L_DUMP_ERRORS'] = '

      %s fel har uppträtt: visa

      '; +$lang['L_FATAL_ERROR_DUMP'] = "Kritiskt fel: CREATE-kommandot i tabellen '%s' i databasen '%s' kunde ej läsas!"; diff --git a/msd/language/sw/lang_filemanagement.php b/msd/language/sw/lang_filemanagement.php new file mode 100644 index 0000000..f55d18f --- /dev/null +++ b/msd/language/sw/lang_filemanagement.php @@ -0,0 +1,75 @@ +%s"'; +$lang['L_DELETE_FILE_ERROR'] = 'Filen "%s" kunde ej raderas!'; +$lang['L_FM_DUMP_HEADER'] = 'Backup'; +$lang['L_DOCRONBUTTON'] = 'Utför Perl-cronscript'; +$lang['L_DOPERLTEST'] = 'Testa Perl-modulerna'; +$lang['L_DOSIMPLETEST'] = 'Testa Perl'; +$lang['L_PERLOUTPUT1'] = 'Angivelse i crondump.pl för absolute_path_of_configd'; +$lang['L_PERLOUTPUT2'] = 'Browseradress eller adress för extern crontab'; +$lang['L_PERLOUTPUT3'] = 'Shelladress eller adress för crontab'; +$lang['L_RESTORE_OF_TABLES'] = 'Återställning av bestämda tabeller'; +$lang['L_CONVERTER'] = 'Backup-konverterare'; +$lang['L_CONVERT_FILE'] = 'fil som skall konverteras'; +$lang['L_CONVERT_FILENAME'] = 'Målfilens namn (utan filändelse)'; +$lang['L_CONVERTING'] = 'Konvertering'; +$lang['L_CONVERT_FILEREAD'] = "Filen '%s' läses in"; +$lang['L_CONVERT_FINISHED'] = "Konverteringen avslutad, '%s' har skapats."; +$lang['L_NO_MOD_BACKUPFILE'] = 'Filer skapade med andra program'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Maximal filstorlek'; +$lang['L_MAX_UPLOAD_SIZE_INFO'] = 'Om din backup-fil är större än det angivna värdet så måste du ladda upp den till mappen "work/backup" via FTP. Därefter visas filen här i översikten och kan väljas för återställning.'; +$lang['L_ENCODING'] = 'Kodering'; +$lang['L_FM_CHOOSE_ENCODING'] = 'Välj backupfilens kodering'; +$lang['L_CHOOSE_CHARSET'] = 'Tyvärr kunde ej fastställas automatiskt med vilken teckensats denna backupfil har skapats.
      Du måste ange koderingen manuellt.
      Därefter ställer MyOOS [Dumper] in förbindelseparametrarna till MySQL-servern till den valda teckensatsen och startar återställningen.
      Om datan återges med fel specialtecken efter återställningen så bör du upprepa återställningen med en annan inställning för teckensatsen.
      Lycka till.'; +$lang['L_DOWNLOAD_FILE'] = 'Ladda hem filen'; +$lang['L_BACKUP_NOT_POSSIBLE'] = 'A backup of the system database `%s` is not possible!'; diff --git a/msd/language/sw/lang_help.php b/msd/language/sw/lang_help.php new file mode 100644 index 0000000..4c93e08 --- /dev/null +++ b/msd/language/sw/lang_help.php @@ -0,0 +1,37 @@ +installationen har avslutats --> starta MyOOS [Dumper]
      '; +$lang['L_INSTALL_TOMENU'] = 'till huvudmenyn'; +$lang['L_INSTALLMENU'] = 'Huvudmeny'; +$lang['L_STEP'] = 'Steg'; +$lang['L_INSTALL'] = 'Installation'; +$lang['L_UNINSTALL'] = 'Avinstallering'; +$lang['L_TOOLS'] = 'Verktyg'; +$lang['L_EDITCONF'] = 'Redigera konfigureringen'; +$lang['L_OSWEITER'] = 'vidare utan att spara'; +$lang['L_ERRORMAN'] = 'Ett fel uppträdde när konfigureringen skrevs!
      Redigera filen '; +$lang['L_MANUELL'] = 'manuellt'; +$lang['L_CREATEDIRS'] = 'skapar mappar'; +$lang['L_INSTALL_CONTINUE'] = 'fortsätt installationen'; +$lang['L_CONNECTTOMYSQL'] = ' förbind med mysql '; +$lang['L_DBPARAMETER'] = 'Databas-parametrar'; +$lang['L_CONFIGNOTWRITABLE'] = 'Kan ej skriva i filen "config.php". Ställ in motsvarande behörighet med ditt FTP-program, t.ex. CHMOD 777.'; +$lang['L_DBCONNECTION'] = 'Databas-förbindelse'; +$lang['L_CONNECTIONERROR'] = 'Fel: förbindelse kunde ej upprättas.'; +$lang['L_CONNECTION_OK'] = 'Databas-förbindelse har upprättats.'; +$lang['L_SAVEANDCONTINUE'] = 'spara och fortsätt installationen'; +$lang['L_CONFBASIC'] = 'Grundinställningar'; +$lang['L_INSTALL_STEP2FINISHED'] = 'Inställningarna har sparats.'; +$lang['L_INSTALL_STEP2_1'] = 'Fortsätt installationen med standardkonfigureringen'; +$lang['L_LASTSTEP'] = 'Avsluta installationen'; +$lang['L_IDOMANUAL'] = 'Jag skapar mapparna manuellt'; +$lang['L_DOFROM'] = 'utgående ifrån'; +$lang['L_FTPMODE2'] = 'Skapa mapparna via FTP:'; +$lang['L_CONNECT'] = 'förbind'; +$lang['L_DIRS_CREATED'] = 'Alla mappar har skapats.'; +$lang['L_CONNECT_TO'] = 'förbind med'; +$lang['L_CHANGEDIR'] = 'Hoppa till mapp'; +$lang['L_CHANGEDIRERROR'] = 'Hopp till mapp ej möjligt'; +$lang['L_FTP_OK'] = 'FTP-parametrara är ok.'; +$lang['L_CREATEDIRS2'] = 'Skapa mappar'; +$lang['L_FTP_NOTCONNECTED'] = 'FTP-förbindelsen kunde ej skapas!'; +$lang['L_CONNWITH'] = 'Förbindelse med'; +$lang['L_ASUSER'] = 'som användare'; +$lang['L_NOTPOSSIBLE'] = 'ej möjligt'; +$lang['L_DIRCR1'] = 'skapar arbetsmapp'; +$lang['L_DIRCR2'] = 'skapar backupmapp'; +$lang['L_DIRCR4'] = 'skapar loggmapp'; +$lang['L_DIRCR5'] = 'skapar konfigureringsmapp'; +$lang['L_INDIR'] = 'nu i mappen'; +$lang['L_CHECK_DIRS'] = 'kontrollera mina mappar'; +$lang['L_DISABLEDFUNCTIONS'] = 'Deaktiverade funktioner'; +$lang['L_NOFTPPOSSIBLE'] = 'Det står inga FTP-funktioner till förfogande!'; +$lang['L_NOGZPOSSIBLE'] = 'Det står inga komprimeringsfunktioner till förfogande!'; +$lang['L_UI1'] = 'Alla arbetsmappar inkl. eventuella backuper som finns i mapparna raderas!'; +$lang['L_UI2'] = 'Är du säker på att du vill det?'; +$lang['L_UI3'] = 'nej, avbryt genast'; +$lang['L_UI4'] = 'ja, fortsätt'; +$lang['L_UI5'] = 'radera arbetsmapp'; +$lang['L_UI6'] = 'allt har raderats'; +$lang['L_UI7'] = 'Radera skriptmappen'; +$lang['L_UI8'] = 'en nivå upp'; +$lang['L_UI9'] = 'Ett fel har uppträtt, radering var ej möjlig.

      Felet uppträdde för mapp'; +$lang['L_IMPORT'] = 'Importera konfigureringen'; +$lang['L_IMPORT3'] = 'Konfigureringen har laddats ...'; +$lang['L_IMPORT4'] = 'Konfigureringen har sparats.'; +$lang['L_IMPORT5'] = 'Starta MyOOS [Dumper]'; +$lang['L_IMPORT6'] = 'Installationsmenyn'; +$lang['L_IMPORT7'] = 'Konfiguration uploaden'; +$lang['L_IMPORT8'] = 'tillbaka till uppladdningen'; +$lang['L_IMPORT9'] = 'Detta är ingen konfigureringsbackup!'; +$lang['L_IMPORT10'] = 'Konfigureringen har laddats upp ...'; +$lang['L_IMPORT11'] = 'Fel: ett problem uppträdde när sql_statements skrevs.'; +$lang['L_IMPORT12'] = 'Fel: ett problem uppstod när config.php skrevs.'; +$lang['L_INSTALL_HELP_PORT'] = '(tom = standardport)'; +$lang['L_INSTALL_HELP_SOCKET'] = '(tom = standardsocket)'; +$lang['L_TRYAGAIN'] = 'försök igen'; +$lang['L_SOCKET'] = 'Socket'; +$lang['L_PORT'] = 'Port'; +$lang['L_FOUND_DB'] = 'hittad databas:'; +$lang['L_FM_FILEUPLOAD'] = 'Ladda upp fil'; +$lang['L_PASS'] = 'Lösenord'; +$lang['L_NO_DB_FOUND_INFO'] = 'Förbindelsen till databasen kunde upprättas.
      Dina inloggningsinformationer är giltiga har accepterats av MySQL-servern.
      Tyvärr kunde MyOOS [Dumper] inte hitta några databaser.
      Automatisk detektering spärras av vissa webbhotell.
      Du måste ange databasen efter installationen, menypunkt "Konfigurering" "Visa förbindelseparametrar".
      Genomför detta steg omedelbart efter installationen.'; +$lang['L_ENTER_DB_INFO'] = 'Klicka först på knappen "förbind med mysql". Endast om denna förbindelse ej fungerar behöver du mata in data här.'; diff --git a/msd/language/sw/lang_log.php b/msd/language/sw/lang_log.php new file mode 100644 index 0000000..30d081a --- /dev/null +++ b/msd/language/sw/lang_log.php @@ -0,0 +1,7 @@ +Skapa filerna manuellt med följande innehåll'; +$lang['L_HTACC_CHECK_ERROR'] = 'It could not be checked whether the program is protected!
      The simulated external access could not be carried out.'; +$lang['L_HTACC_NOT_NEEDED'] = 'The program is protected by higher-level authorizations; local directory protection is not required.'; +$lang['L_HTACC_COMPLETE'] = 'The program is protected, the directory protection is complete.'; +$lang['L_HTACC_INCOMPLETE'] = 'The program is not protected, the directory protection is incomplete!'; +$lang['L_HTACC_PROPOSED'] = 'The program is not protected, directory protection is strongly recommended!'; +$lang['L_HTACC_EDIT'] = 'Editera .htaccess-skyddet'; +$lang['L_HTACCESS18'] = 'Skapa .htaccess i'; +$lang['L_HTACCESS19'] = 'Ladda om'; +$lang['L_HTACCESS20'] = 'Utför skriptet'; +$lang['L_HTACCESS21'] = 'Lägg till handler'; +$lang['L_HTACCESS22'] = 'Gör utförbart'; +$lang['L_HTACCESS23'] = 'Mapp-listning'; +$lang['L_HTACCESS24'] = 'Fel-dokument'; +$lang['L_HTACCESS25'] = 'Aktivera rewrite'; +$lang['L_HTACCESS26'] = 'Deny / Allow'; +$lang['L_HTACCESS27'] = 'Redirect'; +$lang['L_HTACCESS28'] = 'Fel-logg'; +$lang['L_HTACCESS29'] = 'ytterligare exempel och dokumentation'; +$lang['L_HTACCESS30'] = 'Provider'; +$lang['L_HTACCESS31'] = 'allmänt'; +$lang['L_HTACCESS32'] = 'OBS! .htaccess har direkt inverkan på servern.
      Om .htaccess ställs in på fel sätt kan sidan ej nås.'; +$lang['L_DISABLEDFUNCTIONS'] = 'Deaktiverade funktioner'; +$lang['L_NOGZPOSSIBLE'] = 'Det står inga GZIP-funktioner till förfogande eftersom zlib ej har installerats!'; +$lang['L_DELETE_HTACCESS'] = 'Avlägsna mappskyddet (radera .htaccess-filen)'; +$lang['L_WRONG_RIGHTS'] = "Filen eller mappen '%s' kan ej skrivas till.
      Antingen har den fel ägare (Owner) eller fel behörigheter (Chmod).
      Ställ in rätt attribut med ett FTP-program.
      Filen eller mappen måste ha %s.
      "; +$lang['L_CANT_CREATE_DIR'] = "Mappen '%s' kunde ej skapas. Skapa den med ditt FTP-program."; +$lang['L_TABLE_TYPE'] = 'Typ'; +$lang['L_CHECK'] = 'kontrollera'; +$lang['L_OS'] = 'Operativsystem'; +$lang['L_MOD_VERSION'] = 'MyOOS [Dumper]-version'; +$lang['L_NEW_MOD_VERSION_INFO'] = 'There is a new version of MyOOS [Dumper] available.'; +$lang['L_UPDATED_IMPORTANT'] = 'Important: Before updating, please backup your files.'; +$lang['L_UPDATE'] = 'Update now'; +$lang['L_NEW_MOD_VERSION'] = 'New Version'; +$lang['L_MYSQL_VERSION'] = 'MySQL-version'; +$lang['L_PHP_VERSION'] = 'PHP-version'; +$lang['L_MAX_EXECUTION_TIME'] = 'Maximal exekveringstid'; +$lang['L_PHP_EXTENSIONS'] = 'PHP-extensioner'; +$lang['L_MEMORY'] = 'Minne'; +$lang['L_FILE_MISSING'] = 'kunde ej hitta filen'; +$lang['L_INSTALLING_UPDATES'] = 'Installing Updates'; +$lang['L_UPDATE_SUCCESSFUL'] = 'Update successful'; +$lang['L_UPDATE_FAILED'] = 'Update failed'; +$lang['L_UP_TO_DATE'] = 'Current Version is up to date'; diff --git a/msd/language/sw/lang_restore.php b/msd/language/sw/lang_restore.php new file mode 100644 index 0000000..3d9b10a --- /dev/null +++ b/msd/language/sw/lang_restore.php @@ -0,0 +1,19 @@ +%d av %d tabeller skapats.'; +$lang['L_FILE_MISSING'] = 'kunde ej hitta filen'; +$lang['L_RESTORE_DB'] = "Databas '%s' på server '%s'."; +$lang['L_RESTORE_COMPLETE'] = '%s tabeller har skapats.'; +$lang['L_RESTORE_RUN1'] = '
      Hittills har %s av %s dataposter överförts.'; +$lang['L_RESTORE_RUN2'] = "
      För närvarande analyseras datan i tabell '%s'.

      "; +$lang['L_RESTORE_COMPLETE2'] = '%s dataposter har överförts.'; +$lang['L_RESTORE_TABLES_COMPLETED'] = 'Hittills har %d av %d tabeller skapats.'; +$lang['L_RESTORE_TOTAL_COMPLETE'] = '
      Grattis!

      Databasen har återställts komplett.
      All data ur backupfilen har överförts till databasen.

      Allt är färdigt. :-)'; +$lang['L_DB_SELECT_ERROR'] = "
      Fel:
      val av databasen '"; +$lang['L_DB_SELECT_ERROR2'] = "' misslyckades!"; +$lang['L_FILE_OPEN_ERROR'] = 'Fel: filen kunde ej öppnas.'; +$lang['L_PROGRESS_OVER_ALL'] = 'Framsteg totalt'; +$lang['L_BACK_TO_OVERVIEW'] = 'Databasöversikt'; +$lang['L_RESTORE_RUN0'] = '
      Hittills har %s dataposter överförts.'; +$lang['L_UNKNOWN_SQLCOMMAND'] = 'Okänt SQL-kommando:'; +$lang['L_NOTICES'] = 'Hänvisningar'; diff --git a/msd/language/sw/lang_sql.php b/msd/language/sw/lang_sql.php new file mode 100644 index 0000000..2628ba2 --- /dev/null +++ b/msd/language/sw/lang_sql.php @@ -0,0 +1,190 @@ +%s rader har exporterats'; +$lang['L_CSV_FIELDCOUNT_NOMATCH'] = 'Antalet tabell-fält stämmer ej överens med antalet som skall importeras (%d istället för %d).'; +$lang['L_CSV_FIELDSLINES'] = '%d fält fastställda, totalt %d rader'; +$lang['L_CSV_ERRORCREATETABLE'] = 'Fel när tabellen `%s` skulle skapas!'; +$lang['L_FM_UPLOADFILEREQUEST'] = 'Ange en fil.'; +$lang['L_CSV_NODATA'] = 'Ingen data kunde hittas för import!'; +$lang['L_SQLLIB_GENERALFUNCTIONS'] = 'allmäna funktioner'; +$lang['L_SQLLIB_RESETAUTO'] = 'återställ auto-värde'; +$lang['L_SQLLIB_BOARDS'] = 'Forum'; +$lang['L_SQLLIB_DEACTIVATEBOARD'] = 'Deaktivera forumet'; +$lang['L_SQLLIB_ACTIVATEBOARD'] = 'Aktivera forumet'; +$lang['L_SQL_NOTABLESSELECTED'] = 'Inga tabeller har valts!'; +$lang['L_TOOLS'] = 'Verktyg'; +$lang['L_TOOLS_TOOLBOX'] = 'Välj databas / Databasfunktioner / Import/Export '; +$lang['L_SQL_OPENFILE'] = 'Öppna SQL-fil'; +$lang['L_SQL_OPENFILE_BUTTON'] = 'Ladda upp'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Maximal filstorlek'; +$lang['L_SQL_SEARCH'] = 'Sökning'; +$lang['L_SQL_SEARCHWORDS'] = 'Sökord'; +$lang['L_START_SQL_SEARCH'] = 'Starta sökningen'; +$lang['L_RESET_SEARCHWORDS'] = 'Återställ inmatningen'; +$lang['L_SEARCH_OPTIONS'] = 'Sökinställningar'; +$lang['L_SEARCH_RESULTS'] = 'Sökningen på "%s" i tabellen "%s" gav följande resultat'; +$lang['L_SEARCH_NO_RESULTS'] = 'Sökningen på "%s" i tabellen "%s" gav inga träffar!'; +$lang['L_NO_ENTRIES'] = 'Tabellen "%s" är tom och har inga poster.'; +$lang['L_SEARCH_ACCESS_KEYS'] = 'Bläddra: framåt=ALT+V, tillbaka=ALT+C'; +$lang['L_SEARCH_OPTIONS_OR'] = 'en kolumn måste innehålla minst ett sökord (ELLER-sökning)'; +$lang['L_SEARCH_OPTIONS_CONCAT'] = 'en datapost måste innehålla alla sökord, dessa kan dock befinna sig i olika kolumner (stor serverbelastning!)'; +$lang['L_SEARCH_OPTIONS_AND'] = 'en kolumn måste innehålla alla sökord (OCH-sökning)'; +$lang['L_SEARCH_IN_TABLE'] = 'Sök i tabell'; +$lang['L_ERROR_NO_FIELDS'] = 'Search error: it could not be determined which fields the table "%s" has!'; +$lang['L_SQL_EDIT_TABLESTRUCTURE'] = 'Bearbeta tabellens struktur'; +$lang['L_DEFAULT_CHARSET'] = 'Standardteckensats'; +$lang['L_TITLE_KEY_PRIMARY'] = 'Primär nyckel'; +$lang['L_TITLE_KEY_UNIQUE'] = 'Unik nyckel'; +$lang['L_TITLE_INDEX'] = 'Index'; +$lang['L_TITLE_KEY_FULLTEXT'] = 'Fulltextnyckel'; +$lang['L_TITLE_NOKEY'] = 'Ingen nyckel'; +$lang['L_TITLE_SEARCH'] = 'Sök'; +$lang['L_TITLE_MYSQL_HELP'] = 'MySQL dokumentation'; +$lang['L_TITLE_UPLOAD'] = 'Ladda upp SQL-fil'; +$lang['L_PRIMARYKEY_DELETED'] = 'Den primära nyckeln har raderats'; +$lang['L_PRIMARYKEY_NOTFOUND'] = 'Den primära nyckeln kunde ej hittas'; +$lang['L_PRIMARYKEYS_CHANGED'] = 'Den primära nyckeln har ändrats'; +$lang['L_PRIMARYKEYS_CHANGINGERROR'] = 'Ett fel uppträdde när den primära nyckeln skulle ändras'; +$lang['L_SQL_VIEW_COMPACT'] = 'Visning: kompakt'; +$lang['L_SQL_VIEW_STANDARD'] = 'Visning: normal'; +$lang['L_FIELDS_OF_TABLE'] = 'Fält i tabellen'; +$lang['L_ENGINE'] = 'Engine'; +$lang['L_USERNAME'] = 'Användarnamn'; +$lang['L_PASSWORD'] = 'Lösenord'; +$lang['L_PASSWORD_REPEAT'] = 'Upprepa lösenord'; +$lang['L_INFO_SIZE'] = 'Storlek'; +$lang['L_TABLE_TYPE'] = 'Typ'; +$lang['L_KEY_DELETED'] = 'Index deleted'; +$lang['L_KEY_DELETEERROR'] = 'Error deleting index'; +$lang['L_KEY_ADDED'] = 'Index added'; +$lang['L_KEY_ADDERROR'] = 'Error adding index'; diff --git a/msd/language/tr/help.html b/msd/language/tr/help.html new file mode 100644 index 0000000..fe88bc4 --- /dev/null +++ b/msd/language/tr/help.html @@ -0,0 +1,147 @@ +
      +

      MyOOS [Dumper] based on MySQLDumper 1.24.4

      + +

      About this project

      +

      MyOOS [Dumper] is an improved version of MySQLDumper 1.24.4 (January 24, 2011). This enhancement takes into account the development of PHP.

      . +

      Most of all stability, security and handling are the main focus of MyOOS [Dumper]. But also an attractive template is included, which can be edited and adapted to your own needs.

      . + + +

      MyOOS [Dumper] is a backup program for MySQL databases, written in PHP and Perl. With it, backup copies of the data (store, blog, etc.) can be created and restored if necessary. Especially for web space without shell access, MyOOS [Dumper] is a useful alternative.

      . + +

      The idea for MySQLDumper came from Daniel Schlichtholz. He opened the MySQLDumper forum in 2004, whereupon programmers wrote new scripts and extended existing ones.

      + + + +

      Wish List / Future Attractions

      . +

      Do you have any suggestions for improvements? Feel free to contact the development team via the forum https://foren.myoos.de/viewforum.php?f=41.

      + + +

      Contribute

      +

      If you would like to help us improve the MyOOS project, we welcome your pull requests via GitHub here.

      +https://github.com/r23/MyOOS-Dumper/ + + +

      Financial Support

      . +

      You can use PayPal Me
      . +https://www.paypal.com/paypalme/r23de?locale.x=de_DE

      + +

      or via the QR code
      . +Financial support for MyOOS [Dumper]

      + +Send money to the MyOOS project.
      + +

      We hope you enjoy this project.

      The MyOOS [Dumper] Team

      + +MyOOS [Dumper]
      + +

      MyOOS [Dumper] Help

      + +

      Download

      +

      You can always get the latest versions via GitHub
      . +https://github.com/r23/MyOOS-Dumper/releases

      + + +

      System requirement

      . +

      The script works on any server (Windows, Linux, ...)
      +with PHP >= version 7.4 with GZip support, MySQL (version 4.1 or higher), JavaScript (must be enabled)

      . +

      Copy the mod folder from the MyOOS archive to a separate working folder.

      . + +

      Installation

      . +The installation process is straightforward. +

      From the MyOOS archive, copy the mod folder into any folder.
      +Upload all the files from the mod folder to your web server. (For example, to the lowest level in [server web directory/]mod)
      +... done!
      +You can now call MyOOS [Dumper] in your web browser by "https://example.com/mod/",
      +to complete the installation. Just follow the instructions.
      +
      Note:
      If on your server the script is not allowed to create directories,
      +you have to do this manually, because MyOOS [Dumper] stores the data ordered in +directories.
      +The script aborts with an appropriate statement!
      +After you have created the directories (according to the hint), it runs normally and without restrictions.
      + +

      Perl script instructions

      . +Most have a cgi-bin directory where perl can be run.
      +This is usually accessible by browser via http://www.example.com/cgi-bin/.
      +
      +For this case, please perform the following steps:

      . + +1. call the Backup page in MyOOS [Dumper] and click on "Backup Perl".
      +2. copy the path that is behind entry in crondump.pl for $absolute_path_of_configdir:.
      +3. open the file "crondump.pl" in the editor.
      +4. enter the copied path there at absolute_path_of_configdir (no spaces).
      +5. save crondump.pl .
      +6. copy crondump.pl, as well as perltest.pl and simpletest.pl into the cgi-bin directory (ascii mode in FTP).
      +7. give the files the permissions 755.
      +7b. If the ending cgi is desired, change the ending of all 3 files from pl -> cgi (rename).
      +8. call the configuration in MyOOS [Dumper].
      +9. select the page Cronscript.
      +10. change perl execution path to /cgi-bin/ .
      +10b. If the scripts have .pl, change the file extension to .cgi .
      +11.Save the configuration.

      + +Done, the scripts can now be called from the backup page.

      . + +For those who can run Perl in all directories, the following steps will suffice:

      . + +1. call in the MyOOS [Dumper] the page Backup.
      +2. copy the path that is behind entry in crondump.pl for $absolute_path_of_configdir:.
      +3. open the file "crondump.pl" in the editor.
      +4. enter the copied path there at absolute_path_of_configdir (no spaces).
      +5. save crondump.pl .
      +6. give the files the permissions 755.
      +6b. If the extension cgi is desired, change the extension of all 3 files from pl -> cgi (rename).
      +(ev. 10b+11 from above)
      +
      + +Windowsuser have to change the first line of all scripts, there is the path of Perl. Example:
      +instead of: #!/usr/bin/perl -w
      +now: #!C:_usr/bin/perl.exe -w
      + +

      Operation

        . + +
        Menu
        . +In the selection list above you set the database.
        +All actions refer to the database set here. + +
        Home
        +Here you can learn about your system, the different installed versions and details about the +versions and details about the configured databases.
        +If you click on the database name, you will see a list of the tables with the number of entries +with the number of entries, the size and the last update date. + +
        Configuration
        . +Here you can edit your configuration, save it or restore the initial configuration. +restore. +

          +
        • Configured databases: the listing of configured databases. The active database is listed in bold.
        • +
        • Table prefix: here you can specify (for each database) a prefix. This is a filter that will take into account for dumps only the tables that start with this prefix (for example, all tables that start with "phpBB_"). If you want all tables in this database to be saved, just leave the field empty.
        • . +
        • GZip compression: Here you can enable compression. It is recommended to enable it, because the files will be much smaller after all and disk space is always scarce.
        • . +
        • Email with Dumpfile: If this option is enabled, an email with the dump as an attachment will be sent after the backup is complete (caution, compression should absolutely be on, otherwise the attachment will be too large and may not be sent!).
        • +
        • Email address: Recipient address for the email.
        • +
        • Sender of the email: this address appears as the sender in the email.
        • +
        • FTP Transfer: If this option is enabled, the backup file will be sent via FTP after the backup is completed.
        • +
        • FTP Server: The address of the FTP server (e.g. ftp.mybackups.com).
        • +
        • FTP Server Port: The port of the FTP server (usually 21).
        • +
        • FTP User: The username of the FTP account.
        • +
        • FTP Password: The password of the FTP account.
        • +
        • FTP Upload Folder: The directory where the backup file should go (upload permissions must exist!).
        • +
        • Automatic deletion of backups: If this option is enabled, older backups will be deleted automatically according to the following rules.
        • . +
        • Number of backup files: A value > 0 deletes all backup files except for the number specified here.
        • +
        • Language: here you specify the language for the interface.
        • +
        + +
        Administration
        . +This is where the actual actions are performed.
        +It will show you all the files in the backup directory. +For the actions "Restore" and "Delete" a file must be selected. +
          +
        • Restore: This will update the database with the selected backup file.
        • +
        • Delete: This lets you delete the selected backup file.
        • +
        • Start new backup: Here you start a new backup (dump) according to the parameters set in the configuration.
        • . +
        + +
        Log
        +Here you can see and delete the log entries. +
        Credits / Help
        +this page. +
      diff --git a/msd/language/tr/lang.php b/msd/language/tr/lang.php new file mode 100644 index 0000000..dd50200 --- /dev/null +++ b/msd/language/tr/lang.php @@ -0,0 +1,109 @@ +ulaşılamıyor'; +$lang['L_VOM'] = 'den'; +$lang['L_MYSQLVARS'] = 'MySQL Değişkenleri'; +$lang['L_MYSQLSYS'] = 'MySQL komutları'; +$lang['L_STATUS'] = 'Durum'; +$lang['L_PROZESSE'] = 'İşlemler'; +$lang['L_INFO_NOVARS'] = 'Değişkenler bulunmuyor'; +$lang['L_INHALT'] = 'İçerik'; +$lang['L_INFO_NOSTATUS'] = 'durum tespit edilemiyor'; +$lang['L_INFO_NOPROCESSES'] = 'Çalışır işlem yok'; +$lang['L_FM_FREESPACE'] = 'Sunucuda mevcut kullanılabilir hacim'; +$lang['L_LOAD_DATABASE'] = 'Veritabanlarını tekrar yükle'; +$lang['L_HOME'] = 'Anasayfa'; +$lang['L_CONFIG'] = 'Ayar Merkezi'; +$lang['L_DUMP'] = 'Yedekleme'; +$lang['L_RESTORE'] = 'Dönüştürüm'; +$lang['L_FILE_MANAGE'] = 'Yönetim'; +$lang['L_LOG'] = 'Rapor'; +$lang['L_CHOOSE_DB'] = 'Veritabanı seçimi'; +$lang['L_CREDITS'] = 'Künye/Yardım'; +$lang['L_MULTI_PART'] = 'Parçalı yedekleme'; +$lang['L_LOGFILENOTWRITABLE'] = 'Rapor Dosyasına yazılamıyor!'; +$lang['L_SQL_ERROR1'] = 'Sorguda hata oluştu:'; +$lang['L_SQL_ERROR2'] = 'MySQL bildirisi:'; +$lang['L_UNKNOWN'] = 'bilinmeyen'; +$lang['L_UNKNOWN_NUMBER_OF_RECORDS'] = 'bilinmeyen'; +$lang['L_OK'] = 'tamam'; +$lang['L_CRON_COMPLETELOG'] = 'Çıktıları tamamen raporla'; +$lang['L_NO'] = 'hayır'; +$lang['L_CREATE_DATABASE'] = 'Yeni Veritabanı oluştur'; +$lang['L_EXPORTFINISHED'] = 'ihrac tamamlanmıştır'; +$lang['L_SQL_BROWSER'] = 'SQL-Tarayıcısı'; +$lang['L_SERVER'] = 'Sunucu'; +$lang['L_MYSQL_CONNECTION_ENCODING'] = 'MYSQL Sunucunun sabit karakter seti'; +$lang['L_TITLE_SHOW_DATA'] = 'Verileri göster'; +$lang['L_PRIMARYKEY_CONFIRMDELETE'] = 'Birincil anahtar gerçekten silmek istermisin?'; +$lang['L_SETPRIMARYKEYSFOR'] = 'Tablonun yeni birincil anahtar ayarlar'; +$lang['L_PRIMARYKEY_FIELD'] = 'Anahtar alanı'; +$lang['L_PRIMARYKEYS_SAVE'] = 'Birincil anahtar kaydet'; +$lang['L_CANCEL'] = 'İptal'; +$lang['L_VISIT_HOMEPAGE'] = 'Visit Homepage'; +$lang['L_SECONDS'] = 'Seconds'; +$lang['L_BACKUPS'] = 'Yedeklemeler'; +$lang['L_MINUTES'] = 'Minutes'; +$lang['L_PAGE_REFRESHS'] = 'Page refreshs'; +$lang['L_MINUTE'] = 'Minute'; +$lang['L_SETKEYSFOR'] = 'Set new indexes for table'; +$lang['L_KEY_CONFIRMDELETE'] = 'Really delete index?'; diff --git a/msd/language/tr/lang_config_overview.php b/msd/language/tr/lang_config_overview.php new file mode 100644 index 0000000..781d662 --- /dev/null +++ b/msd/language/tr/lang_config_overview.php @@ -0,0 +1,123 @@ +%s
      klasör %s'; +$lang['L_FTP'] = 'FTP'; +$lang['L_SFTP'] = 'SFTP'; +$lang['L_EMAIL_CC'] = 'CC-Alıcı'; +$lang['L_NAME'] = 'İsim'; +$lang['L_CONFIRM_CONFIGFILE_DELETE'] = 'Ayar dosyası %s gerçekten silinsin mi ?'; +$lang['L_ERROR_DELETING_CONFIGFILE'] = 'Hata oluştu: Ayar dosyası %s silinemedi'; +$lang['L_SUCCESS_DELETING_CONFIGFILE'] = 'Ayar dosyası %s başarıyla silindi.'; +$lang['L_SUCCESS_CONFIGFILE_CREATED'] = '%s isimli ayar dosyası başarı ile oluşturuldu'; +$lang['L_ERROR_CONFIGFILE_NAME'] = 'Dosya ismi "%s" izin verilmeyen karakter içeriyor'; +$lang['L_CREATE_CONFIGFILE'] = 'Yeni ayar dosyası oluştur'; +$lang['L_ERROR_LOADING_CONFIGFILE'] = 'Ayar dosyası "%s" yüklenemedi'; +$lang['L_BACKUP_DBS_PHP'] = 'yedeklenecek veritabanları (PHP)'; +$lang['L_BACKUP_DBS_PERL'] = 'yedeklenecek veritabanları (PERL)'; +$lang['L_CRON_COMMENT'] = 'Not ekle'; +$lang['L_AUTODETECT'] = 'otomatik bul'; diff --git a/msd/language/tr/lang_dump.php b/msd/language/tr/lang_dump.php new file mode 100644 index 0000000..475e229 --- /dev/null +++ b/msd/language/tr/lang_dump.php @@ -0,0 +1,57 @@ +%s` Veritabanında tablo bulunamadı.'; +$lang['L_DUMP_ENDERGEBNIS'] = ' %s tabloda %s kayıt yedeklendi.
      '; +$lang['L_MAILERROR'] = 'Mail gönderiminde hata oluştu!'; +$lang['L_EMAILBODY_ATTACH'] = 'Ekte veritabanıyın yedeklemesi bulunuyor.
      yedeklenen Veritabanı `%s` +

      Oluşturulan dosya:

      %s

      Sevgilerler

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_MP_NOATTACH'] = 'Çok parçalı yedekleme oluşturuldu.
      Dosyalar eklenti olarak gönderilmiyor!
      yedeklenen Veritabanı `%s` +

      oluşturulan dosyalar:

      %s


      Sevgilerle

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_MP_ATTACH'] = 'Çok parçalı yedekleme oluşturuldu.
      Dosyalar eklenti olarak gönderilmiyor!Dosyalar ayrı bir mail ile gönderiliyor!
      Yedeklenen Veritabanı `%s` +

      oluşturulan dosyalar:

      %s


      Sevgilerle

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_FOOTER'] = '


      Sevgiler

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_TOOBIG'] = 'Yedekleme boyutu maximumu boyut olan %s aştıgından dolayı eklenti olarak gönderilemiyor.
      Yedeklenen Veritabanı `%s` +

      oluşturulan dosyalar:

      %s +

      Saygılarla

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_NOATTACH'] = 'Yedekleme dosyaları maalesef eklenememiştir.
      yedeklenen Veritabanı `%s` +

      Oluşturulan Dosyalar:

      %s +

      Sevgilerle

      MyOOS [Dumper]
      '; +$lang['L_EMAIL_ONLY_ATTACHMENT'] = ' ... sadece eklentiler'; +$lang['L_TABLESELECTION'] = 'Tablo seçimi'; +$lang['L_SELECTALL'] = 'hepsini seç'; +$lang['L_DESELECTALL'] = 'hepsini kaldır'; +$lang['L_STARTDUMP'] = 'Yedeklemeyi başlat'; +$lang['L_LASTBUFROM'] = 'son yedekleme tarihi'; +$lang['L_NOT_SUPPORTED'] = 'Bu yedekleme istenilen fonksiyonu desteklemiyor.'; +$lang['L_MULTIDUMP'] = 'Parçalı yedekleme:%d Veritabanları yedeklendi.'; +$lang['L_FILESENDFTP'] = 'Dosya FTP ile gönderiliyor... lütfen biraz bekleyiniz. '; +$lang['L_FTPCONNERROR'] = 'FTP-Bağlantısı kurulmadı! Bağlantı '; +$lang['L_FTPCONNERROR1'] = ' Kullanıcı'; +$lang['L_FTPCONNERROR2'] = ' mümkün değil'; +$lang['L_FTPCONNERROR3'] = 'FTP-Yüklemesi başarısız! '; +$lang['L_FTPCONNECTED1'] = 'Kullanılan bağlantı'; +$lang['L_FTPCONNECTED2'] = ' ile '; +$lang['L_FTPCONNECTED3'] = ' kayıt edilen'; +$lang['L_FILESENDSFTP'] = 'Dosya SFTP ile gönderiliyor... lütfen biraz bekleyiniz. '; +$lang['L_SFTPCONNERROR'] = 'SFTP-Bağlantısı kurulmadı! Bağlantı '; +$lang['L_NR_TABLES_SELECTED'] = '- %s seçilmiş tablolar'; +$lang['L_NR_TABLES_OPTIMIZED'] = '%s Tablo arındırıldı.'; +$lang['L_DUMP_ERRORS'] = '

      Hata %s oluştu: göster

      '; +$lang['L_FATAL_ERROR_DUMP'] = "Hata oluştu: CREATE komutu '%s'tablosu '%s' veritabanında okunamadı
      Tabloları onarmanızı öneriyoruz."; diff --git a/msd/language/tr/lang_filemanagement.php b/msd/language/tr/lang_filemanagement.php new file mode 100644 index 0000000..8c89bad --- /dev/null +++ b/msd/language/tr/lang_filemanagement.php @@ -0,0 +1,76 @@ +%s"'; +$lang['L_DELETE_FILE_ERROR'] = 'Dosya "%s" silinemedi!'; +$lang['L_FM_DUMP_HEADER'] = 'Yedekleme'; +$lang['L_DOCRONBUTTON'] = "Perl-Cronscript'i çalıştır"; +$lang['L_DOPERLTEST'] = 'Perl-Modülerini denetle'; +$lang['L_DOSIMPLETEST'] = 'Perli denetle'; +$lang['L_PERLOUTPUT1'] = 'crondump.pl de kayıtlı adres absolute_path_of_configdir'; +$lang['L_PERLOUTPUT2'] = 'Tarayıcı veya dışarıdan çağrışım ile çalışan Cronjob'; +$lang['L_PERLOUTPUT3'] = 'Shell den veya Crontab dan çalışması için'; +$lang['L_RESTORE_OF_TABLES'] = 'belirli tabloları geri dönüştürme'; +$lang['L_CONVERTER'] = 'Yedekleme dönüştürücüsü'; +$lang['L_CONVERT_FILE'] = 'dönüştürülecek dosya'; +$lang['L_CONVERT_FILENAME'] = 'Yeni dosya adı (uzantısız)'; +$lang['L_CONVERTING'] = 'Dönüştürüm'; +$lang['L_CONVERT_FILEREAD'] = "Dosya '%s' okunuyor"; +$lang['L_CONVERT_FINISHED'] = "Dönüştürme tamamlandı, '%s' oluşturuldu."; +$lang['L_NO_MOD_BACKUPFILE'] = 'Başka yazılımların dosyaları:'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Maksimum dosya boyutu'; +$lang['L_MAX_UPLOAD_SIZE_INFO'] = 'Eğer Yedek dosyanız izin verilen boyuttan büyük ise, o zaman FTP ile "work/backup"-Klasörüne yüklemeniz lazım. +Daha sonra bu dosya Yönetim bölümünde gözüküp geri yükleme işlemi için kullanılabilir duruma gelicektir.'; +$lang['L_ENCODING'] = 'kodlama'; +$lang['L_FM_CHOOSE_ENCODING'] = 'alınacak yedeğin karakter setini seçin'; +$lang['L_CHOOSE_CHARSET'] = 'Maalesef veritabanı yedeğinin hangi karakter seti ile kodlandığını otomatik olarak bulunmadı
      Hangi karakter setini kullandıysanız onu seçip elle vermeniz gerekiyor.Daha sonra MyOOSDumper veritabanı serveri ile irtibata gecip yedeği yüklemeye başlıyacaktır.
      Eğer yedek yüklendikten sonra karakter sorunu devam ediyorsa başka bir karakter seti seçip tekrar denemeniz gerekiyor.
      Bol şans ;)'; +$lang['L_DOWNLOAD_FILE'] = 'Dosya indir'; +$lang['L_BACKUP_NOT_POSSIBLE'] = 'A backup of the system database `%s` is not possible!'; diff --git a/msd/language/tr/lang_help.php b/msd/language/tr/lang_help.php new file mode 100644 index 0000000..5c159a1 --- /dev/null +++ b/msd/language/tr/lang_help.php @@ -0,0 +1,40 @@ +Kurulum tamamlanmıştır --> MyOOS [Dumper]'i başlat
      "; +$lang['L_INSTALL_TOMENU'] = 'Ana Menüye geç'; +$lang['L_INSTALLMENU'] = 'Ana Menüsü'; +$lang['L_STEP'] = 'Adım'; +$lang['L_INSTALL'] = 'Kurulum'; +$lang['L_UNINSTALL'] = 'Kurulumu kaldır'; +$lang['L_TOOLS'] = 'Araçlar'; +$lang['L_EDITCONF'] = 'Ayarları elden düzenle'; +$lang['L_OSWEITER'] = 'Kayıt etmeden devam et'; +$lang['L_ERRORMAN'] = 'Ayarların kaydında hata oluştu!
      Dosyayı lütfen elden düzenleyiniz '; +$lang['L_MANUELL'] = 'elden'; +$lang['L_CREATEDIRS'] = 'Klasörler oluşturuluyor'; +$lang['L_INSTALL_CONTINUE'] = 'Kuruluma devam et'; +$lang['L_CONNECTTOMYSQL'] = ' MySQL ile bağlan '; +$lang['L_DBPARAMETER'] = 'Veritabanı-Parametreleri'; +$lang['L_CONFIGNOTWRITABLE'] = '"config.php" yazılamıyor. FTP yazılımınız ile CHMOD ayarlarını 0777 ye çevirin.'; +$lang['L_DBCONNECTION'] = 'Bağlantı Parametreleri'; +$lang['L_CONNECTIONERROR'] = 'Hata: bağlantı kurulamıyor.'; +$lang['L_CONNECTION_OK'] = 'Veritabanı bağlantısı kuruldu.'; +$lang['L_SAVEANDCONTINUE'] = 'Kaydet ve kurulumu devam et'; +$lang['L_CONFBASIC'] = 'Asıl Ayarları'; +$lang['L_INSTALL_STEP2FINISHED'] = 'Veritabanı ayarları kayıt edildi.

      +İsterseniz standart ayarlar ile kurulumu sürdürebilirsiniz, veya ayarları düzenleyebilirsiniz.'; +$lang['L_INSTALL_STEP2_1'] = 'Standart ayarlarla devam'; +$lang['L_LASTSTEP'] = 'Kurulumum tamamlanması'; +$lang['L_IDOMANUAL'] = 'Klasörleri elden oluşturacağım'; +$lang['L_DOFROM'] = 'başlangıç:'; +$lang['L_FTPMODE2'] = 'klasörleri FTP ile oluştur'; +$lang['L_CONNECT'] = 'Bağlantı kur'; +$lang['L_DIRS_CREATED'] = 'Klasörler oluşturuldu.'; +$lang['L_CONNECT_TO'] = 'Bağlantı kur:'; +$lang['L_CHANGEDIR'] = 'Klasöre geç'; +$lang['L_CHANGEDIRERROR'] = 'Klasöre geciş mümkün değil'; +$lang['L_FTP_OK'] = 'FTP-Ayarları geçerli'; +$lang['L_CREATEDIRS2'] = 'Klasörleri oluştur'; +$lang['L_FTP_NOTCONNECTED'] = 'FTP-bağlantısı kurulmadı!'; +$lang['L_CONNWITH'] = 'Kurulacak bağlantı:'; +$lang['L_ASUSER'] = 'Kullanıcı'; +$lang['L_NOTPOSSIBLE'] = 'mümkün değil'; +$lang['L_DIRCR1'] = 'Oluşturuluyor: Çalışma klasörü'; +$lang['L_DIRCR2'] = 'Oluşturuluyor: yedekleme klasörü'; +$lang['L_DIRCR4'] = 'Oluşturuluyor: rapor klasörü'; +$lang['L_DIRCR5'] = 'Oluşturuluyor: ayar klasörü'; +$lang['L_INDIR'] = 'bulunulan klasör'; +$lang['L_CHECK_DIRS'] = 'Kontrol ediliyor'; +$lang['L_DISABLEDFUNCTIONS'] = 'İptal edilmiş fonksiyonlar'; +$lang['L_NOFTPPOSSIBLE'] = 'FTP fonksiyonları kullanılmaz!'; +$lang['L_NOGZPOSSIBLE'] = 'Sıkıştırma fonksiyonları kullanılmıyor!'; +$lang['L_UI1'] = 'Çalışma klasörleri siliniyor (içerisinde bulunan yedekleme dosyaları ile birlikte).'; +$lang['L_UI2'] = 'Eminmisiniz?'; +$lang['L_UI3'] = 'Hayır, hemen iptal et'; +$lang['L_UI4'] = 'Evet, eminim, devam et'; +$lang['L_UI5'] = 'Çalışma klasörü siliniyor'; +$lang['L_UI6'] = 'Hepsi silindi.'; +$lang['L_UI7'] = 'Skript klasörünü lütfen siliniz'; +$lang['L_UI8'] = 'Bir düzey yukarı çık'; +$lang['L_UI9'] = 'Hata oluştu, silme işlemi uygulanamadı

      Hatanın oluştuğu klasör: '; +$lang['L_IMPORT'] = 'Ayarları ithal et'; +$lang['L_IMPORT3'] = 'Ayarlar yüklendi...'; +$lang['L_IMPORT4'] = 'Ayarlar yedeklendi.'; +$lang['L_IMPORT5'] = "MyOOS [Dumper]'i başlat"; +$lang['L_IMPORT6'] = 'Kurulum menüsü'; +$lang['L_IMPORT7'] = 'Ayarları yükle'; +$lang['L_IMPORT8'] = 'Yüklemeye geri dön'; +$lang['L_IMPORT9'] = 'Bu bir ayaryedeklemesi değil!'; +$lang['L_IMPORT10'] = 'Ayarlar yüklendi...'; +$lang['L_IMPORT11'] = 'Hata: SQL komutları yazarken hata oluştu'; +$lang['L_IMPORT12'] = 'Hata: Config.php nin kaydında hata oluştu'; +$lang['L_INSTALL_HELP_PORT'] = '(boş = Standart port)'; +$lang['L_INSTALL_HELP_SOCKET'] = '(boş= Standart socket)'; +$lang['L_TRYAGAIN'] = 'Bir daha dene'; +$lang['L_SOCKET'] = 'Socket'; +$lang['L_PORT'] = 'Port'; +$lang['L_FOUND_DB'] = 'Bulunan Veritabanı: '; +$lang['L_FM_FILEUPLOAD'] = 'Dosya yükle'; +$lang['L_PASS'] = 'Şifre'; +$lang['L_NO_DB_FOUND_INFO'] = 'Veritabanı sunucusu ile bağlantı kuruldu.
      +Bağlantı parametreleri doğrulandı, kullanıcı ismi ve şifresi kabul edildi.
      +Fakat Sunucuda Veritabanı bulunamadı.
      +Otomatik tanıma sunucunuzda kilitli olabilir.
      +Kurulum tamamlandıktan sonra lütfen Ayar Merkezi sayfasına gidin ve Bağlantı parametreleri bölümünde "göster" tıklayınız.
      +Veritabanı ile bağlantı kurulabilmesi için gereken bilgileri oraya girmeniz gerekiyor.'; +$lang['L_ENTER_DB_INFO'] = 'First click the button "Connect to MySQL". Only if no database could be detected you need to provide a database name here.'; diff --git a/msd/language/tr/lang_log.php b/msd/language/tr/lang_log.php new file mode 100644 index 0000000..a52c7d8 --- /dev/null +++ b/msd/language/tr/lang_log.php @@ -0,0 +1,7 @@ +Dosyayı lütfen elden oluşturunuz. İçeriği'; +$lang['L_HTACC_CHECK_ERROR'] = 'It could not be checked whether the program is protected!
      The simulated external access could not be carried out.'; +$lang['L_HTACC_NOT_NEEDED'] = 'The program is protected by higher-level authorizations; local directory protection is not required.'; +$lang['L_HTACC_COMPLETE'] = 'The program is protected, the directory protection is complete.'; +$lang['L_HTACC_INCOMPLETE'] = 'The program is not protected, the directory protection is incomplete!'; +$lang['L_HTACC_PROPOSED'] = 'The program is not protected, directory protection is strongly recommended!'; +$lang['L_HTACC_EDIT'] = '.htaccess dosyasını düzenle'; +$lang['L_HTACCESS18'] = '.htaccess dosyasının oluşturulacağı klasör '; +$lang['L_HTACCESS19'] = 'güncelle '; +$lang['L_HTACCESS20'] = "Skript'i çalıştır"; +$lang['L_HTACCESS21'] = 'Handler ekle'; +$lang['L_HTACCESS22'] = 'Çalıştırılır hale getir'; +$lang['L_HTACCESS23'] = 'Klasör listesi'; +$lang['L_HTACCESS24'] = 'Hata dosyası'; +$lang['L_HTACCESS25'] = "Rewrite'i aç"; +$lang['L_HTACCESS26'] = 'Yasak / Serbest'; +$lang['L_HTACCESS27'] = 'Yönlendir'; +$lang['L_HTACCESS28'] = "Hata-Log'u"; +$lang['L_HTACCESS29'] = 'Başka örnekler ve belgeler'; +$lang['L_HTACCESS30'] = 'Hosting Şirketi'; +$lang['L_HTACCESS31'] = 'genel'; +$lang['L_HTACCESS32'] = 'Dikat .htaccess dosyası tarayıcıyı anında etkiler.
      Yanlış ayarlandığında sayfalara ulaşamazsınız.'; +$lang['L_DISABLEDFUNCTIONS'] = 'İptal edilmiş fonksiyonlar'; +$lang['L_NOGZPOSSIBLE'] = 'Zlib bulunamadığı için Sıkıştırma kullanılamaz!'; +$lang['L_DELETE_HTACCESS'] = 'Klasör koruması kaldırılsın (.htaccess silinecek)'; +$lang['L_WRONG_RIGHTS'] = "Dosya yada Klasör '%s' yazılamıyor !.
      Ya yetkili kullanıcı değilsiniz yada erişim haklarınız kısıtlı (chmod).
      Lütfen Ftp programınızla gerekli erişim haklarını düzenleyin.
      Dosya / Klasör için gerekli erişim hakkı %s.
      "; +$lang['L_CANT_CREATE_DIR'] = "gerekli olan '%s' Klasörü oluşturulamadı. Lütfen FTP Programınız ile yaratın."; +$lang['L_TABLE_TYPE'] = 'Tür'; +$lang['L_CHECK'] = 'check'; +$lang['L_OS'] = 'Operating system'; +$lang['L_MOD_VERSION'] = 'MyOOS [Dumper] - Version'; +$lang['L_NEW_MOD_VERSION'] = 'New Version'; +$lang['L_NEW_MOD_VERSION_INFO'] = 'There is a new version of MyOOS [Dumper] available.'; +$lang['L_UPDATED_IMPORTANT'] = 'Important: Before updating, please backup your files.'; +$lang['L_UPDATE'] = 'Update now'; +$lang['L_MYSQL_VERSION'] = 'MySQL-Version'; +$lang['L_PHP_VERSION'] = 'PHP-Version'; +$lang['L_MAX_EXECUTION_TIME'] = 'Max execution time'; +$lang['L_PHP_EXTENSIONS'] = 'PHP-Extensions'; +$lang['L_MEMORY'] = 'Memory'; +$lang['L_FILE_MISSING'] = 'Dosya bulunamadı'; +$lang['L_INSTALLING_UPDATES'] = 'Installing Updates'; +$lang['L_UPDATE_SUCCESSFUL'] = 'Update successful'; +$lang['L_UPDATE_FAILED'] = 'Update failed'; +$lang['L_UP_TO_DATE'] = 'Current Version is up to date'; diff --git a/msd/language/tr/lang_restore.php b/msd/language/tr/lang_restore.php new file mode 100644 index 0000000..18b64f1 --- /dev/null +++ b/msd/language/tr/lang_restore.php @@ -0,0 +1,19 @@ +%d tablo oluşturuldu.'; +$lang['L_FILE_MISSING'] = 'Dosya bulunamadı'; +$lang['L_RESTORE_DB'] = "Veritabanı: '%s' Sunucu: '%s'."; +$lang['L_RESTORE_COMPLETE'] = '%s Tablolar oluşturuldu.'; +$lang['L_RESTORE_RUN1'] = '
      Şimdiye kadar %s / %s kayıt işlendi.'; +$lang['L_RESTORE_RUN2'] = "
      İşlenen tablo '%s' kayıtlar işleniyor.

      "; +$lang['L_RESTORE_COMPLETE2'] = '%s Kayıtlar işlendi.'; +$lang['L_RESTORE_TABLES_COMPLETED'] = 'Şimdiye kadar %d / %d Tablo oluşturuldu.'; +$lang['L_RESTORE_TOTAL_COMPLETE'] = '
      Tebrikler.

      Veritabanı tamamen dönüştürüldü.
      Yedeklemedeki bulunan bütün bilgiler işlenebildi.

      İşlem tamamlanmıştır. :-)'; +$lang['L_DB_SELECT_ERROR'] = "
      Hata:
      Veritabanı seçimi '"; +$lang['L_DB_SELECT_ERROR2'] = "' Hata oluştu!"; +$lang['L_FILE_OPEN_ERROR'] = 'Hata: Dosya açılamadı.'; +$lang['L_PROGRESS_OVER_ALL'] = 'Süreçin tamamı'; +$lang['L_BACK_TO_OVERVIEW'] = 'Veritabanı listesi'; +$lang['L_RESTORE_RUN0'] = '
      Şimdiye kadar %s kayıt başarılı olarak işlendi.'; +$lang['L_UNKNOWN_SQLCOMMAND'] = 'Tanınmayan SQL komudu:'; +$lang['L_NOTICES'] = 'İpuçlar'; diff --git a/msd/language/tr/lang_sql.php b/msd/language/tr/lang_sql.php new file mode 100644 index 0000000..8980af9 --- /dev/null +++ b/msd/language/tr/lang_sql.php @@ -0,0 +1,190 @@ +%s satır ihraç edildi'; +$lang['L_CSV_FIELDCOUNT_NOMATCH'] = 'Tablo kayıtlarının sayısı, dışalım edilecek bilgilerle uyuşmuyor (%d yerine %d).'; +$lang['L_CSV_FIELDSLINES'] = '%d hücre tespit edildi, toplam %d satır'; +$lang['L_CSV_ERRORCREATETABLE'] = ' `%s` Tablo oluşturmada hata oluştu!'; +$lang['L_FM_UPLOADFILEREQUEST'] = 'Dosya belirtiniz.'; +$lang['L_CSV_NODATA'] = 'Dışalım edilebilecek kayıt bulunamadı!'; +$lang['L_SQLLIB_GENERALFUNCTIONS'] = 'genel fonksiyonlar'; +$lang['L_SQLLIB_RESETAUTO'] = 'Auto-değeri geri al'; +$lang['L_SQLLIB_BOARDS'] = 'Paneller'; +$lang['L_SQLLIB_DEACTIVATEBOARD'] = 'Paneli durdur'; +$lang['L_SQLLIB_ACTIVATEBOARD'] = 'Paneli çalştır'; +$lang['L_SQL_NOTABLESSELECTED'] = 'Tablo seçilmedi!'; +$lang['L_TOOLS'] = 'Araçlar'; +$lang['L_TOOLS_TOOLBOX'] = 'Veritabanı seçimi / Veritabanı işlemleri / Al / Ver'; +$lang['L_SQL_OPENFILE'] = 'SQL dosyasını aç'; +$lang['L_SQL_OPENFILE_BUTTON'] = 'yükle'; +$lang['L_MAX_UPLOAD_SIZE'] = 'maximum Dosya boyutu'; +$lang['L_SQL_SEARCH'] = 'Arama'; +$lang['L_SQL_SEARCHWORDS'] = 'aranan kelime(ler)'; +$lang['L_START_SQL_SEARCH'] = 'aramayı başlat'; +$lang['L_RESET_SEARCHWORDS'] = 'arama sonucunu sil'; +$lang['L_SEARCH_OPTIONS'] = 'Arama Seçenekleri'; +$lang['L_SEARCH_RESULTS'] = "aradığınız \"%s\" kelime sonucu \"%s\" Tablo'da bulunan sonuçlar"; +$lang['L_SEARCH_NO_RESULTS'] = 'aradığınız "%s" kelimesi "%s" Tablo içersinde bulunamadı !'; +$lang['L_NO_ENTRIES'] = '"%s" isimli Tablo boş ve hiçbirşey yazılmamış.'; +$lang['L_SEARCH_ACCESS_KEYS'] = 'Çevir: ileri=ALT+V, geri=ALT+C'; +$lang['L_SEARCH_OPTIONS_OR'] = 'Sütunda en azından bir aranılan kelime bulunmalıdır. (VEYA arama)'; +$lang['L_SEARCH_OPTIONS_CONCAT'] = "Metin'de bütün aranılan kelimeler bir satırda bulunmalıdır, fakat aranılan kelimeler değişik sütunlarda bulunabilir. (Vakit alıcı)"; +$lang['L_SEARCH_OPTIONS_AND'] = 'Sütunun içinde aranan kelimelerin hepsi bulunmalı (VE)'; +$lang['L_SEARCH_IN_TABLE'] = 'Tablonun içinde ara'; +$lang['L_ERROR_NO_FIELDS'] = 'Search error: it could not be determined which fields the table "%s" has!'; +$lang['L_SQL_EDIT_TABLESTRUCTURE'] = 'Tablo yapısını düzenle'; +$lang['L_DEFAULT_CHARSET'] = 'standart karakter seti'; +$lang['L_TITLE_KEY_PRIMARY'] = 'İndeks'; +$lang['L_TITLE_KEY_UNIQUE'] = 'Eşsiz Anahtar'; +$lang['L_TITLE_INDEX'] = 'İndeks'; +$lang['L_TITLE_KEY_FULLTEXT'] = 'Full Metin Anahtari'; +$lang['L_TITLE_NOKEY'] = 'Anahtar yok'; +$lang['L_TITLE_SEARCH'] = 'Ara'; +$lang['L_TITLE_MYSQL_HELP'] = 'MySQL Klavuzu'; +$lang['L_TITLE_UPLOAD'] = 'SQL dosyasını yükle'; +$lang['L_PRIMARYKEY_DELETED'] = 'Birincil Anahtar silindi'; +$lang['L_PRIMARYKEY_NOTFOUND'] = 'Birincil Anahtar bulunmadı'; +$lang['L_PRIMARYKEYS_CHANGED'] = 'Birincil Anahtar değiştirildi'; +$lang['L_PRIMARYKEYS_CHANGINGERROR'] = 'Birincil Anahtar değiştirirken bir hata oluştu'; +$lang['L_SQL_VIEW_COMPACT'] = 'Kompakt görünüm'; +$lang['L_SQL_VIEW_STANDARD'] = 'Varsayılan görünüm'; +$lang['L_FIELDS_OF_TABLE'] = 'Tablonun alanları'; +$lang['L_ENGINE'] = 'Engine'; +$lang['L_USERNAME'] = 'Username'; +$lang['L_PASSWORD'] = 'Password'; +$lang['L_PASSWORD_REPEAT'] = 'Password (repeat)'; +$lang['L_INFO_SIZE'] = 'Ebadı'; +$lang['L_TABLE_TYPE'] = 'Tür'; +$lang['L_KEY_DELETED'] = 'Index deleted'; +$lang['L_KEY_DELETEERROR'] = 'Error deleting index'; +$lang['L_KEY_ADDED'] = 'Index added'; +$lang['L_KEY_ADDERROR'] = 'Error adding index'; diff --git a/msd/language/vn/help.html b/msd/language/vn/help.html new file mode 100644 index 0000000..fe88bc4 --- /dev/null +++ b/msd/language/vn/help.html @@ -0,0 +1,147 @@ +
      +

      MyOOS [Dumper] based on MySQLDumper 1.24.4

      + +

      About this project

      +

      MyOOS [Dumper] is an improved version of MySQLDumper 1.24.4 (January 24, 2011). This enhancement takes into account the development of PHP.

      . +

      Most of all stability, security and handling are the main focus of MyOOS [Dumper]. But also an attractive template is included, which can be edited and adapted to your own needs.

      . + + +

      MyOOS [Dumper] is a backup program for MySQL databases, written in PHP and Perl. With it, backup copies of the data (store, blog, etc.) can be created and restored if necessary. Especially for web space without shell access, MyOOS [Dumper] is a useful alternative.

      . + +

      The idea for MySQLDumper came from Daniel Schlichtholz. He opened the MySQLDumper forum in 2004, whereupon programmers wrote new scripts and extended existing ones.

      + + + +

      Wish List / Future Attractions

      . +

      Do you have any suggestions for improvements? Feel free to contact the development team via the forum https://foren.myoos.de/viewforum.php?f=41.

      + + +

      Contribute

      +

      If you would like to help us improve the MyOOS project, we welcome your pull requests via GitHub here.

      +https://github.com/r23/MyOOS-Dumper/ + + +

      Financial Support

      . +

      You can use PayPal Me
      . +https://www.paypal.com/paypalme/r23de?locale.x=de_DE

      + +

      or via the QR code
      . +Financial support for MyOOS [Dumper]

      + +Send money to the MyOOS project.
      + +

      We hope you enjoy this project.

      The MyOOS [Dumper] Team

      + +MyOOS [Dumper]
      + +

      MyOOS [Dumper] Help

      + +

      Download

      +

      You can always get the latest versions via GitHub
      . +https://github.com/r23/MyOOS-Dumper/releases

      + + +

      System requirement

      . +

      The script works on any server (Windows, Linux, ...)
      +with PHP >= version 7.4 with GZip support, MySQL (version 4.1 or higher), JavaScript (must be enabled)

      . +

      Copy the mod folder from the MyOOS archive to a separate working folder.

      . + +

      Installation

      . +The installation process is straightforward. +

      From the MyOOS archive, copy the mod folder into any folder.
      +Upload all the files from the mod folder to your web server. (For example, to the lowest level in [server web directory/]mod)
      +... done!
      +You can now call MyOOS [Dumper] in your web browser by "https://example.com/mod/",
      +to complete the installation. Just follow the instructions.
      +
      Note:
      If on your server the script is not allowed to create directories,
      +you have to do this manually, because MyOOS [Dumper] stores the data ordered in +directories.
      +The script aborts with an appropriate statement!
      +After you have created the directories (according to the hint), it runs normally and without restrictions.
      + +

      Perl script instructions

      . +Most have a cgi-bin directory where perl can be run.
      +This is usually accessible by browser via http://www.example.com/cgi-bin/.
      +
      +For this case, please perform the following steps:

      . + +1. call the Backup page in MyOOS [Dumper] and click on "Backup Perl".
      +2. copy the path that is behind entry in crondump.pl for $absolute_path_of_configdir:.
      +3. open the file "crondump.pl" in the editor.
      +4. enter the copied path there at absolute_path_of_configdir (no spaces).
      +5. save crondump.pl .
      +6. copy crondump.pl, as well as perltest.pl and simpletest.pl into the cgi-bin directory (ascii mode in FTP).
      +7. give the files the permissions 755.
      +7b. If the ending cgi is desired, change the ending of all 3 files from pl -> cgi (rename).
      +8. call the configuration in MyOOS [Dumper].
      +9. select the page Cronscript.
      +10. change perl execution path to /cgi-bin/ .
      +10b. If the scripts have .pl, change the file extension to .cgi .
      +11.Save the configuration.

      + +Done, the scripts can now be called from the backup page.

      . + +For those who can run Perl in all directories, the following steps will suffice:

      . + +1. call in the MyOOS [Dumper] the page Backup.
      +2. copy the path that is behind entry in crondump.pl for $absolute_path_of_configdir:.
      +3. open the file "crondump.pl" in the editor.
      +4. enter the copied path there at absolute_path_of_configdir (no spaces).
      +5. save crondump.pl .
      +6. give the files the permissions 755.
      +6b. If the extension cgi is desired, change the extension of all 3 files from pl -> cgi (rename).
      +(ev. 10b+11 from above)
      +
      + +Windowsuser have to change the first line of all scripts, there is the path of Perl. Example:
      +instead of: #!/usr/bin/perl -w
      +now: #!C:_usr/bin/perl.exe -w
      + +

      Operation

        . + +
        Menu
        . +In the selection list above you set the database.
        +All actions refer to the database set here. + +
        Home
        +Here you can learn about your system, the different installed versions and details about the +versions and details about the configured databases.
        +If you click on the database name, you will see a list of the tables with the number of entries +with the number of entries, the size and the last update date. + +
        Configuration
        . +Here you can edit your configuration, save it or restore the initial configuration. +restore. +

          +
        • Configured databases: the listing of configured databases. The active database is listed in bold.
        • +
        • Table prefix: here you can specify (for each database) a prefix. This is a filter that will take into account for dumps only the tables that start with this prefix (for example, all tables that start with "phpBB_"). If you want all tables in this database to be saved, just leave the field empty.
        • . +
        • GZip compression: Here you can enable compression. It is recommended to enable it, because the files will be much smaller after all and disk space is always scarce.
        • . +
        • Email with Dumpfile: If this option is enabled, an email with the dump as an attachment will be sent after the backup is complete (caution, compression should absolutely be on, otherwise the attachment will be too large and may not be sent!).
        • +
        • Email address: Recipient address for the email.
        • +
        • Sender of the email: this address appears as the sender in the email.
        • +
        • FTP Transfer: If this option is enabled, the backup file will be sent via FTP after the backup is completed.
        • +
        • FTP Server: The address of the FTP server (e.g. ftp.mybackups.com).
        • +
        • FTP Server Port: The port of the FTP server (usually 21).
        • +
        • FTP User: The username of the FTP account.
        • +
        • FTP Password: The password of the FTP account.
        • +
        • FTP Upload Folder: The directory where the backup file should go (upload permissions must exist!).
        • +
        • Automatic deletion of backups: If this option is enabled, older backups will be deleted automatically according to the following rules.
        • . +
        • Number of backup files: A value > 0 deletes all backup files except for the number specified here.
        • +
        • Language: here you specify the language for the interface.
        • +
        + +
        Administration
        . +This is where the actual actions are performed.
        +It will show you all the files in the backup directory. +For the actions "Restore" and "Delete" a file must be selected. +
          +
        • Restore: This will update the database with the selected backup file.
        • +
        • Delete: This lets you delete the selected backup file.
        • +
        • Start new backup: Here you start a new backup (dump) according to the parameters set in the configuration.
        • . +
        + +
        Log
        +Here you can see and delete the log entries. +
        Credits / Help
        +this page. +
      diff --git a/msd/language/vn/lang.php b/msd/language/vn/lang.php new file mode 100644 index 0000000..9407255 --- /dev/null +++ b/msd/language/vn/lang.php @@ -0,0 +1,112 @@ +không có'; +$lang['L_VOM'] = 'từ'; +$lang['L_MYSQLVARS'] = 'Biến MySQL'; +$lang['L_MYSQLSYS'] = 'Lệnh MySQL'; +$lang['L_STATUS'] = 'Trạng thái'; +$lang['L_PROZESSE'] = 'Tiến trình'; +$lang['L_INFO_NOVARS'] = 'không có biến nào hợp lệ'; +$lang['L_INHALT'] = 'Giá trị'; +$lang['L_INFO_NOSTATUS'] = 'không có trạng thái nào hợp lệ'; +$lang['L_INFO_NOPROCESSES'] = 'không có tiến trình nào đang chạy'; +$lang['L_FM_FREESPACE'] = 'Dung lượng trống trên Server'; +$lang['L_LOAD_DATABASE'] = 'Nạp lại các CSDL'; +$lang['L_HOME'] = 'Trang chủ'; +$lang['L_CONFIG'] = 'Cấu hình'; +$lang['L_DUMP'] = 'Sao lưu'; +$lang['L_RESTORE'] = 'Phục hồi'; +$lang['L_FILE_MANAGE'] = 'File Admin'; +$lang['L_LOG'] = 'Log'; +$lang['L_CHOOSE_DB'] = 'Chọn CSDL'; +$lang['L_CREDITS'] = 'Yêu cầu / Trợ giúp'; +$lang['L_MULTI_PART'] = 'Sao lưu Nhiều phần'; +$lang['L_LOGFILENOTWRITABLE'] = 'Không thể ghi Logfile!'; +$lang['L_SQL_ERROR1'] = 'Lỗi trong Lệnh truy xuất (Query):'; +$lang['L_SQL_ERROR2'] = 'MySQL báo:'; +$lang['L_UNKNOWN'] = 'không rõ'; +$lang['L_UNKNOWN_NUMBER_OF_RECORDS'] = 'không rõ'; +$lang['L_OK'] = 'OK'; +$lang['L_CRON_COMPLETELOG'] = 'Xuất đầy đủ Log'; +$lang['L_NO'] = 'không'; +$lang['L_CREATE_DATABASE'] = 'Tạo ra cơ sở dữ liệu mới'; +$lang['L_EXPORTFINISHED'] = 'Quá trình xuất đã kết thúc.'; +$lang['L_SQL_BROWSER'] = 'Duyệt SQL'; +$lang['L_SERVER'] = 'Máy chủ'; +$lang['L_MYSQL_CONNECTION_ENCODING'] = 'Mã chuẩn của MySQL-Server + + +'; +$lang['L_TITLE_SHOW_DATA'] = 'Show data'; +$lang['L_PRIMARYKEY_CONFIRMDELETE'] = 'Really delete primary key?'; +$lang['L_SETPRIMARYKEYSFOR'] = 'Set new primary keys for table'; +$lang['L_PRIMARYKEY_FIELD'] = 'Primary key field'; +$lang['L_PRIMARYKEYS_SAVE'] = 'Save primary keys'; +$lang['L_CANCEL'] = 'Cancel'; +$lang['L_VISIT_HOMEPAGE'] = 'Visit Homepage'; +$lang['L_SECONDS'] = 'Seconds'; +$lang['L_BACKUPS'] = 'Các sao lưu'; +$lang['L_MINUTES'] = 'Minutes'; +$lang['L_PAGE_REFRESHS'] = 'Page refreshs'; +$lang['L_MINUTE'] = 'Minute'; +$lang['L_SETKEYSFOR'] = 'Set new indexes for table'; +$lang['L_KEY_CONFIRMDELETE'] = 'Really delete index?'; diff --git a/msd/language/vn/lang_config_overview.php b/msd/language/vn/lang_config_overview.php new file mode 100644 index 0000000..1be1b5d --- /dev/null +++ b/msd/language/vn/lang_config_overview.php @@ -0,0 +1,130 @@ +%s
      vào %s'; +$lang['L_FTP'] = 'FTP'; +$lang['L_SFTP_SEND_TO'] = 'tới %s
      vào %s'; +$lang['L_SFTP_SEND_TO'] = 'tới %s
      vào %s'; +$lang['L_SFTP'] = 'SFTP'; +$lang['L_EMAIL_CC'] = 'Đồng gửi'; +$lang['L_NAME'] = 'Tên'; +$lang['L_CONFIRM_CONFIGFILE_DELETE'] = 'Bạn có chắc muốn xóa các tập tin cấu hình %s?'; +$lang['L_ERROR_DELETING_CONFIGFILE'] = 'Lỗi: không thể xóa file cấu hình %s!'; +$lang['L_SUCCESS_DELETING_CONFIGFILE'] = 'File cấu hình %s vừa được xóa thành công.'; +$lang['L_SUCCESS_CONFIGFILE_CREATED'] = 'File cấu hình %s vừa được tạo thành công.'; +$lang['L_ERROR_CONFIGFILE_NAME'] = 'Tên file "%s" có ký tự không phù hợp.'; +$lang['L_CREATE_CONFIGFILE'] = 'Tạo file cấu hình mới'; +$lang['L_ERROR_LOADING_CONFIGFILE'] = 'Không thể tải file cấu hình "%s".'; +$lang['L_BACKUP_DBS_PHP'] = 'CSDL để sao lưu (PHP)'; +$lang['L_BACKUP_DBS_PERL'] = 'CSDL để sao lưu (PERL)'; +$lang['L_CRON_COMMENT'] = 'Nhập ghi chú'; +$lang['L_AUTODETECT'] = 'auto detect'; diff --git a/msd/language/vn/lang_dump.php b/msd/language/vn/lang_dump.php new file mode 100644 index 0000000..5af1845 --- /dev/null +++ b/msd/language/vn/lang_dump.php @@ -0,0 +1,59 @@ +%s` '; +$lang['L_DUMP_ENDERGEBNIS'] = 'File chứa %s bảng với %s bản ghi.
      '; +$lang['L_MAILERROR'] = 'Gửi email thất bại!'; +$lang['L_EMAILBODY_ATTACH'] = 'File đính kèm chứa đựng nội dung sao lưu MySQL.
      Sao lưu Cơ sở dữ liệu `%s` +

      File sau đã được tạo:

      %s

      Trân trọng!

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_MP_NOATTACH'] = 'Một Sao lưu Nhiều phần được tạo ra.
      Những tập tin dự phòng không được gửi kèm email!
      Sao lưu Cơ sở dữ liệu `%s` +

      Những file Sau đã được tạo ra

      %s +

      Trân trọng!

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_MP_ATTACH'] = 'Một Sao lưu Nhiều phần được tạo ra.
      Những tập tin dự phòng đã được gửi kèm email!
      Sao lưu Cơ sở dữ liệu `%s` +

      Những file sau đã được tạo ra:

      %s

      Trân trọng!

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_FOOTER'] = '`

      Trân trọng!

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_TOOBIG'] = 'Tập tin sao lưu vượt hơn kích thước lớn nhất của %s và nó không được đính kèm email.
      Sao lưu Cơ sở dữ liệu `%s` +

      File sau đã được tạo ra:

      %s +

      Trân trọng!

      MyOOS [Dumper]
      '; +$lang['L_EMAILBODY_NOATTACH'] = 'Files không được đính kèm email này!
      Sao lưu của CSDL `%s` +

      File sau đã được tạo ra:

      %s +

      Trân trọng!

      MyOOS [Dumper]
      '; +$lang['L_EMAIL_ONLY_ATTACHMENT'] = ' ... chỉ đính kèm.'; +$lang['L_TABLESELECTION'] = 'Chọn Bảng'; +$lang['L_SELECTALL'] = 'Chọn tất cả'; +$lang['L_DESELECTALL'] = 'Thôi chọn tất cả'; +$lang['L_STARTDUMP'] = 'Bắt đầu sao lưu'; +$lang['L_LASTBUFROM'] = 'cập nhật lần cuối từ'; +$lang['L_NOT_SUPPORTED'] = 'Sao lưu này không hỗ trợ chức năng này.'; +$lang['L_MULTIDUMP'] = 'Multidump: Sao lưu của %d Những cơ sở dữ liệu xong.'; +$lang['L_FILESENDFTP'] = 'gửi file qua FTP... hãy đợi. '; +$lang['L_FTPCONNERROR'] = 'Kết nối FTP không được thiết lập! Kết nối với '; +$lang['L_FTPCONNERROR1'] = ' như người sử dụng '; +$lang['L_FTPCONNERROR2'] = ' không thể xảy ra'; +$lang['L_FTPCONNERROR3'] = 'Upload FTP hỏng! '; +$lang['L_FTPCONNECTED1'] = 'Đã kết nối với '; +$lang['L_FTPCONNECTED2'] = ' trên '; +$lang['L_FTPCONNECTED3'] = ' chuyển đổi thành công'; +$lang['L_FILESENDSFTP'] = 'gửi file qua FTP... hãy đợi. '; +$lang['L_SFTPCONNERROR'] = 'Kết nối FTP không được thiết lập! Kết nối với '; +$lang['L_NR_TABLES_SELECTED'] = '- với %s bảng đã được chọn'; +$lang['L_NR_TABLES_OPTIMIZED'] = '%s những bảng đã được tối ưu hóa.'; +$lang['L_DUMP_ERRORS'] = '

      %s những lỗi xuất hiện: xem

      '; +$lang['L_FATAL_ERROR_DUMP'] = "Lỗi nghiêm trọng: CREATE-Statement của bảng '%s' trong CSDL '%s' không thể đọc!
      +Kiểm tra lại bảng này để tìm lỗi."; diff --git a/msd/language/vn/lang_filemanagement.php b/msd/language/vn/lang_filemanagement.php new file mode 100644 index 0000000..5bee154 --- /dev/null +++ b/msd/language/vn/lang_filemanagement.php @@ -0,0 +1,79 @@ +%s"'; +$lang['L_DELETE_FILE_ERROR'] = 'Error deleting file "%s"!'; +$lang['L_FM_DUMP_HEADER'] = 'Backup'; +$lang['L_DOCRONBUTTON'] = 'Chạy Perl Cron script'; +$lang['L_DOPERLTEST'] = 'Kiểm tra Perl Modules'; +$lang['L_DOSIMPLETEST'] = 'Kiểm tra Perl'; +$lang['L_PERLOUTPUT1'] = 'Các mục trong crondump.pl cho absolute_path_of_configdir'; +$lang['L_PERLOUTPUT2'] = 'URL cho tronhf duyệt hoặc ngoài Cron job'; +$lang['L_PERLOUTPUT3'] = 'Dòng lệnh trong Shell hoặc cho Crontab'; +$lang['L_RESTORE_OF_TABLES'] = 'Chọn Bảng để phục hồi'; +$lang['L_CONVERTER'] = 'Chương trình chuyển đổi sao lưu'; +$lang['L_CONVERT_FILE'] = 'File cần được chuyển đổi'; +$lang['L_CONVERT_FILENAME'] = 'Tên file xuất ra (bỏ qua phần mở rộng)'; +$lang['L_CONVERTING'] = 'Đang chuyển đổi'; +$lang['L_CONVERT_FILEREAD'] = "Đọc file '%s'"; +$lang['L_CONVERT_FINISHED'] = "Kết thúc chuyển đổi, '%s' vừa tạo thành công."; +$lang['L_NO_MOD_BACKUPFILE'] = 'Sao lưu script khác'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Dung lượng tối đa cho file'; +$lang['L_MAX_UPLOAD_SIZE_INFO'] = 'Nếu file sao lưu của các bạn lớn hơn giới hạn được quy định ở trên, bạn phải nạp dữ liệu nó qua FTP vào trong thư mục "work/backup". +Sau đó bạn có thể chọn nó để bắt đầu phục hồi. '; +$lang['L_ENCODING'] = 'mã hóa'; +$lang['L_FM_CHOOSE_ENCODING'] = 'Chọn chế độ mã hóa của file sao lưu'; +$lang['L_CHOOSE_CHARSET'] = 'MyOOS [Dumper] đã không thể phát hiện ra sự mã hóa của File sao lưu một cách tự động. +
      Bạn phải chọn charset đúng với định dạng đã được sao lưu. +
      Nếu bạn thấy bất kỳ vấn đề nào sau khi khôi phục, bạn có thể lặp lại quá trình sao lưu và sau đó chọn charset khác. +
      Chúc may mắn. ;)'; +$lang['L_DOWNLOAD_FILE'] = 'Download file'; +$lang['L_BACKUP_NOT_POSSIBLE'] = 'A backup of the system database `%s` is not possible!'; diff --git a/msd/language/vn/lang_help.php b/msd/language/vn/lang_help.php new file mode 100644 index 0000000..d18c176 --- /dev/null +++ b/msd/language/vn/lang_help.php @@ -0,0 +1,40 @@ +Cài đặt thành công --> Bắt đầu MyOOS [Dumper]
      '; +$lang['L_INSTALL_TOMENU'] = 'Quay lại menu chính'; +$lang['L_INSTALLMENU'] = 'Menu chính'; +$lang['L_STEP'] = 'SBước'; +$lang['L_INSTALL'] = 'Cài đặt'; +$lang['L_UNINSTALL'] = 'Gỡ cài đặt'; +$lang['L_TOOLS'] = 'Công cụ'; +$lang['L_EDITCONF'] = 'Sửa cấu hình'; +$lang['L_OSWEITER'] = 'Tiếp tục (bỏ qua không Lưu)'; +$lang['L_ERRORMAN'] = 'Lỗi trong khi Lưu cấu hình!
      Sửa lại File '; +$lang['L_MANUELL'] = 'bằng tay'; +$lang['L_CREATEDIRS'] = 'Tạo ra những thư mục'; +$lang['L_INSTALL_CONTINUE'] = 'Tiếp tục với sự cài đặt'; +$lang['L_CONNECTTOMYSQL'] = 'Kết nối tới MySQL '; +$lang['L_DBPARAMETER'] = 'Những tham số Cơ sở dữ liệu'; +$lang['L_CONFIGNOTWRITABLE'] = 'Tôi không thể viết vào file "config.php". +Hãy dùng trình FTP của bạn và chmod afile này thành 0777.'; +$lang['L_DBCONNECTION'] = 'Kết nối Cơ sở dữ liệu'; +$lang['L_CONNECTIONERROR'] = 'Lỗi: Không thể nối.'; +$lang['L_CONNECTION_OK'] = 'Kết nối Cơ sở dữ liệu được thiết lập.'; +$lang['L_SAVEANDCONTINUE'] = 'Lưu lại và tiếp tục sự cài đặt'; +$lang['L_CONFBASIC'] = 'Tham số Cơ bản'; +$lang['L_INSTALL_STEP2FINISHED'] = 'Những tham số Cơ sở dữ liệu được Lưu lại thành công.'; +$lang['L_INSTALL_STEP2_1'] = 'Tiếp tục cài đặt với những thiết đặt mặc định'; +$lang['L_LASTSTEP'] = 'Kết thúc Cài đặt'; +$lang['L_FTPMODE'] = 'Tạo ra những thư mục cần thiết Trong safe-mode'; +$lang['L_IDOMANUAL'] = 'Tôi tạo ra những thư mục của tôi'; +$lang['L_DOFROM'] = 'bắt đầu từ'; +$lang['L_FTPMODE2'] = 'Tạo các thư mục với FTP:'; +$lang['L_CONNECT'] = 'kết nối'; +$lang['L_DIRS_CREATED'] = 'Các thư mục được tạo ra và được chấp nhận.'; +$lang['L_CONNECT_TO'] = 'kết nối tới'; +$lang['L_CHANGEDIR'] = 'đổi thư mục'; +$lang['L_CHANGEDIRERROR'] = 'không thể đổi thư mục'; +$lang['L_FTP_OK'] = 'tham số FTP được chấp nhận'; +$lang['L_CREATEDIRS2'] = 'Tạo các thư mục'; +$lang['L_FTP_NOTCONNECTED'] = 'Kết nối FTP không được thiết lập!'; +$lang['L_CONNWITH'] = 'Kết nối Với'; +$lang['L_ASUSER'] = 'như người sử dụng'; +$lang['L_NOTPOSSIBLE'] = 'không thể'; +$lang['L_DIRCR1'] = 'tạo thư mục work'; +$lang['L_DIRCR2'] = 'tạo thư mục backup'; +$lang['L_DIRCR4'] = 'tạo thư mục log'; +$lang['L_DIRCR5'] = 'tạo thư mục configuration'; +$lang['L_INDIR'] = 'đang ở thư mục'; +$lang['L_CHECK_DIRS'] = 'Kiểm tra các thư mục'; +$lang['L_DISABLEDFUNCTIONS'] = 'Vô hiệu hóa những tính năng'; +$lang['L_NOFTPPOSSIBLE'] = 'Bạn không có những tính năng FTP !'; +$lang['L_NOGZPOSSIBLE'] = 'Bạn không có những tính năng nén !'; +$lang['L_UI1'] = 'Tất cả các thư mục làm việc mà có thể chứa đựng những sao lưu sẽ được xóa.'; +$lang['L_UI2'] = 'Bạn chắc chắn bạn muốn điều đó?'; +$lang['L_UI3'] = 'không, bỏ qua ngay lập tức'; +$lang['L_UI4'] = 'có, cứ tiếp tục'; +$lang['L_UI5'] = 'xóa những thư mục làm việc'; +$lang['L_UI6'] = 'mọi thứ đã bị xóa thành công.'; +$lang['L_UI7'] = 'Xin xóa thư mục script'; +$lang['L_UI8'] = 'lên mức trên'; +$lang['L_UI9'] = 'Có lỗi, không thể xóa

      Lỗi với thư mục '; +$lang['L_IMPORT'] = 'Nhập Cấu hình'; +$lang['L_IMPORT3'] = 'Cấu hình được tải ...'; +$lang['L_IMPORT4'] = 'Đã ghi cấu hình.'; +$lang['L_IMPORT5'] = 'Chạy MyOOS [Dumper]'; +$lang['L_IMPORT6'] = 'Menu cài đặt'; +$lang['L_IMPORT7'] = 'Upload cấu hình'; +$lang['L_IMPORT8'] = 'quay lại để upload'; +$lang['L_IMPORT9'] = 'Đây không là một sao lưu cấu hình !'; +$lang['L_IMPORT10'] = 'Cấu hình được upload thành công ...'; +$lang['L_IMPORT11'] = 'Lỗi: Có vấn đề khi đang viết sql_statements'; +$lang['L_IMPORT12'] = 'Lỗi: Có vấn đề khi đang viết config.php'; +$lang['L_INSTALL_HELP_PORT'] = '(để trống = Cổng mặc định)'; +$lang['L_INSTALL_HELP_SOCKET'] = '(để trống = Socket mặc định)'; +$lang['L_TRYAGAIN'] = 'Thử lại'; +$lang['L_SOCKET'] = 'Socket'; +$lang['L_PORT'] = 'Cổng'; +$lang['L_FOUND_DB'] = 'tìm thấy db'; +$lang['L_FM_FILEUPLOAD'] = 'Upload file'; +$lang['L_PASS'] = 'Password'; +$lang['L_NO_DB_FOUND_INFO'] = 'Kết nối tới cSDL được thiết lập thành công.
      +Dữ liệu thành viên hợp lệ và được MySQL-Server chấp nhận.
      +Nhưng MyOOS [Dumper] không thể tìm thấy bất kỳ cơ sở dữ liệu nào.
      +Dò tìm tự động qua script bị cấm trên một vài server.
      +Bạn phải vào databasename của bạn bằng tay sau khi sự cài đặt (thì) kết thúc. +Click "cấu hình" "Tham số Kết nối - hiển thị" và nhập tên CSDL đó.'; +$lang['L_ENTER_DB_INFO'] = 'First click the button "Connect to MySQL". Only if no database could be detected you need to provide a database name here.'; diff --git a/msd/language/vn/lang_log.php b/msd/language/vn/lang_log.php new file mode 100644 index 0000000..c602f6d --- /dev/null +++ b/msd/language/vn/lang_log.php @@ -0,0 +1,10 @@ +Hãy tạo ra 2 file bằng tay với nội dung sau đây'; +$lang['L_HTACC_CHECK_ERROR'] = 'It could not be checked whether the program is protected!
      The simulated external access could not be carried out.'; +$lang['L_HTACC_NOT_NEEDED'] = 'The program is protected by higher-level authorizations; local directory protection is not required.'; +$lang['L_HTACC_COMPLETE'] = 'The program is protected, the directory protection is complete.'; +$lang['L_HTACC_INCOMPLETE'] = 'The program is not protected, the directory protection is incomplete!'; +$lang['L_HTACC_PROPOSED'] = 'The program is not protected, directory protection is strongly recommended!'; +$lang['L_HTACC_EDIT'] = 'Sửa .htaccess'; +$lang['L_HTACCESS18'] = 'Tạo .htaccess trong '; +$lang['L_HTACCESS19'] = 'Nạp lại '; +$lang['L_HTACCESS20'] = 'Thực hiện script'; +$lang['L_HTACCESS21'] = 'Thêm người điều khiển'; +$lang['L_HTACCESS22'] = 'Làm cho có thể thực hiện'; +$lang['L_HTACCESS23'] = 'Danh sách Thư mục'; +$lang['L_HTACCESS24'] = 'Tài liệu Lỗi'; +$lang['L_HTACCESS25'] = 'Kích hoạt Viết lại'; +$lang['L_HTACCESS26'] = 'Từ chối / Cho phép'; +$lang['L_HTACCESS27'] = 'Gửi một lần nữa'; +$lang['L_HTACCESS28'] = 'Danh sách lỗi được ghi nhận'; +$lang['L_HTACCESS29'] = 'Xem thêm ví dụ và tài liệu'; +$lang['L_HTACCESS30'] = 'Nhà cung cấp'; +$lang['L_HTACCESS31'] = 'Tổng quan'; +$lang['L_HTACCESS32'] = 'Chú ý! file .htaccess trực tiếp ảnh hưởng đến hoạt động của trình duyệt.
      Với nội dung sai, những trang này có thể bị chặn truy cập.'; +$lang['L_DISABLEDFUNCTIONS'] = 'Vô hiệu hóa những chức năng'; +$lang['L_NOGZPOSSIBLE'] = 'Vì Zlib chưa được cài đặt, bạn không thể sử dụng thư viện GZip!'; +$lang['L_DELETE_HTACCESS'] = 'Bỏ bảo vệ thư mục (xóa .htaccess)'; +$lang['L_WRONG_RIGHTS'] = "File hay thư mục '%s' không cho phép ghi.
      +Chế độ chmod không đúng hoặc nó không dành cho chúng ta.
      +Đặt lại thuộc tính cho đúng bằng cách sử dụng trình FTP.
      +File hay thư mục cần được thiết lập thành %s.
      "; +$lang['L_CANT_CREATE_DIR'] = "Không thể tạo thư mục '%s'. +Hãy tạo ra nó bằng cách sử dụng trình FTP."; +$lang['L_TABLE_TYPE'] = 'Type'; +$lang['L_CHECK'] = 'check'; +$lang['L_OS'] = 'Operating system'; +$lang['L_MOD_VERSION'] = 'MyOOS [Dumper] - Version'; +$lang['L_NEW_MOD_VERSION'] = 'New Version'; +$lang['L_NEW_MOD_VERSION_INFO'] = 'There is a new version of MyOOS [Dumper] available.'; +$lang['L_UPDATED_IMPORTANT'] = 'Important: Before updating, please backup your files.'; +$lang['L_UPDATE'] = 'Update now'; +$lang['L_MYSQL_VERSION'] = 'MySQL-Version'; +$lang['L_PHP_VERSION'] = 'PHP-Version'; +$lang['L_MAX_EXECUTION_TIME'] = 'Max execution time'; +$lang['L_PHP_EXTENSIONS'] = 'PHP-Extensions'; +$lang['L_MEMORY'] = 'Memory'; +$lang['L_FILE_MISSING'] = 'không tìm thấy file'; +$lang['L_INSTALLING_UPDATES'] = 'Installing Updates'; +$lang['L_UPDATE_SUCCESSFUL'] = 'Update successful'; +$lang['L_UPDATE_FAILED'] = 'Update failed'; +$lang['L_UP_TO_DATE'] = 'Current Version is up to date'; diff --git a/msd/language/vn/lang_restore.php b/msd/language/vn/lang_restore.php new file mode 100644 index 0000000..984c2f9 --- /dev/null +++ b/msd/language/vn/lang_restore.php @@ -0,0 +1,22 @@ +%d bảng đã được tạo ra.'; +$lang['L_FILE_MISSING'] = 'không tìm thấy file'; +$lang['L_RESTORE_DB'] = "'%s' CSDL trong '%s'."; +$lang['L_RESTORE_COMPLETE'] = '%s bảng đã được tạo ra.'; +$lang['L_RESTORE_RUN1'] = '
      Tính đến giờ, %s trong số %s bản ghi đã được thêm vào thành công.'; +$lang['L_RESTORE_RUN2'] = "
      Hiện tại bảng '%s' đang được phục hồi.

      "; +$lang['L_RESTORE_COMPLETE2'] = '%s bản ghi được chèn vào.'; +$lang['L_RESTORE_TABLES_COMPLETED'] = 'Tính đến giờ, %d trong số %d table đã được tạo.'; +$lang['L_RESTORE_TOTAL_COMPLETE'] = '
      Chúc mừng.

      Việc phục hồi cơ sở dữ liệu đã xong.
      Tất cả dữ liệu Sao lưu đã được phục hồi.

      Mọi việc đã kết thúc. :-)'; +$lang['L_DB_SELECT_ERROR'] = '
      Lỗi:
      Lựa chọn CSDL '; +$lang['L_DB_SELECT_ERROR2'] = ' thất bại!'; +$lang['L_FILE_OPEN_ERROR'] = 'Lỗi: Không thể mở file.'; +$lang['L_PROGRESS_OVER_ALL'] = 'Toàn bộ tiến trình'; +$lang['L_BACK_TO_OVERVIEW'] = 'Tổng quan Cơ sở dữ liệu'; +$lang['L_RESTORE_RUN0'] = '
      Tính đến giờ, %s bản ghi đã được thêm vào thành công.'; +$lang['L_UNKNOWN_SQLCOMMAND'] = 'không hiểu lệnh SQL'; +$lang['L_NOTICES'] = 'Chú ý + + +'; diff --git a/msd/language/vn/lang_sql.php b/msd/language/vn/lang_sql.php new file mode 100644 index 0000000..97db56c --- /dev/null +++ b/msd/language/vn/lang_sql.php @@ -0,0 +1,190 @@ +%s dòng được xuất'; +$lang['L_CSV_FIELDCOUNT_NOMATCH'] = 'Việc đếm các Trường không đồng nghĩa rằng dữ liệu xuất ra (%d thay vì %d).'; +$lang['L_CSV_FIELDSLINES'] = '%d Trường được ghi nhận, tổng số %d dòng'; +$lang['L_CSV_ERRORCREATETABLE'] = 'Lỗi trong khi tạo bảng `%s` !'; +$lang['L_FM_UPLOADFILEREQUEST'] = 'chọn 1 file file.'; +$lang['L_CSV_NODATA'] = 'Không tìm thấy dữ liệu nhập vào!'; +$lang['L_SQLLIB_GENERALFUNCTIONS'] = 'những chức năng chung'; +$lang['L_SQLLIB_RESETAUTO'] = 'chạy lại auto-increment (tự đánh số)'; +$lang['L_SQLLIB_BOARDS'] = 'Boards'; +$lang['L_SQLLIB_DEACTIVATEBOARD'] = 'ngưng kích hoạt Board'; +$lang['L_SQLLIB_ACTIVATEBOARD'] = 'kích hoạt Board'; +$lang['L_SQL_NOTABLESSELECTED'] = 'Chưa chọn bảng !'; +$lang['L_TOOLS'] = 'Những công cụ'; +$lang['L_TOOLS_TOOLBOX'] = 'Chọn CSDL / Tính năng của CSDL / Nhập - Xuất '; +$lang['L_SQL_OPENFILE'] = 'Mở SQL-File'; +$lang['L_SQL_OPENFILE_BUTTON'] = 'Upload'; +$lang['L_MAX_UPLOAD_SIZE'] = 'Cỡ file tối đa'; +$lang['L_SQL_SEARCH'] = 'Tìm'; +$lang['L_SQL_SEARCHWORDS'] = 'Từ khóa'; +$lang['L_START_SQL_SEARCH'] = 'bắt đầu tìm'; +$lang['L_RESET_SEARCHWORDS'] = 'xóa'; +$lang['L_SEARCH_OPTIONS'] = 'Tùy chọn tìm kiếm'; +$lang['L_SEARCH_RESULTS'] = 'Kết quả tìm kiếm "%s" trong bảng "%s" như sau'; +$lang['L_SEARCH_NO_RESULTS'] = 'Tìm kiếm cho "%s" trong bảng "%s" không mang lại bất cứ kết quả nào!'; +$lang['L_NO_ENTRIES'] = 'Bảng "%s" trống rỗng và không có bất kỳ mục vào nào.'; +$lang['L_SEARCH_ACCESS_KEYS'] = 'Duyệt: trở đi=ALT+V, trở lại=ALT+C'; +$lang['L_SEARCH_OPTIONS_OR'] = 'Mỗi cột phải có 1 từ khóa (OR-search)'; +$lang['L_SEARCH_OPTIONS_CONCAT'] = 'một dòng phải chứa tất cả các từ khóa trừ phi họ có thể trong bất kỳ cột nào (thỉnh thoảng có thể ngoại lệ)'; +$lang['L_SEARCH_OPTIONS_AND'] = 'cột phải chứa tất cả từ khóa (AND-search)'; +$lang['L_SEARCH_IN_TABLE'] = 'Tìm trong Bảng'; +$lang['L_ERROR_NO_FIELDS'] = 'Search error: it could not be determined which fields the table "%s" has!'; +$lang['L_SQL_EDIT_TABLESTRUCTURE'] = 'Sửa cấu trúc bảng'; +$lang['L_DEFAULT_CHARSET'] = 'Đặt bảng làm mặc định'; +$lang['L_TITLE_KEY_PRIMARY'] = 'Primary key'; +$lang['L_TITLE_KEY_UNIQUE'] = 'Unique key'; +$lang['L_TITLE_INDEX'] = 'Index'; +$lang['L_TITLE_KEY_FULLTEXT'] = 'Fulltext key'; +$lang['L_TITLE_NOKEY'] = 'No key'; +$lang['L_TITLE_SEARCH'] = 'Search'; +$lang['L_TITLE_MYSQL_HELP'] = 'MySQl Documentation'; +$lang['L_TITLE_UPLOAD'] = 'Upload SQL file'; +$lang['L_PRIMARYKEY_DELETED'] = 'Primary key deleted'; +$lang['L_PRIMARYKEY_NOTFOUND'] = 'Primary key not found'; +$lang['L_PRIMARYKEYS_CHANGED'] = 'Primary keys changed'; +$lang['L_PRIMARYKEYS_CHANGINGERROR'] = 'Error changing primary keys'; +$lang['L_SQL_VIEW_COMPACT'] = 'View: compact'; +$lang['L_SQL_VIEW_STANDARD'] = 'View: standard'; +$lang['L_FIELDS_OF_TABLE'] = 'Fields of table'; +$lang['L_ENGINE'] = 'Engine'; +$lang['L_USERNAME'] = 'Username'; +$lang['L_PASSWORD'] = 'Password'; +$lang['L_PASSWORD_REPEAT'] = 'Password (repeat)'; +$lang['L_INFO_SIZE'] = 'kích thước'; +$lang['L_TABLE_TYPE'] = 'Type'; +$lang['L_KEY_DELETED'] = 'Index deleted'; +$lang['L_KEY_DELETEERROR'] = 'Error deleting index'; +$lang['L_KEY_ADDED'] = 'Index added'; +$lang['L_KEY_ADDERROR'] = 'Error adding index'; diff --git a/msd/license.txt b/msd/license.txt new file mode 100644 index 0000000..2e3a6a5 --- /dev/null +++ b/msd/license.txt @@ -0,0 +1,278 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + diff --git a/msd/log.php b/msd/log.php new file mode 100644 index 0000000..1c2a9b7 --- /dev/null +++ b/msd/log.php @@ -0,0 +1,165 @@ +ERROR !
      Logdir is not writable

      '); +} + +//lesen +$errorbutton = ''; +$perlbutton = ''; +$perlbutton2 = ''; + +if (file_exists($loginfo['errorlog'])) { + $errorbutton = ''; +} +if (file_exists($loginfo['perllog'])) { + $perlbutton = ''; +} +if (file_exists($loginfo['perllogcomplete'])) { + $perlbutton2 = ''; +} + +//anzeigen +echo '
      '; +echo ''; +echo "\n".$errorbutton."\n".$perlbutton."\n".$perlbutton2."\n"; +echo '

      '; + +//Status Logfiles +$icon['blank'] = isset($icon['blank']) ? $icon['blank'] : $config['files']['iconpath'].'blank.gif'; +echo '
      '; +echo ''; +echo '
      '.$lang['L_LOGFILEFORMAT'].'

      '.((isset($config['logcompression']) && (1 == $config['logcompression'])) ? 'compressed' : ''); +echo ''.(((isset($config['logcompression']) && 1 == $config['logcompression'])) ? $lang['L_COMPRESSED'] : $lang['L_NOTCOMPRESSED']).'
            '; +echo ''.substr($loginfo['log'], strrpos($loginfo['log'], '/') + 1).'
      '; +echo ($loginfo['errorlog_size'] > 0) ? ''.substr($loginfo['errorlog'], strrpos($loginfo['errorlog'], '/') + 1).'
      ' : substr($loginfo['errorlog'], strrpos($loginfo['errorlog'], '/') + 1).'
      '; +echo ($loginfo['perllog_size'] > 0) ? ''.substr($loginfo['perllog'], strrpos($loginfo['perllog'], '/') + 1).'
      ' : substr($loginfo['perllog'], strrpos($loginfo['perllog'], '/') + 1).'
      '; +echo ($loginfo['perllogcomplete_size'] > 0) ? ''.substr($loginfo['perllogcomplete'], strrpos($loginfo['perllogcomplete'], '/') + 1).'
      ' : substr($loginfo['perllogcomplete'], strrpos($loginfo['perllogcomplete'], '/') + 1).'
      '; +echo 'total
      '.byte_output($loginfo['log_size']).'
      '.byte_output($loginfo['errorlog_size']).'
      '.byte_output($loginfo['perllog_size']).'
      '.byte_output($loginfo['perllogcomplete_size']).'
      '.byte_output($loginfo['log_totalsize']).'
      '.$lang['L_NOREVERSE'].'   '.$lang['L_REVERSE'].'
      '; + +$out = ''; +if (2 != $r) { + $out .= '
      ';
      +}
      +
      +if (file_exists($lfile)) {
      +    $zeilen = ((isset($config['logcompression']) && 1 == $config['logcompression'])) ? gzfile($lfile) : file($lfile);
      +    if (30 == $r) {
      +        echo '
      '.print_r($zeilen, true).'
      '; + exit(); + } + if (1 == $revers) { + $zeilen = array_reverse($zeilen); + } + foreach ($zeilen as $zeile) { + if (2 == $r) { + $out .= $zeile.'
      '; + } elseif (3 == $r) { + $z = explode('|:|', $zeile); + for ($i = 0; $i < count($z); ++$i) { + $out .= ''.substr($z[$i], 0, strpos($z[$i], ': ')).' '.substr($z[$i], strpos($z[$i], ': ')).'
      '; + } + } else { + $out .= $zeile; + } + } +} +if (2 != $r) { + $out .= '
      '; +} + +$suchen = [ + '', + '', +]; +$ersetzen = [ + '', + '', +]; +$out = str_replace($suchen, $ersetzen, $out); + +if ('' != $out) { + echo '

      '; + echo ''; + echo '

      '.$out.'
      '; +} + +echo '
      '; +echo MODFooter(); +ob_end_flush(); +exit(); diff --git a/msd/main.php b/msd/main.php new file mode 100644 index 0000000..134d4da --- /dev/null +++ b/msd/main.php @@ -0,0 +1,118 @@ +<< Home

      '; + phpinfo(); + echo '

      << Home

      '; + exit(); +} + +if (isset($_POST['htaccess']) || 'schutz' == $action) { + include './inc/home/protection_create.php'; +} +if ('edithtaccess' == $action) { + include './inc/home/protection_edit.php'; +} +if ('deletehtaccess' == $action) { + include './inc/home/protection_delete.php'; +} + +$check_update = false; +if (extension_loaded('zlib')) { + $update = new AutoUpdate($config['paths']['temp'], $config['paths']['root'], 60); + $update->setCurrentVersion(MOD_VERSION); + + // Replace with your server update directory + $update->setUpdateUrl('https://oos-shop.de/modserver'); + + // Custom logger (optional) + $logger = new \Monolog\Logger("default"); + $logger->pushHandler(new Monolog\Handler\StreamHandler($config['paths']['log'] . 'update.log')); + $update->setLogger($logger); + + + // Cache (optional but recommended) + $cache = new Desarrolla2\Cache\File($config['paths']['cache']); + $update->setCache($cache, 3600); + + // Check for a new update + if ($update->checkUpdate() === false) { + // die('Could not check for updates! See log file for details.'); + $check_update = false; + } else { + $check_update = true; + } + + if ('update' == $action) { + echo MODHeader(); + require_once './inc/home/update.php'; + echo MODFooter(); + exit; + } +} + + +// Output headnavi +$tpl = new MODTemplate(); +$tpl->set_filenames([ + 'show' => 'tpl/home/headnavi.tpl', ]); +$tpl->assign_vars([ + 'HEADER' => MODHeader(), + 'HEADLINE' => headline($lang['L_HOME']), ]); +$tpl->pparse('show'); + +mod_mysqli_connect(); +if ('status' == $action) { + include './inc/home/home.php'; +} elseif ('db' == $action) { + include './inc/home/databases.php'; +} elseif ('sys' == $action) { + include './inc/home/system.php'; +} elseif ('vars' == $action) { + include './inc/home/mysql_variables.php'; +} + +echo MODFooter(); +ob_end_flush(); +exit(); diff --git a/msd/menu.php b/msd/menu.php new file mode 100644 index 0000000..dcb0baa --- /dev/null +++ b/msd/menu.php @@ -0,0 +1,151 @@ +set_filenames([ + 'header' => 'tpl/menu/header.tpl', + 'footer' => 'tpl/menu/footer.tpl', + 'content' => 'tpl/menu/content.tpl', ]); + +$tpl->assign_vars([ + 'MOD_VERSION' => MOD_VERSION, + 'CONFIG_HOMEPAGE' => $config['homepage'], + 'CONFIG_THEME' => $config['theme'], ]); + +if (isset($_POST['selected_config']) || isset($_GET['config'])) { + if (isset($_POST['selected_config'])) { + $new_config = $_POST['selected_config']; + } + // Configuration was switched in content frame? + if (isset($_GET['config'])) { + $new_config = $_GET['config']; + } + // restore the last active menuitem + if (is_readable($config['paths']['config'].$new_config.'.php')) { + clearstatcache(); + unset($databases); + $databases = []; + if (read_config($new_config)) { + $config['config_file'] = $new_config; + $_SESSION['config_file'] = $new_config; //$config['config_file']; + $config_refresh = ' + '; + } + if (isset($_GET['config'])) { + $config_refresh = ''; + } //Neu-Aufruf bei Uebergabe aus Content-Bereich verhindern + } +} + +echo MODHeader(1); +echo headline('', 0); + +if ($config_refresh > '') { + $tpl->assign_block_vars('CONFIG_REFRESH_TRUE', []); + $tpl->assign_var('CONFIG_REFRESH', $config_refresh); +} + +// changed language +if ($config['language'] != $lang_old) { + $tpl->assign_block_vars('CHANGED_LANGUAGE', []); +} + +if (isset($_GET['action'])) { + if ('dbrefresh' == $_GET['action']) { + // remember the name of the selected database + $old_dbname = isset($databases['Name'][$databases['db_selected_index']]) ? $databases['Name'][$databases['db_selected_index']] : ''; + SetDefault(); + // select old database if it still is there + SelectDB($old_dbname); + $tpl->assign_block_vars('DB_REFRESH', []); + } +} + +if (isset($_POST['dbindex'])) { + $dbindex = intval($_POST['dbindex']); + $databases['db_selected_index'] = $dbindex; + $databases['db_actual'] = $databases['Name'][$dbindex]; + + SelectDB($dbindex); + WriteParams(0); + $tpl->assign_block_vars('DB_REFRESH', []); +} else { + $dbindex = 0; +} + +if (isset($_GET['dbindex'])) { + $dbindex = intval($_GET['dbindex']); + $databases['db_selected_index'] = $dbindex; + $databases['db_actual'] = $databases['Name'][$dbindex]; + SelectDB($dbindex); + WriteParams(0); +} + +if (isset($databases['Name']) && count($databases['Name']) > 0) { + $tpl->assign_block_vars('MAINTENANCE', []); + $tpl->assign_vars([ + 'DB_ACTUAL' => $databases['db_actual'], + 'DB_SELECTED_INDEX' => $databases['db_selected_index'], ]); +} +$tpl->assign_var('GET_FILELIST', get_config_filelist()); + +if (isset($databases['Name']) && count($databases['Name']) > 0) { + $tpl->assign_block_vars('DB_LIST', []); + $datenbanken = count($databases['Name']); + for ($i = 0; $i < $datenbanken; ++$i) { + $selected = ($i == $databases['db_selected_index']) ? ' selected' : ''; + $tpl->assign_block_vars('DB_LIST.DB_ROW', [ + 'ID' => $i, + 'NAME' => $databases['Name'][$i], + 'SELECTED' => $selected, ]); + } +} else { + $tpl->assign_block_vars('NO_DB_FOUND', []); +} + +$tpl->assign_var('PIC_CACHE', PicCache()); + +if (!isset($databases['Name']) || count($databases['Name']) < 1) { + $tpl->assign_block_vars('DB_NAME_TRUE', []); +} else { + $tpl->assign_block_vars('DB_NAME_FALSE', []); +} + +$tpl->pparse('header'); +$tpl->pparse('content'); +$tpl->pparse('footer'); + +ob_end_flush(); diff --git a/msd/mod_cron/crondump.pl b/msd/mod_cron/crondump.pl new file mode 100644 index 0000000..08399b4 --- /dev/null +++ b/msd/mod_cron/crondump.pl @@ -0,0 +1,1426 @@ +#!/usr/bin/perl -w +# +# MyOOS [Dumper] +# https://www.oos-shop.de/ +# +# Copyright (c) 2013 - 2022 by the MyOOS Development Team. +# ---------------------------------------------------------------------- +# Based on: +# +# MySqlDumper +# http://www.mysqldumper.de +# +# Copyright (C)2004-2011 Daniel Schlichtholz (admin@mysqldumper.de) +# ---------------------------------------------------------------------- +# Released under the GNU General Public License +# ---------------------------------------------------------------------- +# +######################################################################################## +# MySQLDumper CronDump +# +# 2004-2010 by Steffen Kamper, Daniel Schlichtholz +# additional scripting: Detlev Richter, Jonathan Tietz +# +# This file is part of MySQLDumper released under the GNU/GPL 2 license +# http://www.mysqldumper.net +# @package MySQLDumper +# @version Rev: 1371 $ +# @author Author: dsb1971 $ +# +######################################################################################## +# Script-Version +my $pcd_version='5.0.20'; + +######################################################################################## +# please enter the absolute path of the config-dir +# when calling the script without parameters the default_configfile (myoosdumper.conf.php) will be loaded +# e.g. - (zum Beispiel): +# my $absolute_path_of_configdir="/home/www/doc/8176/example.org/www/myoosdumper/work/config/"; + +my $absolute_path_of_configdir="/var/www/web360/htdocs/survey/msd/work/config/"; +my $cgibin_path=""; # this is needed for MIME::Lite if it is in cgi-bin +my $default_configfile="myoosdumper.conf.php"; + +######################################################################################## +# nothing to edit below this line !!! +######################################################################################## +# import the necessary modules ... +use strict; +use warnings; +use DBI; +use File::Find; +use File::Basename; +use CGI::Carp qw(warningsToBrowser fatalsToBrowser); +warningsToBrowser(1); +use CGI; +use Data::Dumper; +use Getopt::Long; + +######################################################################################## +use vars qw( +$pcd_version $dbhost $dbname $dbuser $dbpass $dbport $dbsocket +$cron_dbindex @cron_db_array @ftp_server $dbpraefix @cron_dbpraefix_array +$compression $backup_path $logdatei $completelogdatei $command_beforedump $command_afterdump +$cron_printout $cronmail $cronmail_dump $cronmailto $cronmailto_cc $cronmailfrom +$cronftp $mp $multipart_groesse $email_maxsize +$auto_delete $max_backup_files $perlspeed $optimize_tables_beforedump $result +@key_value $pair $key $value $conffile @confname $logcompression $log_maxsize $complete_log +$starttime $Sekunden $Minuten $Stunden $Monatstag $Monat $Jahr $Wochentag $Jahrestag $Sommerzeit +$rct $tabelle @tables @tablerecords $dt $sql_create @ergebnis @ar $sql_daten $inhalt +$insert $totalrecords $error_message $cfh $oldbar $print_out $msg $dt $ftp $dateistamm $dateiendung +$mpdatei $i $BodyNormal $BodyMultipart $BodyToBig $BodyNoAttach $BodyAttachOnly $Body $DoAttach $cmt $part $fpath $fname +$fmtime $timenow $daydiff $datei $inh $gz $search $fdbname @str $item %dbanz $anz %db_dat +$fieldlist $first_insert $my_comment $sendmail_call $config_read_from +$cron_smtp $cron_smtp_port $cron_use_sendmail +@ftp_transfer @ftp_timeout @ftp_user @ftp_pass @ftp_dir @ftp_server @ftp_port @ftp_mode @ftp_useSSL +$output $query $skip $html_output $datei +@trash_files $time_stamp @filearr $sql_file $backupfile $memory_limit $dbh $sth @db_array +@dbpraefix_array @cron_command_before_dump @cron_command_after_dump $db_anz +$record_count $filesize $status_start $status_end $sql_text $punktzaehler @backupfiles_name +@backupfiles_size $mysql_commentstring $character_set $mod_gz $mod_mime $mod_ftp +$mod_ftpssl @multipartfiles %db_tables @tablenames $tablename $opttbl $command $current_dir +); + +$memory_limit=100000; +$mysql_commentstring="-- "; +$character_set="utf8mb4"; +$sql_text=''; +$sql_file=''; +$punktzaehler=0; +@trash_files=(); +@filearr=(); +$opttbl=0; +$dbpraefix=""; +$complete_log= 0; +$cron_printout = 1; +#config file +$conffile=""; + +#return perl version +sub GetPerlVersion (){ + my $pversion ; + if ($^V){ + $pversion = sprintf "v%vd", $^V ; # v5.10.1 + }else{ + $pversion = local $]; + } + return $pversion; +} + +# import the optional modules ... +my $eval_in_died; +$mod_gz=0; +$mod_ftp=0; +$mod_mime=0; +$mod_ftpssl=0; +push (@INC, "$cgibin_path"); + +eval { $eval_in_died = 1; require Compress::Zlib; }; +if(!$@){ + $mod_gz = 1; + import Compress::Zlib; +} +eval { $eval_in_died = 1; require Net::FTP; }; +if(!$@){ + $mod_ftp = 1; + import Net::FTP; +} +eval { $eval_in_died = 1; require Net::FTPSSL; }; +if(!$@){ + $mod_ftpssl = 1; + import Net::FTPSSL; +} +eval { $eval_in_died = 1; require MIME::Lite; }; +if(!$@){ + $mod_mime = 1; + import MIME::Lite; +} + +#include config file +$conffile=""; + +#read args from command +GetOptions ("config=s" => \$conffile, "html_output=s" => \$html_output); +if (!defined $html_output) { $html_output=0; }; # suppress HTML Output + +#called via browser or cmd +if($ENV{'QUERY_STRING'}) { + $html_output=1; # turn HTML Output on if called via Browser-Request + my $querystring=$ENV{'QUERY_STRING'}; + #$querystring=~ s/\?/ /g; + @key_value = split(/&/,$querystring); + foreach $pair(@key_value) + { + #$pair =~ tr/+/ /; + ($key,$value) = split(/=/,$pair); + if($key eq "config") + { + $value=~ s/\?/ /g; + $conffile=$value; + $config_read_from="Querystring"; + } + if($key eq "html_output") { $html_output=$value; }; # overwrite var if given in call + } +}else{ + $config_read_from="shell"; +} + +# Now we know if script was called via HTTP-Requets or from Shell. So output Headers +PrintHeader(); + +# Security: try to detect wether someone tries to include some external configfile +die "Hacking attempt - I wont do anything!\nGo away\n\n" if (lc($conffile) =~ m /:/); + +#try to guess path if $absolute_path_of_configdir is not filled +if($absolute_path_of_configdir eq "" || ! -d $absolute_path_of_configdir) +{ + #get full path + if ($config_read_from eq "shell") { $i=$0; } else { $i=$ENV{'SCRIPT_FILENAME'}; }; + if ($i=~m#^(.*)\\#) { + #windows + $current_dir = $1; + $current_dir =~ s/mod\_cron//g; + + #set default log-files + $logdatei= $current_dir ."work\\log\\myoosdump_perl.log"; + $completelogdatei= $current_dir . "work\\log\\myoosdump_perl.complete.log"; + + $absolute_path_of_configdir = $current_dir ."work\\config\\"; + } elsif ($i=~m#^(.*)\/# ) { + #*nix + $current_dir = $1; + $current_dir =~ s/mod\_cron//g; + + #set default log-files + $logdatei= $current_dir ."work/log/myoosdump_perl.log"; + $completelogdatei= $current_dir . "work/log/myoosdump_perl.complete.log"; + + $absolute_path_of_configdir = $current_dir."work/config/"; + } + #$absolute_path_of_configdir =~ s/mod\_cron//g; + $backup_path = $absolute_path_of_configdir; + $backup_path =~ s/config/backup/g; + + #if zlib is available, set default to compress + if ($mod_gz){ + $logdatei .= ".gz"; + $completelogdatei .= ".gz"; + $logcompression=1; + } +} + +$conffile=trim($conffile); +if($conffile eq "") +{ + $conffile=$default_configfile; # no Parameter for configfile given -> use standardfile "mysqldumper.conf.php" + $config_read_from="standard configuration"; +} + +# check config-dir +$absolute_path_of_configdir=trim($absolute_path_of_configdir); # remove spaces +if (!opendir(DIR, $absolute_path_of_configdir)){ + err_trap("The config-directory you entered is wrong !\n($absolute_path_of_configdir - $!) \n\nPlease edit $0 and enter the right configuration-path.\n",0,1); +} +closedir(DIR); + +#add trailing slash to confdir +if(substr($absolute_path_of_configdir,-1) ne "/") { + $absolute_path_of_configdir=$absolute_path_of_configdir."/"; +} + +#add conffile extension +if (substr($conffile,length($conffile)-5,5) eq '.conf') { $conffile.='.php'; }; +if (substr($conffile,length($conffile)-9,9) ne '.conf.php') { $conffile.='.conf.php'; }; + +# load configuration file +$datei=$absolute_path_of_configdir.$conffile; +open(CONFIG,"<$datei") or die "\nI couldn't open the configurationfile:".$datei."\nFile not found or not accessible!\n\n"; +while (my $line = ) +{ + chomp($line); + if ($line ne '' && substr($line,0,1) ne '#') + { + eval($line); + } +} +close(CONFIG); + +if ($html_output==1) { $cron_printout=1; }; # overwrite output if HTML-Output is activated + +# more than one conffile? +@confname=split(/\//,$conffile); + + +PrintOut("Configuration '".$conffile."' loaded successfully from ".$config_read_from."."); +if($mod_gz==1) { + PrintOut("Compression Library loaded successfully..."); +} else { + $compression=0; + PrintOut("Compression Library loading failed - Compression deactivated ..."); +} +if($mod_ftp==1) { + PrintOut("FTP Library loaded successfully..."); +} else { + $cronftp=0; + PrintOut("FTP Library loading failed - FTP deactivated ..."); +} +if($mod_ftpssl==1) { + PrintOut("FTP-SSL Library loaded successfully..."); + $cronftp=1; +} else { + $cronftp=0; + PrintOut("FTP-SSL Library loading failed - FTP-SSL deactivated ..."); +} +if($mod_mime==1) { + PrintOut("Mail Library loaded successfully..."); +} else { + $cronmail=0; + PrintOut("Mail Library loading failed - Mail deactivated ..."); +} + +#try writing to logfile +write_log("***********************************************************************\n"); +write_log("Starting backup using Perlscript version $pcd_version (using perl ".GetPerlVersion().")\n"); +PrintOut("Starting backup using Perlscript version $pcd_version (using perl ".GetPerlVersion().")\n"); +write_log("Using configuration $conffile\n"); + +#now do the dump + +#more than one db +if($cron_dbindex > -1) +{ + $dbname=$cron_db_array[$cron_dbindex]; + $dbpraefix=$cron_dbpraefix_array[$cron_dbindex]; + $command_beforedump=$cron_command_before_dump[$cron_dbindex]; + $command_afterdump=$cron_command_after_dump[$cron_dbindex]; + ExecuteCommand(1,$command_beforedump); + DoDump(); + ExecuteCommand(2,$command_afterdump); +} +else +{ + $db_anz=@cron_db_array; + for(my $ii = 0; $ii < $db_anz; $ii++) + { + if ($mp>0) { $mp=1; } # Part-Reset if using Multipart (for next database) + $dbname=$cron_db_array[$ii]; + $dbpraefix=$cron_dbpraefix_array[$ii]; + $command_beforedump=$cron_command_before_dump[$ii]; + $command_afterdump=$cron_command_after_dump[$ii]; + PrintOut("
      Starting to backup database `$dbname` (".($ii+1)."/$db_anz)."); + if ($dbpraefix ne "") { + PrintOut("Scanning for tables with prefix '$dbpraefix')"); + } + ExecuteCommand(1,$command_beforedump); + DoDump(); + ExecuteCommand(2,$command_afterdump); + } +} + +if($auto_delete>0) +{ + if($max_backup_files>0) + { + PrintOut("
      Starting autodelete function:
      Keep the latest $max_backup_files backup files for each database and delete older ones."); + find(\&AutoDeleteCount, $backup_path); + DoAutoDeleteCount(); + DeleteFiles (\@trash_files); + } +} +closeScript(); +if ($html_output==0) { print "\nEnd of Cronscript\n"; } + +############################################## +# Subroutinen # +############################################## +sub DoDump { + undef(%db_tables); + ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat, $Jahr, $Wochentag, $Jahrestag, $Sommerzeit) = localtime(time); + $Jahr+=1900;$Monat+=1;$Jahrestag+=1; + my $CTIME_String = localtime(time); + my $ret=0; + $time_stamp=$Jahr."_".sprintf("%02d",$Monat)."_".sprintf("%02d",$Monatstag)."_".sprintf("%02d",$Stunden)."_".sprintf("%02d",$Minuten); + $starttime= sprintf("%02d",$Monatstag).".".sprintf("%02d",$Monat).".".$Jahr." ".sprintf("%02d",$Stunden).":".sprintf("%02d",$Minuten); + $fieldlist=""; + # Verbindung mit MySQL herstellen, $dbh ist das Database Handle + if (trim($dbsocket) eq "") + { + $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost:$dbport","$dbuser","$dbpass") || die "Database connection not made: $DBI::errstr"; + } + else + { + $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost:$dbport;mysql_socket=$dbsocket","$dbuser","$dbpass") || die "Database connection not made: $DBI::errstr"; + } + # herausfinden welche Mysql-Version verwendet wird + $sth = $dbh->prepare("SELECT VERSION()"); + $sth->execute; + my @mysql_version=$sth->fetchrow; + my @v=split(/\./,$mysql_version[0]); + + if($v[0]>=5 || ($v[0]>=4 && $v[1]>=1) ) + { + #mysql Version >= 4.1 + $sth = $dbh->prepare("SET NAMES '".$character_set."'"); + $sth->execute; + # get standard encoding of MySQl-Server + $sth = $dbh->prepare("SHOW VARIABLES LIKE 'character_set_connection'"); + $sth->execute; + @ar=$sth->fetchrow; + $character_set=$ar[1]; + } + else + { + # mysql Version < 4.1 -> no SET NAMES available + # get standard encoding of MySQl-Server + $sth = $dbh->prepare("SHOW VARIABLES LIKE 'character_set'"); + $sth->execute; + @ar=$sth->fetchrow; + if (defined($ar[1])) { $character_set=$ar[1]; } + } + PrintOut("Characterset of connection and backup file set to ".$character_set."."); + + #Statuszeile erstellen + my $t=0; + my $r=0; + my $st_e="\n"; + undef(@tables); + undef(@tablerecords); + my $value=0; + my $engine=''; + my %db_tables_views; + my $query="SHOW TABLE STATUS FROM `$dbname`"; + if ($dbpraefix ne "") + { + $query.=" LIKE '$dbpraefix%'"; + PrintOut("Searching for tables inside database `$dbname` with prefix '$dbpraefix'."); + } + else + { + PrintOut("Searching for tables inside database `$dbname`."); + } + $sth = $dbh->prepare($query); + $sth->execute || err_trap("Error executing: ".$query." ! MySQL-Error: ".$DBI::errstr); + while ( $value=$sth->fetchrow_hashref()) + { + $value->{skip_data}=0; #defaut -> backup data of table + # decide if we need to skip the data while dumping (VIEWs and MEMORY) + + # check for old MySQL3-Syntax Type=xxx + if (defined $value->{Type}) + { + # port old index type to index engine, so we can use the index Engine in the rest of the script + $value->{Engine}=$value->{Type}; + $engine=uc($value->{Type}); + if ($engine eq "MEMORY") + { + $value->{skip_data}=1; + } + } + + # check for >MySQL3 Engine=xxx + if (defined $value->{Engine}) + { + $engine=uc($value->{Engine}); + if ($engine eq "MEMORY") + { + $value->{skip_data}=1; + } + } + + # check for Views - if it is a view the comment starts with "VIEW" + if (defined $value->{Comment} && uc(substr($value->{Comment},0,4)) eq 'VIEW') + { + $value->{skip_data}=1; + $value->{Engine}='VIEW'; + $value->{Update_time}=''; + $db_tables_views{$value->{Name}}=$value; + } + else + { + $db_tables{$value->{Name}}=$value; + } + # cast indexes to int, cause they are used for builing the statusline + $value->{Rows}+=0; + $value->{Data_length}+=0; + $value->{Index_length}+=0; + + } + $sth->finish; + + @tablenames=sort keys(%db_tables); + # add VIEW at the end as they need all tables to be created before + @tablenames = (@tablenames,sort keys(%db_tables_views)); + %db_tables = (%db_tables,%db_tables_views); + + $tablename=''; + if (@tablenames<1) + { + PrintOut("There are no tables inside database ".$dbname."! It doesn't make sense to backup an empty database. Skipping this one."); + return; + } + if($optimize_tables_beforedump==1) + { + optimise_tables(); + } + + $st_e.="-- TABLE-INFO\n"; + foreach $tablename (@tablenames) + { + my $dump_table=1; + if ($dbpraefix ne "") + { + if (substr($tablename,0,length($dbpraefix)) ne $dbpraefix) + { + # exclude table from backup because it doesn't fit to praefix + $dump_table=0; + } + } + + if ($dump_table==1) + { + + #www.betanet-web.ch - 30.04.2019 + #Erweitert mit SQL Abfrage für Ausgabe Anzahl der Einträge in der Tabelle (analog PHP) + $sql_create = "SELECT COUNT(*) FROM `$tablename`"; + $sth = $dbh->prepare($sql_create); + if (!$sth) + { + err_trap("Fatal error sending Query '".$sql_create."'! MySQL-Error: ".$DBI::errstr); + } + + $sth->execute || err_trap("Couldn't execute ".$sql_create); + $rct = $sth->fetchrow; + $sth->finish; + + $r+=$rct; + #Ende der Erweiterung + + push(@tables,$db_tables{$tablename}{Name}); # add tablename to backuped tables + $t++; + if (!defined $db_tables{$tablename}{Update_time}) + { + $db_tables{$tablename}{Update_time}=0; + } + + $st_e.=$mysql_commentstring."TABLE\|$db_tables{$tablename}{Name}\|$db_tables{$tablename}{Rows}\|".($db_tables{$tablename}{Data_length}+$db_tables{$tablename}{Index_length})."\|$db_tables{$tablename}{Update_time}|$db_tables{$tablename}{Engine}\n"; + } + } + $st_e.="-- EOF TABLE-INFO"; + + PrintOut("Found ".(@tables)." tables with $r records."); + + #AUFBAU der Statuszeile: + # -- Status:tabellenzahl:datensaetze:Multipart:Datenbankname:script:scriptversion:Kommentar:MySQLVersion:Backupflags:SQLBefore:SQLAfter:Charset:EXTINFO + # Aufbau Backupflags (1 Zeichen pro Flag, 0 oder 1, 2=unbekannt) + # (complete inserts)(extended inserts)(ignore inserts)(delayed inserts)(downgrade)(lock tables)(optimize tables) + # + my $version=0; + $version=$mysql_version[0]; + while ($version =~ s/:/--/) {} + while ($my_comment =~ s/:/--/) {} + $status_start=$mysql_commentstring."Status:$t:$r:"; + my $flags="1$optimize_tables_beforedump"; + $status_end=":$dbname:perl:$pcd_version:$my_comment:$version:$flags"; + $status_end.=":$command_beforedump:$command_afterdump:$character_set:EXTINFO$st_e\n".$mysql_commentstring."Dump created on $CTIME_String by PERL Cron-Script\n".$mysql_commentstring."Dump by MyOOS Dumper (https://www.oos-shop.de/)\n\n"; + + + if($mp>0) + { + $sql_text=$status_start."MP_$mp".$status_end; + } + else + { + $sql_text=$status_start.$status_end; + } + NewFilename(); + + $totalrecords=0; + foreach $tablename (@tables) + { + # first get CREATE TABLE Statement + if($dbpraefix eq "" || ($dbpraefix ne "" && substr($tablename,0,length($dbpraefix)) eq $dbpraefix)) + { + PrintOut("Dumping table `$tablename` (Type ".$db_tables{$tablename}{Engine}."):"); + $a="\n\n$mysql_commentstring\n$mysql_commentstring"."Table structure for table `$tablename`\n$mysql_commentstring\n"; + if ($db_tables{$tablename}{Engine} ne 'VIEW' ) { + $a.="DROP TABLE IF EXISTS `$tablename`;\n"; + } else { + $a.="DROP VIEW IF EXISTS `$tablename`;\n"; + } + $sql_text.=$a; + $sql_create="SHOW CREATE TABLE `$tablename`"; + $sth = $dbh->prepare($sql_create); + if (!$sth) + { + err_trap("Fatal error sending Query '".$sql_create."'! MySQL-Error: ".$DBI::errstr); + } + + $sth->execute || err_trap("Couldn't execute ".$sql_create); + @ergebnis=$sth->fetchrow; + $sth->finish; + $a=$ergebnis[1].";\n"; + if (length($a)<10) + { + err_trap("Fatal error! Couldn't read CREATE-Statement of table `$tabelle`! This backup might be incomplete! Check your database for errors."."' MySQL-Error: ".$DBI::errstr,1); + $skip=1; + } + else + { + $sql_text.=$a; + } + + if ($db_tables{$tablename}{skip_data} == 0) + { + $sql_text.="\n$mysql_commentstring\n$mysql_commentstring"."Dumping data for table `$tablename`\n$mysql_commentstring\n"; + $sql_text.="/*!40000 ALTER TABLE `$tablename` DISABLE KEYS */;"; + + WriteToFile($sql_text,0); + $sql_text=""; + $punktzaehler=0; + + # build fieldlist + $fieldlist="("; + $sql_create="SHOW FIELDS FROM `$tablename`"; + $sth = $dbh->prepare($sql_create); + if (!$sth) + { + err_trap("Fatal error sending Query '".$sql_create."'! MySQL-Error: ".$DBI::errstr); + } + + $sth->execute || err_trap("Couldn't execute ".$sql_create); + while ( @ar=$sth->fetchrow) { + $fieldlist.="`".$ar[0]."`,"; + } + $sth->finish; + + # remove trailing ',' and add ')' + $fieldlist=substr($fieldlist,0,length($fieldlist)-1).")"; + + # how many rows + + #www.betanet-web.ch - 30.04.2019 + #Erweitert mit SQL Abfrage für Ausgabe Anzahl der Einträge in der Tabelle (analog PHP) + $sql_create = "SELECT COUNT(*) FROM `$tablename`"; + $sth = $dbh->prepare($sql_create); + if (!$sth) + { + err_trap("Fatal error sending Query '".$sql_create."'! MySQL-Error: ".$DBI::errstr); + } + + $sth->execute || err_trap("Couldn't execute ".$sql_create); + $rct = $sth->fetchrow; + $sth->finish; + + #Ende der Erweiterung + + + for (my $ttt=0;$ttt<$rct;$ttt+=$perlspeed) + { + # default beginning for INSERT-String + $insert = "INSERT INTO `$tablename` $fieldlist VALUES ("; + $first_insert=0; + + # get rows (parts) + $sql_daten="SELECT * FROM `$tablename` LIMIT ".$ttt.",".$perlspeed.";"; + $sth = $dbh->prepare($sql_daten); + if (!$sth) + { + err_trap("Fatal error sending Query '".$sql_create."'! MySQL-Error: ".$DBI::errstr); + } + $sth->execute || err_trap("Couldn't execute \"".$sql_daten."\" - MySQL-Error: ".$DBI::errstr); + while ( @ar=$sth->fetchrow) + { + #Start the insert + if($first_insert==0) + { + $a="\n$insert"; + } + else + { + $a="\n("; + } + + # quote all values + foreach $inhalt(@ar) { $a.= $dbh->quote($inhalt).","; } + + # remove trailing ',' and add end-sql + $a=substr($a,0, length($a)-1).");"; + $sql_text.= $a; + if($memory_limit>0 && length($sql_text)>$memory_limit) + { + WriteToFile($sql_text); + $sql_text=""; + if($mp>0 && $filesize>$multipart_groesse) {NewFilename();} + } + } + $sth->finish; + } + $sql_text.="\n/*!40000 ALTER TABLE `$tablename` ENABLE KEYS */;\n"; + } + + # write sql commands to file + WriteToFile($sql_text); + $sql_text=""; + + if ($db_tables{$tablename}{skip_data} == 0) + { + PrintOut("\n
      $db_tables{$tablename}{Rows} inserted records (size of backupfile: ".byte_output($filesize).")"); + $totalrecords+=$db_tables{$tablename}{Rows}; + } + else + { + PrintOut("\n
      Dumping structure of `$tablename` (Type ".$db_tables{$tablename}{Engine}." ) (size of backupfile: ".byte_output($filesize).")"); + } + + if($mp>0 && $filesize>$multipart_groesse) {NewFilename();} + } + } + # end + WriteToFile("\nSET FOREIGN_KEY_CHECKS=1;\n"); + WriteToFile($mysql_commentstring."EOB\n"); + PrintOut("\n
      Finished backup of database `$dbname`.\n"); + write_log("Finished backup of database `$dbname`.\n"); + + # sent via email + if($cronmail==1) { + PrintOut("Sending E-Mail ..."); + $ret=send_mail(); + if ($ret) + { + write_log("Recipient/s: $cronmailto $cronmailto_cc\n"); + PrintOut("Recipient/s: $cronmailto $cronmailto_cc\n"); + } + } + + # sent to ftp-server + send_ftp(); +} + +#print error message and optional exit +sub err_trap { + my $error_message = shift(@_); + + #continue instead of exit + my $continue = shift(@_); + + #don't write to logfile, if we did not read config before + my $nolog = shift(@_); + + PrintOut("Perl Cronscript ERROR: $error_message
      \n"); + write_log("Perl Cronscript ERROR: $error_message
      \n") if !defined $nolog; + if (!defined $continue || $continue ==0) + { + PrintOut("Stopping script because of this fatal error!
      \n"); + write_log("Stopping script because of this fatal error!
      \n") if !defined $nolog; + exit(1); + } +} + +sub PrintHeader { + my $cgi = new CGI; + my $perlversion = GetPerlVersion(); + + if ($html_output==1) + { + print $cgi->header(-type => 'text/html; charset=utf-8', -cache_control => 'no-cache, no-store, must-revalidate'); + print "\n"; + print "\n\nMyOOS [Dumper] - Perl CronDump [Version $pcd_version (using perl $perlversion)]\n"; + print "\n"; + print "\n

      MyOOS [Dumper] - Perl CronDump [Version $pcd_version (using perl $perlversion)]

      \n"; + } + else + { + #small output for external cronjobs, which expect a small returnvalue + print "MyOOS [Dumper] - Perl CronDump [Version $pcd_version] started successfully (using perl $perlversion)\n"; + } +} + +sub PrintOut { + $print_out = shift(@_); + + if (defined $print_out && length(trim($print_out))>0) + { + if($complete_log==1) + { + my $logsize=0; + ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat, $Jahr, $Wochentag, $Jahrestag, $Sommerzeit) = localtime(time); + $Jahr+=1900; $Monat+=1;$Jahrestag+=1; + $dt=sprintf("%02d",$Monatstag).".".sprintf("%02d",$Monat).".".sprintf("%02d",$Jahr)." ".sprintf("%02d",$Stunden).":".sprintf("%02d",$Minuten).":".sprintf("%02d",$Sekunden); + if (-e $completelogdatei) + { + $logsize=(stat($completelogdatei))[7]; + unlink($completelogdatei) if($logsize + length($print_out)>$log_maxsize && $log_maxsize>0); + } + my $output=$print_out; + #$output =~ s/<(.*?)>//gi; + $output =~ s/\n//gi; + $output =~ s/\r//gi; + $output =~ s/
      //gi; + $output=trim($output); + + if ( ($logcompression==0) || ($mod_gz==0)) + { + open(DATEI,">>$completelogdatei") || err_trap('can\'t open myoosdump_perl.complete.log ('.$completelogdatei.').'); + print DATEI "$dt $output\n" || err_trap('can\'t write to myoosdump_perl.complete.log ('.$completelogdatei.').'); + close(DATEI)|| err_trap('can\'t close myoosdump_perl.complete.log ('.$completelogdatei.').'); + chmod(0777,$completelogdatei); + } + else + { + $gz = gzopen($completelogdatei, "ab") || err_trap("Cannot open myoosdump_perl.complete.log.gz. "); + $gz->gzwrite("$dt $output\n") || err_trap("Error writing myoosdump_perl.complete.log.gz. "); + $gz->gzclose ; + chmod(0777,$completelogdatei); + } + } + if($cron_printout==1) + { + #save current autoflush-setting + local ($oldbar) = $|; + + #save current output filehandle and change it to STDOUT + $cfh = select (STDOUT); + + #set autoflush on + $| = 1; + + #remove html-tags + if($html_output==0) + { + $print_out =~ s/<(.*?)>//gi; + } + + print $print_out; + + #TODO don't print
      with the last printout (-> wrong html-syntax) + if ($html_output==1){ print "
      \n"; } else { print "\n"; }; + + #restore old autoflush-setting + $| = $oldbar; + + #set default output back to old filehandle + select ($cfh); + } + } +} + +sub write_log { + $msg = shift(@_); + ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat, $Jahr, $Wochentag, $Jahrestag, $Sommerzeit) = localtime(time); + $Jahr+=1900; $Monat+=1;$Jahrestag+=1; + #$dt=sprintf("%02d",$Monatstag).".".sprintf("%02d",$Monat).".".sprintf("%02d",$Jahr)." ".sprintf("%02d",$Stunden).":".sprintf("%02d",$Minuten).":".sprintf("%02d",$Sekunden); + $dt=sprintf("%02d.%02d.%02d %02d:%02d:%02d",$Monatstag,$Monat,$Jahr,$Stunden,$Minuten,$Sekunden); + + my $logsize=0; + if (-e $logdatei) + { + $logsize=(stat($logdatei))[7]; + unlink($logdatei) if($logsize+200>$log_maxsize && $log_maxsize>0); + } + + if ( ($logcompression==0) || ($mod_gz==0)) + { + open(DATEI,">>$logdatei") || err_trap("Can't open file ($logdatei)."); + print DATEI "$dt $msg" || err_trap("Can't write to file ($logdatei)."); + close(DATEI)|| err_trap("can't close file ($logdatei)."); + chmod(0777,$logdatei); + } + else + { + $gz = gzopen($logdatei, "ab") || err_trap("Can't open $logdatei."); + $gz->gzwrite("$dt $msg") || err_trap("Can't write to $logdatei. "); + $gz->gzclose ; + chmod(0777,$logdatei); + } +} + +sub send_ftp { + #save files to ftp-server + my $ret=0; + my $x=0; + for(my $i = 0; $i <3; $i++) + { + if ($ftp_transfer[$i]==1) + { + if ($ftp_timeout[$i]<1) { $ftp_timeout[$i]=30; }; + if (${ftp_useSSL[$i]}==1 && $mod_ftpssl==1) + { #use ftp-ssl + $ftp = Net::FTPSSL->new($ftp_server[$i], Encryption => Net::FTPSSL->EXP_CRYPT, Port => $ftp_port[$i], DataProtLevel =>Net::FTPSSL->DATA_PROT_CLEAR, Timeout => $ftp_timeout[$i], Debug => 0) or err_trap( "FTP-SSL-ERROR: Can't connect: $@\n",1); + } + else + { #use 'plain' ftp + $ftp = Net::FTP->new($ftp_server[$i], Port => $ftp_port[$i], Timeout => $ftp_timeout[$i], Debug => 1,Passive => $ftp_mode[$i]) or err_trap( "FTP-ERROR: Can't connect: $@\n",1); + } + $ftp->login($ftp_user[$i], $ftp_pass[$i]) or err_trap("FTP-ERROR: Couldn't login\n",1); + $ftp->binary(); + $ftp->cwd($ftp_dir[$i]) or err_trap("FTP-ERROR: Couldn't change directory: ".$ftp_dir[$i],1); + + if($mp==0) + { + PrintOut("FTP: transferring `$backupfile`"); + $ret=$ftp->put($sql_file); + if (!$ret) + { + err_trap("FTP-Error: Couldn't put $backupfile to ".$ftp_server[$i]." into dir ".$ftp_dir[$i]."\n",1); + } + else + { + write_log("FTP: transferred `$backupfile` to $ftp_server[$i] into dir $ftp_dir[$i] successfully\n"); + PrintOut(" to $ftp_server[$i] into dir $ftp_dir[$i] was successful.\n"); + } + } + else + { + $dateistamm=substr($backupfile,0,index($backupfile,"part_"))."part_"; + $dateiendung=($compression==1)?".sql.gz":".sql"; + $mpdatei=""; + for ($x=1;$x<$mp;$x++) + { + $mpdatei=$dateistamm.$x.$dateiendung; + PrintOut("FTP: transferring multipart $mpdatei"); + + $ret=$ftp->put($backup_path.$mpdatei); + if (!$ret) + { + err_trap("Couldn't put $backup_path.$mpdatei to ".$ftp_server[$i]." into dir ".$ftp_dir[$i]."\n",1); + } + else + { + #write_log("FTP: transferring of `$mpdatei` to ".$ftp_server[$i]." finished successfully.\n"); + #PrintOut("FTP: transferring of `$mpdatei` to $ftp_server[$i] finished successfully."); + write_log("FTP: transferred multipart '$mpdatei' to $ftp_server[$i] into dir $ftp_dir[$i] successfully\n"); + PrintOut(" to $ftp_server[$i] into dir $ftp_dir[$i] was successful.\n"); + } + } + } + } + } +} + +sub send_mail { + #sent email w/o files + $BodyNormal='Find attached a backup of your database `'.$dbname.'`.'; + $BodyMultipart="A multipart backup has been made.
      You will receive one or more emails with the backup-files attached.
      The database `".$dbname."` has been backuped.
      The following files have been created:"; + $BodyToBig="The backup is bigger than the allowed max-limit of ".byte_output($email_maxsize)." and has not been attached.
      Backup of database ".$dbname."

      The following files have been created:"; + $BodyNoAttach="The backup has not been attached.
      I saved your database `".$dbname."` to file
      "; + $BodyAttachOnly="Here is your backup."; + $Body=""; + $DoAttach=1; + my @mparray; + my $ret=0; + if($mp==0) + { #no multipart + if(($email_maxsize>0 && $filesize>$email_maxsize) || $cronmail_dump==0) + { + #attache files + if($cronmail_dump==0) + { #The backup has not been attached + $Body=$BodyNoAttach.$backupfile." (".byte_output($filesize).")"; + } + else + { #The backup is bigger than the allowed max-limit + $Body=$BodyToBig.$backupfile." (".byte_output($filesize).")"; + } + $DoAttach=0; + } + else + { #Find attached your backup + $Body=$BodyNormal." File ".$backupfile." (".byte_output($filesize).")"; + } + } + else + { #multipart + $Body=$BodyMultipart; + $dateistamm=substr($backupfile,0,index($backupfile,"part_"))."part_"; + $dateiendung=($compression==1)?".sql.gz":".sql"; + $mpdatei=""; + for ($i=1;$i<$mp;$i++) + { + $mpdatei=$dateistamm.$i.$dateiendung; + push(@mparray,"$mpdatei|$i"); + $filesize=(stat($backup_path.$mpdatei))[7]; + $Body.="\n
      $mpdatei (".(byte_output($filesize))." )"; + } + } + $Body.="\n\n

      Best regards,

      MyOOS [Dumper]
      If you have any questions, feel free and visit the support board at:
      https://foren.myoos.de/viewforum.php?f=40"; + + if ($cron_use_sendmail==1) + { + MIME::Lite->send("sendmail", $sendmail_call) || err_trap("Error setting sendmail call!",1); + } + else + { + MIME::Lite->send('smtp', $cron_smtp, Timeout=>60) || err_trap("Error setting smtp call !",1); + } + + $msg = MIME::Lite->new( + From => $cronmailfrom, + To => $cronmailto, + Cc => $cronmailto_cc, + Subject => "MOD (Perl) - Backup of DB ".$dbname, + Type => 'text/html; charset=iso-8859-1', + Data => "\n".$Body."\n" + ); + + if($DoAttach==1 && $mp==0) + { #attach files, no multipart + $msg->attach( + Type => "BINARY", + Path => "$sql_file", + Encoding => "base64", + Filename => "$backupfile" + ); + $ret=$msg->send; + if (!$ret) + { + err_trap("Error 1 sending mail with backup ".$backupfile."!",1); + } + else + { + PrintOut("E-Mail with backup ".$backupfile." sent successfully."); + write_log("E-Mail with backup ".$backupfile." sent successfully.\n"); + } + return $ret; + } + + if($DoAttach==1 && $mp>0 && $cronmail_dump>0) + { #attach files, multipart + foreach $datei(@mparray) + { + @str=split(/\|/,$datei); + $msg = MIME::Lite->new( + From => $cronmailfrom, + To => $cronmailto, + Cc => $cronmailto_cc, + Subject => "MOD (Perl) - Backup of DB $dbname File ".$str[1]." of ".@mparray , + Type => 'text/html; charset=iso-8859-1', + Data => "\n".$Body."\n" + ); + + $msg->attach( + Type => "BINARY", + Path => $backup_path.$str[0], + Encoding => "base64", + Filename => $str[0] + ); + $ret=$msg->send; + if (!$ret) + { + err_trap("Error 2 sending mail with backup ".$str[0]."!",1); + } + else + { + PrintOut("E-Mail with backup ".$str[0]." sent successfully."); + write_log("E-Mail with backup ".$str[0]." sent successfully.\n"); + } + } + return $ret; + } + + $ret=$msg->send; + if (!$ret) + { + err_trap("Error 3 sending E-Mail!
      ",1); + } + else + { + PrintOut("E-Mail sent successfully."); + write_log("E-Mail sent successfully.\n"); + } + return $ret; +} + +sub NewFilename { + $part=""; + if($mp>0) + { + $part="_part_$mp"; + $mp++; + } + if($compression==0) + { + $sql_file=$backup_path.$dbname."_".$time_stamp.$part.".sql"; + $backupfile=$dbname."_".$time_stamp.$part.".sql"; + } + else + { + $sql_file=$backup_path.$dbname."_".$time_stamp.$part.".sql.gz"; + $backupfile=$dbname."_".$time_stamp.$part.".sql.gz"; + } + if($mp==0) + { + PrintOut("\n
      Starting to dump data into file `$backupfile`"); + write_log("Dumping data into file `$backupfile` \n"); + } + if($mp==2) + { + PrintOut("\n
      Starting to dump data into multipart-file `$backupfile`"); + write_log("Start Perl Multipart-Dump with file `$backupfile` \n"); + } + if($mp>2) + { + PrintOut("\n
      Continuing Multipart-Dump with file `$backupfile`"); + write_log("Continuing Multipart-Dump with file `$backupfile` \n"); + } + if($mp>0) + { + $sql_text=$status_start."MP_".($mp-1).$status_end; + } + else + { + $sql_text=$status_start.$status_end; + } + $sql_text.="/*!40101 SET NAMES '".$character_set."' */;\n"; + $sql_text.="SET FOREIGN_KEY_CHECKS=0;\n"; + + WriteToFile($sql_text,1); + chmod(0777,$sql_file); + $sql_text=""; + $first_insert=0; + $punktzaehler=0; + push(@backupfiles_name,$sql_file); +} + +sub WriteToFile { + $inh=shift; + my $points=shift; + if (!defined($points)) { $points=2; } + + if(length($inh)>0) { + if($compression==0){ + open(DATEI,">>$sql_file"); + print DATEI $inh; + close(DATEI); + } else { + $gz = gzopen($sql_file, "ab") || err_trap("Cannot open ".$sql_file); + $gz->gzwrite($inh) || err_trap("Error writing ".$sql_file); + $gz->gzclose ; + } + if ($points>1) + { + print "."; + } + $filesize= (stat($sql_file))[7]; + $punktzaehler++; + if($punktzaehler>120) + { + if ($html_output==1) { print "
      "; } else { print "\n"; }; + $punktzaehler=0; + } + } +} + +sub AutoDeleteCount { + $fpath=$File::Find::name; + $fname=basename($fpath); + my @fileparts=split(/\./,"$fname"); + my $partcount=@fileparts; + if ($partcount>1) + { + my $end=$fileparts[(@fileparts-1)]; + # Read Statusline and extract info + my $line=''; + if ($end eq 'sql') + { + if (open(DATEI,"<$fpath")) + { + $line=; + close(DATEI); + } + else + { + print "
      Error: couldn\'t open file: ".$fpath; + } + } + if ($end eq 'gz') + { + $gz = gzopen("$fpath", "rb"); + if ($gz) + { + $gz->gzreadline($line); + $gz->gzclose; + } + else + { + print "
      Error: couldn\'t open file: ".$fpath; + } + } + if (length($line)>0 && (substr($line,0,10) eq "-- Status:" ||substr($line,0,11) eq "-- Status:")) + { + #statusline read + my @infos=split(/\:/,$line); + my $file_multipart=($infos[3])?$infos[3]:''; + $file_multipart=~ s/MP_/ /g; + $file_multipart=trim($file_multipart); + my $file_databasename=($infos[4])?$infos[4]:''; + if ($file_multipart eq "" || substr($file_multipart,0,1) eq "0") + { + #no multipartfile + push(@filearr,"$fname|$file_databasename"); + } + else + { + push(@filearr,"$fname|$file_databasename|$file_multipart"); + } + } + else + { + PrintOut("No Statusline in `$fname` found. Seems not to be a MyOOS [Dumper] file. Skipping file."); + } + } +} + +sub DoAutoDeleteCount { + my @str; + my @dbarray; + my $item; + my $item2; + my %dbanz; + my $anz=@filearr; + # sort filearr descending -> so the latest backups are at top + # multipartfiles sorting is also descending -> part3, part2, part1 + @filearr=sort{"$b" cmp "$a"}(@filearr); + @multipartfiles=(); + if ($anz>0) + { + foreach $item (@filearr) + { + @str=split(/\|/,$item); + # str[0]=filename, str[1]=databasename, str[2]=multipart number + + #init db-counter if this index doesn't exist yet + if (defined $str[1] && !defined $dbanz{$str[1]}) + { + $dbanz{$str[1]}=0; + @multipartfiles=(); + } + #PrintOut($max_backup_files.': '.$dbanz{$str[1]}.' -> '.$str[0].' - '.$str[1].' - '.$str[2]); + + #no multipart file -> update db counter + if(defined $str[1] && !defined $str[2]) + { + # handling for non multipart files + $dbanz{$str[1]}++; + # is the max number of backups for this database reached? + # if yes -> push the actual filename into trash_files + if($dbanz{$str[1]}>$max_backup_files) + { + push(@trash_files, $str[0]); + } + } + else + { + # keep multipartz filename + if(defined $str[1] && $dbanz{$str[1]}>=$max_backup_files) + { + push(@multipartfiles,$str[0]); + } + + # if it is a multipart file and it is part_1 -> update db counter + # multiparts with higher numbers already passed the loop, so we can use + # part1 to increase the db-counter + if(defined $str[2] && $str[2]==1) + { + $dbanz{$str[1]}++; + # now check if we have reached the limit + if($dbanz{$str[1]}>$max_backup_files) + { + foreach $item2 (@multipartfiles) + { + push(@trash_files, $item2); + } + } + # clear array for next multipart backup + @multipartfiles=(); + } + } + } + } +} + +sub DeleteFiles +{ + my $res=0; + if(@trash_files==0) + { + PrintOut("No file to delete."); + } + else + { + foreach $datei(@trash_files) + { + my $file_to_delete = $backup_path.$datei; + $res=unlink($file_to_delete); + if ($res) + { + PrintOut("Autodelete: old backup file ".$datei." deleted."); + write_log( "Autodelete: old backup file $datei deleted.\n" ) ; + } + else + { + err_trap("Autodelete: Error deleting old backup file ".$datei."!
      ",1); + } + } + undef(@trash_files); + } +} + +sub ExecuteCommand +{ + my $cmt = shift(@_); + my $command = shift(@_); + my (@cad, $errText, $succText, $cd2, $commandDump); + my $err=''; + $commandDump=$command; + if($cmt==1) + { #before dump + $errText="Error while executing Query before Dump"; + $succText="Executing Query before Dump was successful"; + } + else + { + $errText="Error while executing Query after Dump"; + $succText="executing Query after Dump was successful"; + } + + if(defined $commandDump && length($commandDump)>0) + { + if(substr($commandDump,0,7) ne "system:") + { + # prepare command + $commandDump = replaceQueryStringSimple($commandDump); + if (trim($dbsocket) eq "") + { + $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost:$dbport","$dbuser","$dbpass") || err_trap("Database connection not made: $DBI::errstr"); + } + else + { + $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost:$dbport;mysql_socket=$dbsocket","$dbuser","$dbpass") || die "Database connection not made: $DBI::errstr"; + } + + if(index($commandDump,";")>0) + { + # more than 1 query + @cad=split(/;/,$commandDump); + } + else + { + @cad=$commandDump; + } + + for($i=0;$i<@cad;$i++) + { + if($cad[$i] ne '') + { + $err=''; + # replace $$MOD$$ back to ';' + $cad[$i] =~ s/\$\$MOD\$\$/\;/g; + $sth = $dbh->prepare($cad[$i]); + $sth->execute or $err=$sth->errstr(); + if ($err ne '') + { + write_log("Executing Query '$cad[$i]' caused an error! MySQL returns: '$err'\n"); + PrintOut("Executing Query '$cad[$i]' caused an error! MySQL returns: '$err'\n"); + } + else + { + write_log("Successfully executed Query: $cad[$i]\n"); + PrintOut("Successfully executed Query: $cad[$i]\n"); + } + $sth->finish; + } + } + } + else + { + #Systembefehl + $commandDump=substr($commandDump,7); + system($commandDump); + PrintOut("

      $succText ($commandDump)

      "); + write_log("$succText ($commandDump)\n"); + } + } +} + +sub closeScript +{ + my ($Start, $Jetzt, $Totalzeit); + $Start = $^T; $Jetzt = (time); + $Totalzeit=$Jetzt - $Start; + ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat, $Jahr, $Wochentag, $Jahrestag, $Sommerzeit) = localtime(time); + $Jahr+=1900;$Monat+=1;$Jahrestag+=1; + $starttime=sprintf("%02d",$Monatstag).".".sprintf("%02d",$Monat).".".$Jahr." ".sprintf("%02d",$Stunden).":".sprintf("%02d",$Minuten).":".sprintf("%02d",$Sekunden); + PrintOut("
      Everythings is done: closing script $starttime"); + PrintOut("total time used: $Totalzeit sec."); + PrintOut("#EOS (End of script)
      "); + # Datenbankverbindung schliessen + $sth->finish() if (defined $sth); + ($dbh->disconnect() || warn $dbh->errstr) if (defined $dbh); +} + +sub trim +{ + my $string = shift; + if (defined($string)) + { + $string =~ s/^\s+//; + $string =~ s/\s+$//; + } + else + { + $string=''; + } + return $string; +} + +sub byte_output +{ + my $bytes= shift; + my $suffix="Bytes"; + if ($bytes>=1024) { $suffix="KB"; $bytes=sprintf("%.2f",($bytes/1024));}; + if ($bytes>=1024) { $suffix="MB"; $bytes=sprintf("%.2f",($bytes/1024));}; + my $ret=sprintf "%.2f",$bytes; + $ret.=' '.$suffix; + return $ret; +} + +sub optimise_tables +{ + my $engine=''; + my $ret=0; + $opttbl=0; + PrintOut("Optimizing tables:"); + foreach $tablename (@tablenames) + { + #optimize table if engine supports optimization + $engine=uc($db_tables{$tablename}{Engine}); + if ( $engine eq "MYISAM" or $engine eq "BDB" or $engine eq "INNODB") + { + my $sth_to = $dbh->prepare("OPTIMIZE TABLE `$tablename`"); + $ret=$sth_to->execute; + if ($ret) + { + PrintOut("Table ".($opttbl+1)." `$tablename` optimized successfully."); + $opttbl++; + } + else + { + err_trap("  Error optimizing table `$tablename`",1); + } + } + } + PrintOut("$opttbl tables have been optimized
      ") if($opttbl>0) ; +} + +# replace in querystring all ';' in VALUES with '$$MOD$$' +sub replaceQueryStringSimple{ + my $string = shift(@_); + + if ($string =~ m#(.*)\'(.*)\;(.*)\'(.*)#){ + # if found search for more ';' + return replaceQueryStringSimple($1.'\''.$2.'$$MOD$$'.$3.'\''.$4);; + }else{ + return $string; + } +} + diff --git a/msd/mod_cron/perltest.pl b/msd/mod_cron/perltest.pl new file mode 100644 index 0000000..f3d07fc --- /dev/null +++ b/msd/mod_cron/perltest.pl @@ -0,0 +1,162 @@ +#!/usr/bin/perl -w +# +# MyOOS [Dumper] +# https://www.oos-shop.de/ +# +# Copyright (c) 2013 - 2022 by the MyOOS Development Team. +# ---------------------------------------------------------------------- +# Based on: +# +# MySqlDumper +# http://www.mysqldumper.de +# +# Copyright (C)2004-2011 Daniel Schlichtholz (admin@mysqldumper.de) +# ---------------------------------------------------------------------- +# Released under the GNU General Public License +# ---------------------------------------------------------------------- +# +# This file is part of MySQLDumper released under the GNU/GPL 2 license +# http://www.mysqldumper.net +# @package MySQLDumper +# @version Rev: 1351 +# @author Author: jtietz + + +use strict; +use Socket; +use Config; +use CGI::Carp qw(warningsToBrowser fatalsToBrowser); +use CGI; +warningsToBrowser(1); # dies ist ganz wichtig! + +my $eval_in_died; +my $mod_dbi=0; +my $mod_ff=0; +my $mod_fb=0; +my $mod_gz=0; +my $mod_ftp=0; +my $mod_mime=0; +my $mod_ftpssl=0; +my $dbi_driver; +my $dbi_mysql_exists=0; +my $get_options=0; +my $ok=''; +my $err=''; +my $zlib_version='unknown'; + +my $cgi = CGI->new(); +print $cgi->header(-type => 'text/html; charset=utf-8', -cache_control => 'no-cache, no-store, must-revalidate'); +print "\n"; +print "MyOOS [Dumper] Perl modul test\n"; +print ''; +print "

      Testing needed Perl-Moduls in order to run the Perl script crondump.pl

      \n"; +print "

      Necessary Modules for crondump.pl

      "; +print "testing DBI ...\n"; +eval { $eval_in_died = 1; require DBI; }; + if(!$@){ + $mod_dbi = 1; + import DBI; + } +if($mod_dbi!=1){ + print $err."
      Couldn't find DBI!
      crondump.pl can't establish a connection to the MySQL database!
      \n"; +} else { + print $ok."Found modul DBI. OK.
      \n"; + my @available_drivers = DBI->available_drivers('quiet'); + foreach $dbi_driver (@available_drivers) + { + print "
      Found modul DBI::$dbi_driver\n"; + if ( $dbi_driver eq 'mysql' ) { $dbi_mysql_exists=1; } ; + } + if ($dbi_mysql_exists !=1 ) { print $err."
      Critical error: modul DBI::mysql not found! crondump.pl can't establish a connection to the MySQL-Database if this modul isn't installed! Please install DBI::mysql!
      "; } + else { print "
      ".$ok."Found modul DBI::mysql. OK. crondump.pl can connect to MySQL-Database.
      "; } +} + +print "

      testing File::Find ...\n"; +eval { $eval_in_died = 1; require File::Find; }; + if(!$@){ + $mod_ff = 1; + import File::Find; + } +if($mod_ff!=1){ + print $err."Critical error: modul File::Find not found! Please install it

      \n"; +} else { + print $ok."Found modul File::Find. OK.

      \n"; +} + +print "testing File::Basename ...\n"; +eval { $eval_in_died = 1; require File::Basename; }; + if(!$@){ + $mod_fb = 1; + import File::Basename; + } +if($mod_fb!=1){ + print $err."Critical error: modul File::Basename not found! Please install it!

      \n"; +} else { + print $ok."Found modul File::Basename. OK.
      \n"; +} + +print "testing Getop...\n"; +eval { $eval_in_died = 1; require Getopt::Long; }; + if(!$@){ + $get_options = 1; + import Getopt::Long; + } +if($get_options!=1){ + print $err."Modul Getopt not found! You should install it if you want to set configfile via shell.

      \n"; +} else { + print $ok."Found modul Getopt. OK. crondump.pl can read configfile-parameter from shell.
      \n"; +} + +print "

      Configurable functions for crondump.pl (these moduls are only needed when explained option is turned on):

      "; + +print "testing Compress::Zlib (needed for dumping data into a crompessed *.gz-file)...
      \n"; +eval { $eval_in_died = 1; require Compress::Zlib; }; + if(!$@){ + $zlib_version=qq[ver $Compress::Zlib::VERSION]; + $mod_gz = 1; + import Compress::Zlib; + } +if($mod_gz!=1){ + print "Error: modul Compress::Zlib not found! crondump.pl can't write compressed files. Falling back to uncrompressed files (files are 10 times bigger).
      \n"; +} else { + print $ok."Found modul Compress::Zlib ".$zlib_version.". OK. crondump.pl can write compressed backups.
      \n"; +} + +print "
      testing Net::FTP (needed if you want to transfer backups to another server)...
      \n"; +eval { $eval_in_died = 1; require Net::FTP; }; + if(!$@){ + $mod_ftp = 1; + import Net::FTP; + } +if($mod_ftp!=1){ + print $err."Error: modul Net::FTP not found! crondump.pl can't transfer data via FTP.
      \n"; +} else { + print $ok."Found modul Net::FTP. OK - crondump.pl can send backups via FTP.
      \n"; +} + +print "
      testing Net::FTPSSL (needed if you want to transfer backups to another server with ssl encryption)...
      \n"; +eval { $eval_in_died = 1; require Net::FTPSSL; }; + if(!$@){ + $mod_ftpssl = 1; + import Net::FTPSSL; + } +if($mod_ftpssl !=1){ +print $err."Error: modul Net::FTPSSL not found! crondump.pl can't transfer data via FTP with ssl encryption.
      \n"; +} else { + print $ok."Found modul Net::FTPSSL. OK - crondump.pl can send backups via FTP with ssl encryption.
      \n"; +} + + +print "
      testing MIME::Lite (needed if you want to send backups via email)...
      \n"; +eval { $eval_in_died = 1; require MIME::Lite; }; + if(!$@){ + $mod_mime = 1; + import MIME::Lite; + } +if($mod_mime!=1){ + print $err."Error: modul MIME::Lite not found!
      crondump.pl can't send emails! Option will automatically be deactivated. Install Mime::Lite in order to send emails!\n"; +} else { + print $ok."Found modul MIME::Lite. OK. crondump.pl can send emails.
      \n"; +} + +print "



      \n"; \ No newline at end of file diff --git a/msd/mod_cron/simpletest.pl b/msd/mod_cron/simpletest.pl new file mode 100644 index 0000000..73733f1 --- /dev/null +++ b/msd/mod_cron/simpletest.pl @@ -0,0 +1,52 @@ +#!/usr/bin/perl -w +# +# ERROR / FEHLER !! +# +++++++++++++++++ +# If you can read this line Perl is not executed. +# Ask your hoster how to activate Perl. +# +# Wenn Du diese Zeile hier siehst, dann wird Perl nicht ausgefuehrt. +# Frage Deinen Hoster, ob und wie Du Perl aktivieren kannst. +# +# Sample Apache-Config: +# +# Options ExecCGI +# AddHandler cgi-script .cgi .pl +# +# +# MyOOS [Dumper] +# https://www.oos-shop.de/ +# +# Copyright (c) 2013 - 2022 by the MyOOS Development Team. +# ---------------------------------------------------------------------- +# Based on: +# +# MySqlDumper +# http://www.mysqldumper.de +# +# Copyright (C)2004-2011 Daniel Schlichtholz (admin@mysqldumper.de) +# ---------------------------------------------------------------------- +# Released under the GNU General Public License +# ---------------------------------------------------------------------- +# +# +# This file is part of MySQLDumper released under the GNU/GPL 2 license +# http://www.mysqldumper.net +# @package MySQLDumper +# @version Rev: 1351 +# @author Author: jtietz + + + +use strict; +use CGI::Carp qw(warningsToBrowser fatalsToBrowser); +warningsToBrowser(1); + +print "Content-type: text/html\n\n"; +print "\n"; +print "MyOOS [Dumper] - simple Perl test\n"; +print ''; +print "\n\n"; +print "

      If you see this perl works fine on your system !

      "; +print "Wenn Du das siehst, funktioniert Perl auf Deinem System !

      "; +print "\n"; \ No newline at end of file diff --git a/msd/refresh_dblist.php b/msd/refresh_dblist.php new file mode 100644 index 0000000..32d469c --- /dev/null +++ b/msd/refresh_dblist.php @@ -0,0 +1,43 @@ + $val) { + if (is_array($val)) { + foreach ($val as $key2 => $val2) { + $restore[$key][$key2] = $val2; + } + } + } + include './'.$config['files']['parameter']; + $restore['max_zeit'] = intval($config['max_execution_time'] * $config['time_buffer']); + if (0 == $restore['max_zeit']) { + $restore['max_zeit'] = 20; + } + $restore['startzeit'] = time(); + $restore['xtime'] = (isset($_POST['xtime'])) ? $_POST['xtime'] : time(); + $restore['fileEOF'] = false; // Ende des Files erreicht? + $restore['actual_table'] = (!empty($_POST['actual_table'])) ? $_POST['actual_table'] : 'unbekannt'; + $restore['offset'] = (!empty($_POST['offset'])) ? $_POST['offset'] : 0; + $restore['aufruf'] = (!empty($_POST['aufruf'])) ? $_POST['aufruf'] : 0; + $restore['table_ready'] = (!empty($_POST['table_ready'])) ? $_POST['table_ready'] : 0; + $restore['part'] = (isset($_POST['part'])) ? $_POST['part'] : 0; + $restore['do_it'] = (isset($_POST['do_it'])) ? $_POST['do_it'] : false; + $restore['errors'] = (isset($_POST['err'])) ? $_POST['err'] : 0; + $restore['notices'] = (isset($_POST['notices'])) ? $_POST['notices'] : 0; + $restore['anzahl_eintraege'] = (isset($_POST['anzahl_eintraege'])) ? $_POST['anzahl_eintraege'] : 0; + $restore['anzahl_tabellen'] = (isset($_POST['anzahl_tabellen'])) ? $_POST['anzahl_tabellen'] : 0; + $restore['filename'] = (isset($_POST['filename'])) ? urldecode($_POST['filename']) : ''; + if (isset($_GET['filename'])) { + $restore['filename'] = urldecode($_GET['filename']); + } + $restore['actual_fieldcount'] = (isset($_POST['actual_fieldcount'])) ? $_POST['actual_fieldcount'] : 0; + $restore['eintraege_ready'] = (isset($_POST['eintraege_ready'])) ? $_POST['eintraege_ready'] : 0; + $restore['anzahl_zeilen'] = (isset($_POST['anzahl_zeilen'])) ? $_POST['anzahl_zeilen'] : $config['minspeed']; + $restore['summe_eintraege'] = (isset($_POST['summe_eintraege'])) ? $_POST['summe_eintraege'] : 0; + $restore['erweiterte_inserts'] = (isset($_POST['erweiterte_inserts'])) ? $_POST['erweiterte_inserts'] : 0; + $restore['flag'] = (isset($_POST['flag'])) ? $_POST['flag'] : -1; + $restore['EOB'] = false; + $restore['dump_encoding'] = (isset($_POST['dump_encoding'])) ? $_POST['dump_encoding'] : 'utf8'; + if (isset($_GET['dump_encoding'])) { + $restore['dump_encoding'] = $_GET['dump_encoding']; + } + $restore['compressed'] = ('gz' == substr(strtolower($restore['filename']), -2)) ? 1 : 0; + // wurden nur bestimmte Tabellen ausgwaehlt? + if (!isset($databases['db_actual_tableselected'])) { + $databases['db_actual_tableselected'] = ''; + } + if ('' != $databases['db_actual_tableselected']) { + $restore['tables_to_restore'] = explode('|', $databases['db_actual_tableselected']); + } else { + $restore['tables_to_restore'] = false; + } + $_SESSION['config'] = $config; + $_SESSION['databases'] = $databases; +} else { + $config = $_SESSION['config']; + $databases = $_SESSION['databases']; + $restore = $_SESSION['restore']; + $restore['startzeit'] = time(); + // some Server limit the number of vars that can be saved in a session + // if this is the case and we lost the language-var we simply include the configuration again + // this way the include is skipped on servers with unlimited vars + if (!isset($config['language'])) { + include './'.$config['files']['parameter']; + } +} +include './language/'.$config['language'].'/lang.php'; +include './language/'.$config['language'].'/lang_restore.php'; +$config['files']['iconpath'] = './css/'.$config['theme'].'/icons/'; +$aus = []; +$pageheader = MODheader().headline($lang['L_RESTORE']); +$aus1 = $page_parameter = ''; +$RestoreFertig = $eingetragen = $dauer = $filegroesse = 0; +mod_mysqli_connect($restore['dump_encoding'], true, $restore['actual_table']); +@mysqli_select_db($config['dbconnection'], $databases['db_actual']) or exit($lang['L_DB_SELECT_ERROR'].$databases['db_actual'].$lang['L_DB_SELECT_ERROR2']); + +// open backup file +$restore['filehandle'] = (1 == $restore['compressed']) ? gzopen($config['paths']['backup'].$restore['filename'], 'r') : fopen($config['paths']['backup'].$restore['filename'], 'r'); +if ($restore['filehandle']) { + //nur am Anfang Logeintrag + if (0 == $restore['offset'] && 0 == $restore['anzahl_tabellen']) { + // Statuszeile auslesen + $restore['part'] = 0; + $statusline = (1 == $restore['compressed']) ? gzgets($restore['filehandle']) : fgets($restore['filehandle']); + $sline = ReadStatusline($statusline, true); + + $restore['anzahl_tabellen'] = $sline['tables']; + $restore['anzahl_eintraege'] = $sline['records']; + if ('MP_0' != $sline['part']) { + $restore['part'] = 1; + } //substr($sline['part'],3); + if (1 == $config['empty_db_before_restore']) { + EmptyDB($databases['db_actual']); + } + $restore['tablelock'] = 0; + $restore['erweiterte_inserts'] = 0; + + if ('-1' == $sline['tables']) { + ($restore['compressed']) ? gzseek($restore['filehandle'], 0) : fseek($restore['filehandle'], 0); + } + if ($restore['part'] > 0) { + WriteLog('Start Multipart-Restore \''.$restore['filename'].'\''); + } else { + WriteLog('Start Restore \''.$restore['filename'].'\''); + } + } else { + if (0 == $restore['compressed']) { + $filegroesse = filesize($config['paths']['backup'].$restore['filename']); + } + // Dateizeiger an die richtige Stelle setzen + ($restore['compressed']) ? gzseek($restore['filehandle'], $restore['offset']) : fseek($restore['filehandle'], $restore['offset']); + + // Jetzt basteln wir uns mal unsere Befehle zusammen... + $a = 0; + $dauer = 0; + $restore['EOB'] = false; + // Disable Keys of actual table to speed up restoring + if (is_array($restore['tables_to_restore']) && sizeof($restore['tables_to_restore']) > 0 && in_array($restore['actual_table'], $restore['tables_to_restore'])) { + @mysqli_query($config['dbconnection'], '/*!40000 ALTER TABLE `'.$restore['actual_table'].'` DISABLE KEYS */;'); + } elseif (!is_array($restore['tables_to_restore']) && + (is_array($restore['tables_to_restore']) && 0 == sizeof($restore['tables_to_restore'])) && + ($restore['actual_table'] > '' && 'unbekannt' != $restore['actual_table'])) { + @mysqli_query($config['dbconnection'], '/*!40000 ALTER TABLE `'.$restore['actual_table'].'` DISABLE KEYS */;'); + } + + while (($a < $restore['anzahl_zeilen']) && (!$restore['fileEOF']) && ($dauer < $restore['max_zeit']) && !$restore['EOB']) { + $sql_command = get_sqlbefehl(); + if ($sql_command > '') { + //WriteLog(htmlspecialchars($sql_command)); + $res = mysqli_query($config['dbconnection'], $sql_command); + if (false === !$res) { + $anzsql = mysqli_affected_rows($config['dbconnection']); + // Anzahl der eingetragenen Datensaetze ermitteln (Indexaktionen nicht zaehlen) + $command = strtoupper(substr($sql_command, 0, 7)); + if ('INSERT ' == $command) { + $anzsql = mysqli_affected_rows($config['dbconnection']); + if ($anzsql > 0) { + $restore['eintraege_ready'] += $anzsql; + } + } + } else { + // Bei MySQL-Fehlern sofort abbrechen und Info ausgeben + $meldung = mysqli_error($config['dbconnection']); + if ('' != $meldung) { + if ('duplicate entry' == strtolower(substr($meldung, 0, 15))) { + ErrorLog('RESTORE', $databases['db_actual'], $sql_command, $meldung, 1); + ++$restore['notices']; + } else { + if (0 == $config['stop_with_error']) { + Errorlog('RESTORE', $databases['db_actual'], $sql_command, $meldung); + ++$restore['errors']; + } else { + Errorlog('RESTORE', $databases['db_actual'], $sql_command, 'Restore failed: '.$meldung, 0); + SQLError($sql_command, $meldung); + exit($sql_command.' -> '.$meldung); + } + } + } + } + } + ++$a; + $dauer = time() - $restore['startzeit']; + } + $eingetragen = $a - 1; + } + + $restore['offset'] = ($restore['compressed']) ? gztell($restore['filehandle']) : ftell($restore['filehandle']); + $restore['compressed'] ? gzclose($restore['filehandle']) : fclose($restore['filehandle']); + ++$restore['aufruf']; + if (!$restore['compressed']) { + $prozent = ($filegroesse > 0) ? ($restore['offset'] * 100) / $filegroesse : 0; + } else { + if ($restore['anzahl_eintraege'] > 0) { + $prozent = $restore['eintraege_ready'] * 100 / $restore['anzahl_eintraege']; + } else { + $prozent = 0; + } + } + if ($prozent > 100) { + $prozent = 100; + } + + if ('' != $aus1) { + $aus[] = '
      '.$aus1.'

      '; + } + $aus[] = sprintf($lang['L_RESTORE_DB'], $databases['db_actual'], $config['dbhost']).'
      '.$lang['L_FILE'].': '.$restore['filename'].'
      '.$lang['L_CHARSET'].': '.$restore['dump_encoding'].'
      '; + if ($restore['part'] > 0) { + $aus[] = '
      Multipart File '.$restore['part'].'
      '; + } + $tabellen_fertig = ($restore['table_ready'] > 0) ? $restore['table_ready'] : '0'; + $to_do = ($restore['anzahl_tabellen'] > 0) ? $restore['anzahl_tabellen'] : $lang['L_UNKNOWN']; + if ($restore['anzahl_tabellen'] > 0) { + $aus[] = sprintf($lang['L_RESTORE_TABLES_COMPLETED'], $tabellen_fertig, $to_do); + } else { + $aus[] = sprintf($lang['L_RESTORE_TABLES_COMPLETED0'], $tabellen_fertig); + } + $done = number_format($restore['eintraege_ready'], 0, ',', '.'); + $to_do = number_format($restore['anzahl_eintraege'], 0, ',', '.'); + if ($restore['anzahl_eintraege'] > 0) { + $aus[] = sprintf($lang['L_RESTORE_RUN1'], $done, $to_do); + } else { + $aus[] = sprintf($lang['L_RESTORE_RUN0'], $done); + } + $aus[] = sprintf($lang['L_RESTORE_RUN2'], $restore['actual_table']).$lang['L_PROGRESS_OVER_ALL'].'
      '; + + //Fortschrittsbalken + $prozentbalken = (round($prozent, 0) * 3); + if ($restore['anzahl_eintraege'] > 0 && false === $restore['tables_to_restore']) { + $aus[] = ''; + if ($prozentbalken >= 3) { + $aus[] = ''.''.'
      +  '.(number_format($prozent, 2, ',', '.')).' %
      '; + } + } else { + $aus[] = ' '.$lang['L_UNKNOWN_NUMBER_OF_RECORDS'].'

      '; + } + + //Speed-Anzeige + $fw = ($config['maxspeed'] == $config['minspeed']) ? 300 : round(($restore['anzahl_zeilen'] - $config['minspeed']) / ($config['maxspeed'] - $config['minspeed']) * 300, 0); + $aus[] = '
      '.'
      '.'Speed
      '.$restore['anzahl_zeilen'].'
      '.''.'
      '.''.'
      '.''.''.'
      '.$config['minspeed'].''.$config['maxspeed'].'
      '; + + //Status-Text + $aus[] = '

      '.zeit_format(time() - $restore['xtime']).', '.$restore['aufruf'].' '.$lang['L_PAGE_REFRESHS'].(($restore['part'] > 0) ? ', file '.$restore['part'] : '').(($restore['errors'] > 0) ? ', '.$restore['errors'].' errors' : '').(($restore['notices'] > 0) ? ', '.$restore['notices'].' notices' : '').'

      '; + $restore['summe_eintraege'] += $eingetragen; + + //Zeitanpassung + if ($dauer < $restore['max_zeit']) { + $restore['anzahl_zeilen'] = $restore['anzahl_zeilen'] * $config['tuning_add']; + // wenn wir mehr als die Haelfte der Zeit noch haetten nutzen koennen: Anzahl direkt um fast das Doppelte erhoehen + if ($dauer < $restore['max_zeit'] / 2) { + $restore['anzahl_zeilen'] = $restore['anzahl_zeilen'] * 1.8; + } + if ($restore['anzahl_zeilen'] > $config['maxspeed']) { + $restore['anzahl_zeilen'] = $config['maxspeed']; + } + } else { + $restore['anzahl_zeilen'] = $restore['anzahl_zeilen'] * $config['tuning_sub']; + if ($restore['anzahl_zeilen'] < $config['minspeed']) { + $restore['anzahl_zeilen'] = $config['minspeed']; + } + } + $restore['anzahl_zeilen'] = intval($restore['anzahl_zeilen']); + if ($restore['fileEOF'] && 0 == $restore['part']) { + $restore['EOB'] = true; + } + if ($restore['EOB']) { + // Uff, geschafft! Jetzt darf die Leitung wieder abkuehlen. :-) + unset($aus); + $aus = []; + $restore['xtime'] = time() - $restore['xtime']; + WriteLog("Restore '".$restore['filename']."' finished in ".zeit_format($restore['xtime']).'.'); + $aus[] = $lang['L_RESTORE_TOTAL_COMPLETE'].'
      '; + $aus[] = $lang['L_FILE'].': '.$restore['filename'].'

      '; + $aus[] = sprintf($lang['L_RESTORE_COMPLETE'], $restore['table_ready']).'
      '; + $aus[] = sprintf($lang['L_RESTORE_COMPLETE2'], number_format($restore['eintraege_ready'], 0, ',', '.')); + $aus[] = '

      '.zeit_format($restore['xtime']).', '.$restore['aufruf'].' '.$lang['L_PAGE_REFRESHS'].'

      '; + if ($restore['errors'] > 0) { + $aus[] = $lang['L_ERRORS'].': '.$restore['errors'].' » '.$lang['L_VIEW'].'
      '; + } + if ($restore['notices'] > 0) { + $aus[] = $lang['L_NOTICES'].': '.$restore['notices'].' » '.$lang['L_VIEW'].'
      '; + } + $aus[] = '
         "; + $aus[] = '   "; + $RestoreFertig = 1; + } else { + if ($restore['fileEOF']) { + //Multipart-Restore + $restore['fileEOF'] = false; + $nextfile = NextPart($restore['filename'], 0, true); + if (!file_exists($config['paths']['backup'].$nextfile)) { + $done = number_format($restore['eintraege_ready'], 0, ',', '.'); + $to_do = number_format($restore['anzahl_eintraege'], 0, ',', '.'); + $aus = []; + $aus[] = '

      '.$lang['L_RESTORE'].'

      '; + $aus[] = sprintf($lang['L_RESTORE_DB'], $databases['db_actual'], $config['dbhost']); + $aus[] = '

      '.$lang['L_MULTI_PART'].': '.$lang['L_FILE_MISSING'].' \''.$nextfile.'\' !

      '; + $aus[] = sprintf($lang['L_RESTORE_RUN1'], $done, $to_do); + $aus[] = sprintf($lang['L_RESTORE_RUN2'], $restore['actual_table']); + $aus[] = '

      '.zeit_format(time() - $restore['xtime']).', '.$restore['aufruf'].' '.$lang['L_PAGE_REFRESHS']; + $aus[] = ($restore['part'] > 0) ? ', '.$lang['L_FILE'].' '.$restore['part'] : ''; + $aus[] = ($restore['errors'] > 0) ? ', '.$restore['errors'].' errors' : ''; + $aus[] = '

      '; + WriteLog('Restore unsuccessful: Cannot find Multipartfile \''.$nextfile.'\''); + $RestoreFertig = 1; + } else { + $restore['filename'] = $nextfile; + $restore['offset'] = 0; + ++$restore['part']; + WriteLog("Continue Multipart-Restore with File '".$restore['filename']."'"); + } + } + } +} else { + $aus[] = $config['paths']['backup'].$restore['filename'].' : '.$lang['L_FILE_OPEN_ERROR']; +} + +$pagefooter = (1 == $RestoreFertig) ? MODFooter() : '
      '; +// formerly all parameters were submitted via POST; now we use a session but we need the form to do the js-selfcall +$page_parameter = '
      '; +if (1 == $RestoreFertig) { + $complete_page = $pageheader.(('' != $aus) ? implode("\n", $aus) : '').$pagefooter; +} else { + $aus[] = $page_parameter; + $_SESSION['restore'] = $restore; + $selbstaufruf = ''; + $complete_page = $pageheader.(('' != $aus) ? implode("\n", $aus) : '').$selbstaufruf.$pagefooter; +} +echo $complete_page; +ob_end_flush(); +exit(); diff --git a/msd/sql.php b/msd/sql.php new file mode 100644 index 0000000..c4ec54f --- /dev/null +++ b/msd/sql.php @@ -0,0 +1,356 @@ + + var auswahl = "document.getElementsByName(\"f_export_tables[]\")[0]"; + var msg1 = "'.$lang['L_SQL_NOTABLESSELECTED'].'"; + '; +} +//Variabeln +$mysql_help_ref = 'http://dev.mysql.com/doc/'; +$mysqli_errorhelp_ref = 'http://dev.mysql.com/doc/mysql/en/error-handling.html'; +$no_order = false; +$config['interface_table_compact'] = isset($config['interface_table_compact']) ? $config['interface_table_compact'] : 1; +$tdcompact = (isset($_GET['tdc'])) ? $_GET['tdc'] : $config['interface_table_compact']; +$db = (!isset($_GET['db'])) ? $databases['db_actual'] : $_GET['db']; +$dbid = (!isset($_GET['dbid'])) ? $databases['db_selected_index'] : $_GET['dbid']; +$context = (!isset($_GET['context'])) ? 0 : $_GET['context']; +$context = (!isset($_POST['context'])) ? $context : $_POST['context']; +$tablename = (!isset($_GET['tablename'])) ? '' : $_GET['tablename']; +$limitstart = (isset($_POST['limitstart'])) ? intval($_POST['limitstart']) : 0; +if (isset($_GET['limitstart'])) { + $limitstart = intval($_GET['limitstart']); +} +$orderdir = (!isset($_GET['orderdir'])) ? '' : $_GET['orderdir']; +$order = (!isset($_GET['order'])) ? '' : $_GET['order']; +$sqlconfig = (isset($_GET['sqlconfig'])) ? 1 : 0; +$norder = ('DESC' == $orderdir) ? 'ASC' : 'DESC'; +$sql['order_statement'] = ('' != $order) ? ' ORDER BY `'.$order.'` '.$norder : ''; +$sql['sql_statement'] = (isset($_GET['sql_statement'])) ? urldecode($_GET['sql_statement']) : ''; +if (isset($_POST['sql_statement'])) { + $sql['sql_statement'] = $_POST['sql_statement']; +} + +$showtables = (!isset($_GET['showtables'])) ? 0 : $_GET['showtables']; +$limit = $add_sql = ''; +$bb = (isset($_GET['bb'])) ? $_GET['bb'] : -1; +if (isset($_POST['tablename'])) { + $tablename = $_POST['tablename']; +} +$search = (isset($_GET['search'])) ? $_GET['search'] : 0; + +//SQL-Statement geposted +if (isset($_POST['execsql'])) { + $sql['sql_statement'] = (isset($_POST['sqltextarea'])) ? $_POST['sqltextarea'] : ''; + $db = $_POST['db']; + $dbid = $_POST['dbid']; + $tablename = $_POST['tablename']; + if (isset($_POST['tablecombo']) && $_POST['tablecombo'] > '') { + $sql['sql_statement'] = $_POST['tablecombo']; + $tablename = ExtractTablenameFromSQL($sql['sql_statement']); + } + if (isset($_POST['sqltextarea']) && $_POST['sqltextarea'] > '') { + $tablename = ExtractTablenameFromSQL($_POST['sqltextarea']); + } + if ('' == $tablename) { + $tablename = ExtractTablenameFromSQL($sql['sql_statement']); + } +} + +if ('' == $sql['sql_statement']) { + if ('' != $tablename && 0 == $showtables) { + $sql['sql_statement'] = "SELECT * FROM `$tablename`"; + } else { + $sql['sql_statement'] = "SHOW TABLE STATUS FROM `$db`"; + $showtables = 1; + } +} + +//sql-type +$sql_to_display_data = 0; +$Anzahl_SQLs = getCountSQLStatements($sql['sql_statement']); +$sql_to_display_data = sqlReturnsRecords($sql['sql_statement']); +if ($Anzahl_SQLs > 1) { + $sql_to_display_data = 0; +} +if (1 == $sql_to_display_data) { + // only one SQL statement + $limitende = ($limitstart + $config['sql_limit']); + + // Is it allowed to edit? + $no_edit = ('SELECT' != strtoupper(substr($sql['sql_statement'], 0, 6)) || 1 == $showtables || preg_match('@^((-- |#)[^\n]*\n|/\*.*?\*/)*(UNION|JOIN)@im', $sql['sql_statement'])); + if ($no_edit) { + $no_order = true; + } + + // May be sorted? + $op = strpos(strtoupper($sql['sql_statement']), ' ORDER '); + if ($op > 0) { + //is order by last ? + $sql['order_statement'] = substr($sql['sql_statement'], $op); + if (strpos($sql['order_statement'], ')') > 0) { + $sql['order_statement'] = ''; + } else { + $sql['sql_statement'] = substr($sql['sql_statement'], 0, $op); + } + } +} + +if (isset($_POST['tableselect']) && '1' != $_POST['tableselect']) { + $tablename = $_POST['tableselect']; +} +mod_mysqli_connect(); +mysqli_select_db($config['dbconnection'], $db); + +///*** EDIT / UPDATES / INSERTS ***/// +///*** ***/// + +// handle update action after submitting it +if (isset($_POST['update']) || isset($_GET['update'])) { + GetPostParams(); + $f = explode('|', $_POST['feldnamen']); + $sqlu = 'UPDATE `'.$_POST['db'].'`.`'.$tablename.'` SET '; + for ($i = 0; $i < count($f); ++$i) { + $index = isset($_POST[$f[$i]]) ? $f[$i] : correct_post_index($f[$i]); + // Check if field is set to null + if (isset($_POST['null_'.$index])) { + // Yes, set it to NULL in Querystring + $sqlu .= '`'.$f[$i].'`=NULL, '; + } else { + $sqlu .= '`'.$f[$i].'`=\''.db_escape(convert_to_latin1($_POST[$index])).'\', '; + } + } + $sqlu = substr($sqlu, 0, strlen($sqlu) - 2).' WHERE '.$recordkey; + $res = mod_query($sqlu); + $msg = '

      '.$lang['L_SQL_RECORDUPDATED'].'

      '; + if (isset($mode) && 'searchedit' == $mode) { + $search = 1; + } + $sql_to_display_data = 1; +} +// handle insert action after submitting it +if (isset($_POST['insert'])) { + GetPostParams(); + $f = explode('|', $_POST['feldnamen']); + $sqlu = 'INSERT INTO `'.$tablename.'` SET '; + for ($i = 0; $i < count($f); ++$i) { + $index = isset($_POST[$f[$i]]) ? $f[$i] : correct_post_index($f[$i]); + if (isset($_POST['null_'.$index])) { + // Yes, set it to NULL in Querystring + $sqlu .= '`'.$f[$i].'` = NULL, '; + } else { + $sqlu .= '`'.$f[$i].'` = \''.db_escape(convert_to_latin1($_POST[$index])).'\', '; + } + } + $sqlu = substr($sqlu, 0, strlen($sqlu) - 2); + $res = mod_query($sqlu); + $msg = '

      '.$lang['L_SQL_RECORDINSERTED'].'

      '; + $sql_to_display_data = 1; +} + +if (isset($_POST['cancel'])) { + GetPostParams(); +} + +//Tabellenansicht +$showtables = ('SHOW TABLE' == substr(strtoupper($sql['sql_statement']), 0, 10)) ? 1 : 0; +$tabellenansicht = ('SHOW ' == substr(strtoupper($sql['sql_statement']), 0, 5)) ? 1 : 0; + +if (!isset($limitstart)) { + $limitstart = 0; +} +$limitende = $config['sql_limit']; +if ('select' == strtolower(substr($sql['sql_statement'], 0, 6))) { + $limit = ' LIMIT '.$limitstart.', '.$limitende.';'; +} + +$params = 'sql.php?db='.$db.'&tablename='.$tablename.'&dbid='.$dbid.'&context='.$context.'&sql_statement='.urlencode($sql['sql_statement']).'&tdc='.$tdcompact.'&showtables='.$showtables; +if ('' != $order) { + $params .= '&order='.$order.'&orderdir='.$orderdir.'&context='.$context; +} +if ($bb > -1) { + $params .= '&bb='.$bb; +} + +$aus = headline($lang['L_SQL_BROWSER']); + +if (0 == $search && !$download) { + echo $aus; + $aus = ''; + include './inc/sqlbrowser/sqlbox.php'; + + if ($mode > '' && 0 == $context) { + if (isset($recordkey) && $recordkey > '') { + $rk = urldecode($recordkey); + } + if (isset($_GET['tablename'])) { + $tablename = $_GET['tablename']; + } + + if ('kill' == $mode || 'kill_view' == $mode) { + if (0 == $showtables) { + $sqlk = "DELETE FROM `$tablename` WHERE ".$rk.' LIMIT 1'; + $res = mod_query($sqlk); + //echo "
      ".$sqlk; + $aus .= '

      '.$lang['L_SQL_RECORDDELETED'].'

      '; + } else { + $sqlk = "DROP TABLE `$rk`"; + if ('kill_view' == $mode) { + $sqlk = 'DROP VIEW `'.$rk.'`'; + } + $res = mod_query($sqlk); + $aus .= '

      '.sprintf($lang['L_SQL_RECORDDELETED'], $rk).'

      '; + } + } + if ('empty' == $mode) { + if (0 != $showtables) { + $sqlk = "TRUNCATE `$rk`"; + $res = mod_query($sqlk); + $aus .= '

      '.sprintf($lang['L_SQL_TABLEEMPTIED'], $rk).'

      '; + } + } + if ('emptyk' == $mode) { + if (0 != $showtables) { + $sqlk = "TRUNCATE `$rk`;"; + $res = mod_query($sqlk); + $sqlk = "ALTER TABLE `$rk` AUTO_INCREMENT = 1;"; + $res = mod_query($sqlk); + $aus .= '

      '.sprintf($lang['L_SQL_TABLEEMPTIEDKEYS'], $rk).'

      '; + } + } + + $javascript_switch = ''; + + if ('edit' == $mode || 'searchedit' == $mode) { + include './inc/sqlbrowser/sql_record_update_inputmask.php'; + } + if ('new' == $mode) { + include './inc/sqlbrowser/sql_record_insert_inputmask.php'; + } + } + if (0 == $context) { + include_once './inc/sqlbrowser/sql_dataview.php'; + } + if (1 == $context) { + include './inc/sqlbrowser/sql_commands.php'; + } + if (2 == $context) { + include './inc/sqlbrowser/sql_tables.php'; + } + if (3 == $context) { + include './inc/sql_tools.php'; + } +} +if (4 == $context) { + include './inc/sql_importexport.php'; +} +if (1 == $search) { + include './inc/sqlbrowser/mysql_search.php'; +} + +if (!$download) { + ?> + +

      '; + echo MODFooter(); + ob_end_flush(); + exit(); +} + +function FormHiddenParams() +{ + global $db, $dbid, $tablename, $context, $limitstart, $order, $orderdir; + + $s = ''; + $s .= ''; + $s .= ''; + $s .= ''; + $s .= ''; + $s .= ''; + $s .= ''; + + return $s; +} diff --git a/msd/tpl/dump_select_tables.tpl b/msd/tpl/dump_select_tables.tpl new file mode 100644 index 0000000..183bc2f --- /dev/null +++ b/msd/tpl/dump_select_tables.tpl @@ -0,0 +1,58 @@ +
      {PAGETITLE}
      +
      {L_DATABASE}: {DATABASE}
      +
      +
      + +
      +
      + + + + + + + + + + + + + + + + + + + + + + + +
      #{L_NAME}{L_ROWS}{L_SIZE}{L_LAST_UPDATE}{L_TABLE_TYPE}
      {ROW.NR}. {ROW.RECORDS}{ROW.SIZE}{ROW.LAST_UPDATE}{ROW.TABLETYPE}
      +
      +
      +
      +
      +
      +
      +
      +
      + + diff --git a/msd/tpl/home/databases_list_dbs.tpl b/msd/tpl/home/databases_list_dbs.tpl new file mode 100644 index 0000000..aa7594a --- /dev/null +++ b/msd/tpl/home/databases_list_dbs.tpl @@ -0,0 +1,47 @@ +
      {L_INFO_DATABASES}
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      #{L_DBS}{L_TABLES}
      {DB_NOT_FOUND.NR}.{DB_NOT_FOUND.DB_NAME}{L_INFO_NODB}
      {ROW.NR}. + + + {ROW.DB_NAME} + + {ROW.TABLE_COUNT} + + {L_TABLE} + + + {L_TABLES} + +
      +
      + +

      +
      diff --git a/msd/tpl/home/databases_list_tables.tpl b/msd/tpl/home/databases_list_tables.tpl new file mode 100644 index 0000000..f40d046 --- /dev/null +++ b/msd/tpl/home/databases_list_tables.tpl @@ -0,0 +1,109 @@ + + +
      {L_INFO_DBDETAIL} "{DB_NAME}"
      +{MESSAGE} + +
      + Found MyISAM tables with disabled keys! For better performance you should enable them.
      +
      +
      + + + +{L_INFO_DBEMPTY}
      + + + +1 {L_TABLE} + + + +{TABLE_COUNT} {L_TABLES} + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      #{L_TABLE}{L_INFO_RECORDS}{L_INFO_SIZE}{L_INFO_LASTUPDATE}{L_ENGINE}{L_INFO_OPTIMIZED}{L_STATUS}Key {L_STATUS}
      {ROW.NR}. + {ROW.TABLE_NAME}{ROW.RECORDS}{ROW.SIZE}{ROW.LAST_UPDATE}{ROW.ENGINE} + + + + + + + + + + {L_CHECK} + + + + + + + +  repair  + + + + +  Enable keys + + + + + +
      {L_INFO_SUM}{SUM.RECORDS}{SUM.SIZE}{SUM.LAST_UPDATE} 
      + +
      + +
      + Found MyISAM tables with disabled keys! For better performance you should enable them.
      +

      +
      + +
      + + + + + + + +
      +


      diff --git a/msd/tpl/home/headnavi.tpl b/msd/tpl/home/headnavi.tpl new file mode 100644 index 0000000..427ffb2 --- /dev/null +++ b/msd/tpl/home/headnavi.tpl @@ -0,0 +1,9 @@ +{HEADER} +{HEADLINE} + +
      diff --git a/msd/tpl/home/home.tpl b/msd/tpl/home/home.tpl new file mode 100644 index 0000000..ade2c87 --- /dev/null +++ b/msd/tpl/home/home.tpl @@ -0,0 +1,74 @@ +
      {L_STATUSINFORMATIONEN}
      + + {DIRECTORY_WARNINGS.MSG}
      + + + +
      + {L_NEW_MOD_VERSION_INFO}
      + {L_UPDATED_IMPORTANT}
      +
      + {L_UPDATE}
      +
      + + + + {DIRECTORY_PROTECTION_STATUS.MSG}
      + + + + {DIRECTORY_PROTECTION_STATUS_ERROR.MSG}
      + + + +
      + {L_HTACC_EDIT}  + {L_DELETE_HTACCESS}
      + + + +
      + {L_HTACC_CREATE}
      + + +
      {L_VERSIONSINFORMATIONEN}
      +love your data +{L_MOD_VERSION}: {MOD_VERSION}
      + + + {UPDATE_INFO.MSG}
      + + +{L_OS}: {OS} ({OS_EXT})
      +{L_MYSQL_VERSION}: {MYSQL_VERSION}
      +{L_PHP_VERSION}: {PHP_VERSION}  {L_MEMORY}: {MEMORY}   +{L_MAX_EXECUTION_TIME}: {MAX_EXECUTION_TIME} {L_SECONDS}   +PHP-Info
      + + + {L_NOFTPPOSSIBLE}
      + + + + {L_NOGZPOSSIBLE}
      + + +{L_PHP_EXTENSIONS}: {PHP_EXTENSIONS}
      + +
      + {L_DISABLEDFUNCTIONS}: {DISABLED_FUNCTIONS.PHP_DISABLED_FUNCTIONS}
      + + +
      + +
      {L_MOD_INFO}
      +{L_INFO_LOCATION} "{SERVER_NAME}" ({MOD_PATH})
      +{L_INFO_ACTDB}: {DB}
      +{L_BACKUPFILESANZAHL} {NR_OF_BACKUP_FILES} +{L_BACKUPS} ({SIZE_BACKUPS})
      +{L_FM_FREESPACE}: {FREE_DISKSPACE}
      + + + {L_LASTBACKUP} {L_VOM} {LAST_BACKUP.LAST_BACKUP_INFO}
      +      {LAST_BACKUP.LAST_BACKUP_NAME}
      + diff --git a/msd/tpl/home/protection_create.tpl b/msd/tpl/home/protection_create.tpl new file mode 100644 index 0000000..b94e4a6 --- /dev/null +++ b/msd/tpl/home/protection_create.tpl @@ -0,0 +1,146 @@ + + + + + + + + + + MyOOS [Dumper] + + + + + +{HEADLINE} + +{MSG.TEXT}

      + + + +
      + + + + + + + + + + + + + + + + + + + + + + +
      {L_USERNAME}:
      {L_PASSWORD}: + +
      {L_PASSWORD_REPEAT}: + +
       
      {L_ENCRYPTION_TYPE}: + + + + + + + + + + + + + + + + + + + + + +
      + + + +
      + + + +
      + + + +
      + + + +
      + + + +
      +
      +
      + +

      +
      +
      + + + + {L_HTACC_CONTENT} .htaccess:

      +
      {CREATE_SUCCESS.HTACCESS}
      + +

      {L_HTACC_CONTENT} .htpasswd:

      +
      {CREATE_SUCCESS.HTPASSWD}
      +

      + {L_HOME} + + + +

      {L_HTACC_CREATE_ERROR}:

      + + {L_HTACC_CONTENT} .htaccess:

      + + +
      {L_HTACC_CONTENT} .htpasswd:

      + + +

      + {L_HOME} + diff --git a/msd/tpl/menu/content.tpl b/msd/tpl/menu/content.tpl new file mode 100644 index 0000000..1049e2a --- /dev/null +++ b/msd/tpl/menu/content.tpl @@ -0,0 +1,44 @@ + + +
      +
      +
      {L_CONFIG}: +
      +
      +
      +
      {L_CHOOSE_DB}: + + + + + + {L_NO_DB_FOUND} + +

      {L_LOAD_DATABASE}

      +
      +
      +
      + diff --git a/msd/tpl/menu/footer.tpl b/msd/tpl/menu/footer.tpl new file mode 100644 index 0000000..9a17eb7 --- /dev/null +++ b/msd/tpl/menu/footer.tpl @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/msd/tpl/menu/header.tpl b/msd/tpl/menu/header.tpl new file mode 100644 index 0000000..0e5bdcf --- /dev/null +++ b/msd/tpl/menu/header.tpl @@ -0,0 +1,33 @@ +{MOD_HEADER} +{MOD_HEADLINE} + + {CONFIG_REFRESH} + + + + + + + + + +MyOOS [Dumper] - Homepage + + diff --git a/msd/tpl/restore_select_tables.tpl b/msd/tpl/restore_select_tables.tpl new file mode 100644 index 0000000..36b2fd6 --- /dev/null +++ b/msd/tpl/restore_select_tables.tpl @@ -0,0 +1,65 @@ +
      {PAGETITLE}
      +
      {L_DATABASE}: {DATABASE}
      +
      +
      +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      #{L_NAME}{L_RESTORE}{L_ROWS}{L_SIZE}{L_LAST_UPDATE}{L_TABLE_TYPE}
      {L_NO_MOD_BACKUP}
      {ROW.NR}. + + + + + + {ROW.RECORDS} + {ROW.SIZE}{ROW.LAST_UPDATE}{ROW.TABLETYPE}
      +
      +
      +
      +
      +
      +
      + + + +
      +
      + + \ No newline at end of file diff --git a/msd/tpl/sqlbrowser/mysql_search.tpl b/msd/tpl/sqlbrowser/mysql_search.tpl new file mode 100644 index 0000000..b2ef3b7 --- /dev/null +++ b/msd/tpl/sqlbrowser/mysql_search.tpl @@ -0,0 +1,73 @@ +
      +
      +
      + {LANG_SQLSEARCH} + {LANG_SQL_SEARCHWORDS}: + + + {LANG_SEARCH_EXPLAIN}
      +
      + {LANG_SEARCH_OPTIONS} + + +
      + + +
      + + +
      + {LANG_SEARCH_IN_TABLE}:   + + + {HIDDEN_FIELDS} +
      +
      +
      + + {HITS.LANG_SEARCH_RESULTS}:
      + +    + +    + {HITS.LANG_ACCESS_KEYS} + +

      + + + + + + + + + + + + + + + + + +
       #{HITS.TABLEHEAD.KEY}
      + {HITS.TABLEROW.ICON_EDIT}{HITS.TABLEROW.ICON_DELETE} + {HITS.TABLEROW.NR}. {HITS.TABLEROW.TABLEDATA.VAL}
      + + + + {NO_RESULTS.LANG_SEARCH_NO_RESULTS} + + + + {NO_ENTRIES.LANG_NO_ENTRIES} + +
      + + diff --git a/msd/tpl/sqlbrowser/sql_record_insert_inputmask.tpl b/msd/tpl/sqlbrowser/sql_record_insert_inputmask.tpl new file mode 100644 index 0000000..f28b733 --- /dev/null +++ b/msd/tpl/sqlbrowser/sql_record_insert_inputmask.tpl @@ -0,0 +1,52 @@ + +
      + + +{HIDDEN_FIELDS} + + + + + + + + + + + + + + + +
      {L_SQL_RECORDNEW}
      {L_NAME}NULL{L_INHALT}
      {ROW.FIELD_NAME} +   + + +   + + + + + + + + + + +
      +
      + + +     +     +

      +
      +
      diff --git a/msd/tpl/sqlbrowser/sql_record_update_inputmask.tpl b/msd/tpl/sqlbrowser/sql_record_update_inputmask.tpl new file mode 100644 index 0000000..2b4f528 --- /dev/null +++ b/msd/tpl/sqlbrowser/sql_record_update_inputmask.tpl @@ -0,0 +1,53 @@ + +
      + + +{HIDDEN_FIELDS} + + + + + + + + + + + + + + + +
      {L_SQL_RECORDEDIT}
      {L_NAME}NULL{L_INHALT}
      {ROW.FIELD_NAME} +   + + +   + + + + + + + + + + +
      +
      + + +     +     +

      +
      +
      diff --git a/msd/tpl/sqlbrowser/sqlbox.tpl b/msd/tpl/sqlbrowser/sqlbox.tpl new file mode 100644 index 0000000..36a534e --- /dev/null +++ b/msd/tpl/sqlbrowser/sqlbox.tpl @@ -0,0 +1,56 @@ +

      + + + + [{LANG_TOOLS}] +  {LANG_DB}: + `{DB}` + +  {LANG_TABLE}: `{TABLENAME}` + +

      + +
      + + + + + + + +
      {SQLUPLOAD.LANG_OPENSQLFILE}{SQLUPLOAD.LANG_SQL_MAXSIZE}: {SQLUPLOAD.MAX_FILESIZE}
      +
      + + +
      +
      +
      + +    + + show less +  show more +     + + + {SQLCOMBO.SQL_COMBOBOX}   + + {TABLE_COMBOBOX}  +   +   + +   {ICON_UPLOAD} +   {ICON_SEARCH} +   {ICON_MYSQL_HELP} +
      + +
      + +
      {LANG_SQL_WARNING}
      + + + +
      +
      +
      +
      diff --git a/msd/vendor/autoload.php b/msd/vendor/autoload.php new file mode 100644 index 0000000..bc51020 --- /dev/null +++ b/msd/vendor/autoload.php @@ -0,0 +1,25 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var ?string */ + private $vendorDir; + + // PSR-4 + /** + * @var array[] + * @psalm-var array> + */ + private $prefixLengthsPsr4 = array(); + /** + * @var array[] + * @psalm-var array> + */ + private $prefixDirsPsr4 = array(); + /** + * @var array[] + * @psalm-var array + */ + private $fallbackDirsPsr4 = array(); + + // PSR-0 + /** + * @var array[] + * @psalm-var array> + */ + private $prefixesPsr0 = array(); + /** + * @var array[] + * @psalm-var array + */ + private $fallbackDirsPsr0 = array(); + + /** @var bool */ + private $useIncludePath = false; + + /** + * @var string[] + * @psalm-var array + */ + private $classMap = array(); + + /** @var bool */ + private $classMapAuthoritative = false; + + /** + * @var bool[] + * @psalm-var array + */ + private $missingClasses = array(); + + /** @var ?string */ + private $apcuPrefix; + + /** + * @var self[] + */ + private static $registeredLoaders = array(); + + /** + * @param ?string $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); + } + + /** + * @return string[] + */ + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + /** + * @return array[] + * @psalm-return array> + */ + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + /** + * @return array[] + * @psalm-return array + */ + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + /** + * @return array[] + * @psalm-return array + */ + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + /** + * @return string[] Array of classname => path + * @psalm-return array + */ + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param string[] $classMap Class to filename map + * @psalm-param array $classMap + * + * @return void + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param string[]|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param string[]|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param string[]|string $paths The PSR-0 base directories + * + * @return void + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param string[]|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + * + * @return void + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + * + * @return void + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + * + * @return void + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return true|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + (self::$includeFile)($file); + + return true; + } + + return null; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders indexed by their corresponding vendor directories. + * + * @return self[] + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } + + private static function initializeIncludeClosure(): void + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = static function($file) { + include $file; + }; + } +} diff --git a/msd/vendor/composer/InstalledVersions.php b/msd/vendor/composer/InstalledVersions.php new file mode 100644 index 0000000..b3696bb --- /dev/null +++ b/msd/vendor/composer/InstalledVersions.php @@ -0,0 +1,352 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final + */ +class InstalledVersions +{ + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']); + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints($constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + + if (self::$canGetVendors) { + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; + if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { + self::$installed = $installed[count($installed) - 1]; + } + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = require __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + $installed[] = self::$installed; + + return $installed; + } +} diff --git a/msd/vendor/composer/LICENSE b/msd/vendor/composer/LICENSE new file mode 100644 index 0000000..6256709 --- /dev/null +++ b/msd/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/msd/vendor/composer/autoload_classmap.php b/msd/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..a3ed879 --- /dev/null +++ b/msd/vendor/composer/autoload_classmap.php @@ -0,0 +1,10 @@ + $vendorDir . '/composer/InstalledVersions.php', +); diff --git a/msd/vendor/composer/autoload_files.php b/msd/vendor/composer/autoload_files.php new file mode 100644 index 0000000..151129f --- /dev/null +++ b/msd/vendor/composer/autoload_files.php @@ -0,0 +1,10 @@ + $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php', +); diff --git a/msd/vendor/composer/autoload_namespaces.php b/msd/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000..ff5488f --- /dev/null +++ b/msd/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/phpseclib/phpseclib/phpseclib'), + 'VisualAppeal\\' => array($vendorDir . '/visualappeal/php-auto-update/src'), + 'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), + 'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'), + 'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'), + 'League\\Flysystem\\PhpseclibV2\\' => array($vendorDir . '/league/flysystem-sftp'), + 'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'), + 'Desarrolla2\\Cache\\' => array($vendorDir . '/desarrolla2/cache/src'), + 'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'), +); diff --git a/msd/vendor/composer/autoload_real.php b/msd/vendor/composer/autoload_real.php new file mode 100644 index 0000000..9baed45 --- /dev/null +++ b/msd/vendor/composer/autoload_real.php @@ -0,0 +1,50 @@ +register(true); + + $filesToLoad = \Composer\Autoload\ComposerStaticInit2352399418895b7bd82ed699298d379a::$files; + $requireFile = static function ($fileIdentifier, $file) { + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + + require $file; + } + }; + foreach ($filesToLoad as $fileIdentifier => $file) { + ($requireFile)($fileIdentifier, $file); + } + + return $loader; + } +} diff --git a/msd/vendor/composer/autoload_static.php b/msd/vendor/composer/autoload_static.php new file mode 100644 index 0000000..3dd365d --- /dev/null +++ b/msd/vendor/composer/autoload_static.php @@ -0,0 +1,103 @@ + __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'p' => + array ( + 'phpseclib\\' => 10, + ), + 'V' => + array ( + 'VisualAppeal\\' => 13, + ), + 'P' => + array ( + 'Psr\\SimpleCache\\' => 16, + 'Psr\\Log\\' => 8, + ), + 'M' => + array ( + 'Monolog\\' => 8, + ), + 'L' => + array ( + 'League\\MimeTypeDetection\\' => 25, + 'League\\Flysystem\\PhpseclibV2\\' => 29, + 'League\\Flysystem\\' => 17, + ), + 'D' => + array ( + 'Desarrolla2\\Cache\\' => 18, + ), + 'C' => + array ( + 'Composer\\Semver\\' => 16, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'phpseclib\\' => + array ( + 0 => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib', + ), + 'VisualAppeal\\' => + array ( + 0 => __DIR__ . '/..' . '/visualappeal/php-auto-update/src', + ), + 'Psr\\SimpleCache\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/simple-cache/src', + ), + 'Psr\\Log\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', + ), + 'Monolog\\' => + array ( + 0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog', + ), + 'League\\MimeTypeDetection\\' => + array ( + 0 => __DIR__ . '/..' . '/league/mime-type-detection/src', + ), + 'League\\Flysystem\\PhpseclibV2\\' => + array ( + 0 => __DIR__ . '/..' . '/league/flysystem-sftp', + ), + 'League\\Flysystem\\' => + array ( + 0 => __DIR__ . '/..' . '/league/flysystem/src', + ), + 'Desarrolla2\\Cache\\' => + array ( + 0 => __DIR__ . '/..' . '/desarrolla2/cache/src', + ), + 'Composer\\Semver\\' => + array ( + 0 => __DIR__ . '/..' . '/composer/semver/src', + ), + ); + + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit2352399418895b7bd82ed699298d379a::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit2352399418895b7bd82ed699298d379a::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit2352399418895b7bd82ed699298d379a::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/msd/vendor/composer/installed.json b/msd/vendor/composer/installed.json new file mode 100644 index 0000000..81f260f --- /dev/null +++ b/msd/vendor/composer/installed.json @@ -0,0 +1,763 @@ +{ + "packages": [ + { + "name": "composer/semver", + "version": "3.3.2", + "version_normalized": "3.3.2.0", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "time": "2022-04-01T19:23:25+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "install-path": "./semver" + }, + { + "name": "desarrolla2/cache", + "version": "v3.0.2", + "version_normalized": "3.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/desarrolla2/Cache.git", + "reference": "0b8f985d09abfc04a2c1535943f6f07b7206161a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/desarrolla2/Cache/zipball/0b8f985d09abfc04a2c1535943f6f07b7206161a", + "reference": "0b8f985d09abfc04a2c1535943f6f07b7206161a", + "shasum": "" + }, + "require": { + "php": ">=7.2.0", + "psr/simple-cache": "^1.0" + }, + "provide": { + "psr/simple-cache-implementation": "1.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "ext-apcu": "*", + "ext-json": "*", + "ext-memcached": "*", + "ext-mysqli": "*", + "mikey179/vfsstream": "v1.6.8", + "mongodb/mongodb": "^1.3", + "phpstan/phpstan": "^0.12.29", + "phpunit/phpunit": "^8.3 || ^9.0", + "predis/predis": "~1.0.0", + "symfony/phpunit-bridge": "^5.2" + }, + "time": "2022-03-27T23:04:06+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Desarrolla2\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel González", + "homepage": "http://desarrolla2.com/" + }, + { + "name": "Arnold Daniels", + "homepage": "https://jasny.net/" + } + ], + "description": "Provides an cache interface for several adapters Apc, Apcu, File, Mongo, Memcache, Memcached, Mysql, Mongo, Redis is supported.", + "homepage": "https://github.com/desarrolla2/Cache/", + "keywords": [ + "apc", + "apcu", + "cache", + "file", + "memcache", + "memcached", + "mongo", + "mysql", + "psr-16", + "redis", + "simple-cache" + ], + "support": { + "issues": "https://github.com/desarrolla2/Cache/issues", + "source": "https://github.com/desarrolla2/Cache/tree/v3.0.2" + }, + "install-path": "../desarrolla2/cache" + }, + { + "name": "league/flysystem", + "version": "2.5.0", + "version_normalized": "2.5.0.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "8aaffb653c5777781b0f7f69a5d937baf7ab6cdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/8aaffb653c5777781b0f7f69a5d937baf7ab6cdb", + "reference": "8aaffb653c5777781b0f7f69a5d937baf7ab6cdb", + "shasum": "" + }, + "require": { + "ext-json": "*", + "league/mime-type-detection": "^1.0.0", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "guzzlehttp/ringphp": "<1.1.1" + }, + "require-dev": { + "async-aws/s3": "^1.5", + "async-aws/simple-s3": "^1.0", + "aws/aws-sdk-php": "^3.132.4", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "friendsofphp/php-cs-fixer": "^3.2", + "google/cloud-storage": "^1.23", + "phpseclib/phpseclib": "^2.0", + "phpstan/phpstan": "^0.12.26", + "phpunit/phpunit": "^8.5 || ^9.4", + "sabre/dav": "^4.1" + }, + "time": "2022-09-17T21:02:32+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/2.5.0" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "install-path": "../league/flysystem" + }, + { + "name": "league/flysystem-sftp", + "version": "2.5.0", + "version_normalized": "2.5.0.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-sftp.git", + "reference": "e30acbc9be024bb7df6ee4b159f2c8db3efcb6a7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-sftp/zipball/e30acbc9be024bb7df6ee4b159f2c8db3efcb6a7", + "reference": "e30acbc9be024bb7df6ee4b159f2c8db3efcb6a7", + "shasum": "" + }, + "require": { + "league/flysystem": "^2.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^7.2 || ^8.0", + "phpseclib/phpseclib": "^2.0" + }, + "time": "2022-04-27T17:27:27+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "League\\Flysystem\\PhpseclibV2\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "SFTP filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "sftp" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem-sftp/issues", + "source": "https://github.com/thephpleague/flysystem-sftp/tree/2.5.0" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "abandoned": "league/flysystem-sftp-v3", + "install-path": "../league/flysystem-sftp" + }, + { + "name": "league/mime-type-detection", + "version": "1.11.0", + "version_normalized": "1.11.0.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3" + }, + "time": "2022-04-17T13:12:02+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "install-path": "../league/mime-type-detection" + }, + { + "name": "monolog/monolog", + "version": "2.8.0", + "version_normalized": "2.8.0.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "720488632c590286b88b80e62aa3d3d551ad4a50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/720488632c590286b88b80e62aa3d3d551ad4a50", + "reference": "720488632c590286b88b80e62aa3d3d551ad4a50", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2", + "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpspec/prophecy": "^1.15", + "phpstan/phpstan": "^0.12.91", + "phpunit/phpunit": "^8.5.14", + "predis/predis": "^1.1 || ^2.0", + "rollbar/rollbar": "^1.3 || ^2 || ^3", + "ruflin/elastica": "^7", + "swiftmailer/swiftmailer": "^5.3|^6.0", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "time": "2022-07-24T11:55:47+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/2.8.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "install-path": "../monolog/monolog" + }, + { + "name": "phpseclib/phpseclib", + "version": "2.0.41", + "version_normalized": "2.0.41.0", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "7e763c6f97ec1fcb37c46aa8ecfc20a2c71d9c1b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/7e763c6f97ec1fcb37c46aa8ecfc20a2c71d9c1b", + "reference": "7e763c6f97ec1fcb37c46aa8ecfc20a2c71d9c1b", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phing/phing": "~2.7", + "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4", + "squizlabs/php_codesniffer": "~2.0" + }, + "suggest": { + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations.", + "ext-xml": "Install the XML extension to load XML formatted public keys." + }, + "time": "2022-12-23T16:44:18+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/2.0.41" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "install-path": "../phpseclib/phpseclib" + }, + { + "name": "psr/log", + "version": "1.1.4", + "version_normalized": "1.1.4.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2021-05-03T11:20:27+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "install-path": "../psr/log" + }, + { + "name": "psr/simple-cache", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2017-10-23T01:57:42+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/master" + }, + "install-path": "../psr/simple-cache" + }, + { + "name": "visualappeal/php-auto-update", + "version": "1.0.2", + "version_normalized": "1.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/VisualAppeal/PHP-Auto-Update.git", + "reference": "4454361a0fa346c7fb179deef11585c79715b645" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/VisualAppeal/PHP-Auto-Update/zipball/4454361a0fa346c7fb179deef11585c79715b645", + "reference": "4454361a0fa346c7fb179deef11585c79715b645", + "shasum": "" + }, + "require": { + "composer/semver": "^3.0", + "desarrolla2/cache": "^3.0", + "ext-curl": "*", + "ext-json": "*", + "ext-zip": "*", + "monolog/monolog": "^2.1", + "php": ">=7.2.0", + "psr/log": "1.1.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "roave/security-advisories": "dev-master" + }, + "time": "2021-11-27T22:39:05+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "VisualAppeal\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tim Helfensdörfer", + "email": "tim@visualappeal.de" + } + ], + "description": "Autoupdater for PHP", + "support": { + "issues": "https://github.com/VisualAppeal/PHP-Auto-Update/issues", + "source": "https://github.com/VisualAppeal/PHP-Auto-Update/tree/1.0.2" + }, + "install-path": "../visualappeal/php-auto-update" + } + ], + "dev": true, + "dev-package-names": [] +} diff --git a/msd/vendor/composer/installed.php b/msd/vendor/composer/installed.php new file mode 100644 index 0000000..db7c59d --- /dev/null +++ b/msd/vendor/composer/installed.php @@ -0,0 +1,125 @@ + array( + 'name' => '__root__', + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'reference' => '36584e64258f869722dd8bc34b66b083a03b2369', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev' => true, + ), + 'versions' => array( + '__root__' => array( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'reference' => '36584e64258f869722dd8bc34b66b083a03b2369', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'composer/semver' => array( + 'pretty_version' => '3.3.2', + 'version' => '3.3.2.0', + 'reference' => '3953f23262f2bff1919fc82183ad9acb13ff62c9', + 'type' => 'library', + 'install_path' => __DIR__ . '/./semver', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'desarrolla2/cache' => array( + 'pretty_version' => 'v3.0.2', + 'version' => '3.0.2.0', + 'reference' => '0b8f985d09abfc04a2c1535943f6f07b7206161a', + 'type' => 'library', + 'install_path' => __DIR__ . '/../desarrolla2/cache', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'league/flysystem' => array( + 'pretty_version' => '2.5.0', + 'version' => '2.5.0.0', + 'reference' => '8aaffb653c5777781b0f7f69a5d937baf7ab6cdb', + 'type' => 'library', + 'install_path' => __DIR__ . '/../league/flysystem', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'league/flysystem-sftp' => array( + 'pretty_version' => '2.5.0', + 'version' => '2.5.0.0', + 'reference' => 'e30acbc9be024bb7df6ee4b159f2c8db3efcb6a7', + 'type' => 'library', + 'install_path' => __DIR__ . '/../league/flysystem-sftp', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'league/mime-type-detection' => array( + 'pretty_version' => '1.11.0', + 'version' => '1.11.0.0', + 'reference' => 'ff6248ea87a9f116e78edd6002e39e5128a0d4dd', + 'type' => 'library', + 'install_path' => __DIR__ . '/../league/mime-type-detection', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'monolog/monolog' => array( + 'pretty_version' => '2.8.0', + 'version' => '2.8.0.0', + 'reference' => '720488632c590286b88b80e62aa3d3d551ad4a50', + 'type' => 'library', + 'install_path' => __DIR__ . '/../monolog/monolog', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'phpseclib/phpseclib' => array( + 'pretty_version' => '2.0.41', + 'version' => '2.0.41.0', + 'reference' => '7e763c6f97ec1fcb37c46aa8ecfc20a2c71d9c1b', + 'type' => 'library', + 'install_path' => __DIR__ . '/../phpseclib/phpseclib', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/log' => array( + 'pretty_version' => '1.1.4', + 'version' => '1.1.4.0', + 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/log', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/log-implementation' => array( + 'dev_requirement' => false, + 'provided' => array( + 0 => '1.0.0 || 2.0.0 || 3.0.0', + ), + ), + 'psr/simple-cache' => array( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'reference' => '408d5eafb83c57f6365a3ca330ff23aa4a5fa39b', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/simple-cache', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/simple-cache-implementation' => array( + 'dev_requirement' => false, + 'provided' => array( + 0 => '1.0', + ), + ), + 'visualappeal/php-auto-update' => array( + 'pretty_version' => '1.0.2', + 'version' => '1.0.2.0', + 'reference' => '4454361a0fa346c7fb179deef11585c79715b645', + 'type' => 'library', + 'install_path' => __DIR__ . '/../visualappeal/php-auto-update', + 'aliases' => array(), + 'dev_requirement' => false, + ), + ), +); diff --git a/msd/vendor/composer/platform_check.php b/msd/vendor/composer/platform_check.php new file mode 100644 index 0000000..bd86409 --- /dev/null +++ b/msd/vendor/composer/platform_check.php @@ -0,0 +1,26 @@ += 70400)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.4.0". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR + ); +} diff --git a/msd/vendor/composer/semver/CHANGELOG.md b/msd/vendor/composer/semver/CHANGELOG.md new file mode 100644 index 0000000..c951477 --- /dev/null +++ b/msd/vendor/composer/semver/CHANGELOG.md @@ -0,0 +1,209 @@ +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +### [3.3.2] 2022-04-01 + + * Fixed handling of non-string values (#134) + +### [3.3.1] 2022-03-16 + + * Fixed possible cache key clash in the CompilingMatcher memoization (#132) + +### [3.3.0] 2022-03-15 + + * Improved performance of CompilingMatcher by memoizing more (#131) + * Added CompilingMatcher::clear to clear all memoization caches + +### [3.2.9] 2022-02-04 + + * Revert #129 (Fixed MultiConstraint with MatchAllConstraint) which caused regressions + +### [3.2.8] 2022-02-04 + + * Updates to latest phpstan / CI by @Seldaek in https://github.com/composer/semver/pull/130 + * Fixed MultiConstraint with MatchAllConstraint by @Toflar in https://github.com/composer/semver/pull/129 + +### [3.2.7] 2022-01-04 + + * Fixed: typo in type definition of Intervals class causing issues with Psalm scanning vendors + +### [3.2.6] 2021-10-25 + + * Fixed: type improvements to parseStability + +### [3.2.5] 2021-05-24 + + * Fixed: issue comparing disjunctive MultiConstraints to conjunctive ones (#127) + * Fixed: added complete type information using phpstan annotations + +### [3.2.4] 2020-11-13 + + * Fixed: code clean-up + +### [3.2.3] 2020-11-12 + + * Fixed: constraints in the form of `X || Y, >=Y.1` and other such complex constructs were in some cases being optimized into a more restrictive constraint + +### [3.2.2] 2020-10-14 + + * Fixed: internal code cleanups + +### [3.2.1] 2020-09-27 + + * Fixed: accidental validation of broken constraints combining ^/~ and wildcards, and -dev suffix allowing weird cases + * Fixed: normalization of beta0 and such which was dropping the 0 + +### [3.2.0] 2020-09-09 + + * Added: support for `x || @dev`, not very useful but seen in the wild and failed to validate with 1.5.2/1.6.0 + * Added: support for `foobar-dev` being equal to `dev-foobar`, dev-foobar is the official way to write it but we need to support the other for BC and convenience + +### [3.1.0] 2020-09-08 + + * Added: support for constraints like `^2.x-dev` and `~2.x-dev`, not very useful but seen in the wild and failed to validate with 3.0.1 + * Fixed: invalid aliases will no longer throw, unless explicitly validated by Composer in the root package + +### [3.0.1] 2020-09-08 + + * Fixed: handling of some invalid -dev versions which were seen as valid + +### [3.0.0] 2020-05-26 + + * Break: Renamed `EmptyConstraint`, replace it with `MatchAllConstraint` + * Break: Unlikely to affect anyone but strictly speaking a breaking change, `*.*` and such variants will not match all `dev-*` versions anymore, only `*` does + * Break: ConstraintInterface is now considered internal/private and not meant to be implemented by third parties anymore + * Added `Intervals` class to check if a constraint is a subsets of another one, and allow compacting complex MultiConstraints into simpler ones + * Added `CompilingMatcher` class to speed up constraint matching against simple Constraint instances + * Added `MatchAllConstraint` and `MatchNoneConstraint` which match everything and nothing + * Added more advanced optimization of contiguous constraints inside MultiConstraint + * Added tentative support for PHP 8 + * Fixed ConstraintInterface::matches to be commutative in all cases + +### [2.0.0] 2020-04-21 + + * Break: `dev-master`, `dev-trunk` and `dev-default` now normalize to `dev-master`, `dev-trunk` and `dev-default` instead of `9999999-dev` in 1.x + * Break: Removed the deprecated `AbstractConstraint` + * Added `getUpperBound` and `getLowerBound` to ConstraintInterface. They return `Composer\Semver\Constraint\Bound` instances + * Added `MultiConstraint::create` to create the most-optimal form of ConstraintInterface from an array of constraint strings + +### [1.7.2] 2020-12-03 + + * Fixed: Allow installing on php 8 + +### [1.7.1] 2020-09-27 + + * Fixed: accidental validation of broken constraints combining ^/~ and wildcards, and -dev suffix allowing weird cases + * Fixed: normalization of beta0 and such which was dropping the 0 + +### [1.7.0] 2020-09-09 + + * Added: support for `x || @dev`, not very useful but seen in the wild and failed to validate with 1.5.2/1.6.0 + * Added: support for `foobar-dev` being equal to `dev-foobar`, dev-foobar is the official way to write it but we need to support the other for BC and convenience + +### [1.6.0] 2020-09-08 + + * Added: support for constraints like `^2.x-dev` and `~2.x-dev`, not very useful but seen in the wild and failed to validate with 1.5.2 + * Fixed: invalid aliases will no longer throw, unless explicitly validated by Composer in the root package + +### [1.5.2] 2020-09-08 + + * Fixed: handling of some invalid -dev versions which were seen as valid + * Fixed: some doctypes + +### [1.5.1] 2020-01-13 + + * Fixed: Parsing of aliased version was not validating the alias to be a valid version + +### [1.5.0] 2019-03-19 + + * Added: some support for date versions (e.g. 201903) in `~` operator + * Fixed: support for stabilities in `~` operator was inconsistent + +### [1.4.2] 2016-08-30 + + * Fixed: collapsing of complex constraints lead to buggy constraints + +### [1.4.1] 2016-06-02 + + * Changed: branch-like requirements no longer strip build metadata - [composer/semver#38](https://github.com/composer/semver/pull/38). + +### [1.4.0] 2016-03-30 + + * Added: getters on MultiConstraint - [composer/semver#35](https://github.com/composer/semver/pull/35). + +### [1.3.0] 2016-02-25 + + * Fixed: stability parsing - [composer/composer#1234](https://github.com/composer/composer/issues/4889). + * Changed: collapse contiguous constraints when possible. + +### [1.2.0] 2015-11-10 + + * Changed: allow multiple numerical identifiers in 'pre-release' version part. + * Changed: add more 'v' prefix support. + +### [1.1.0] 2015-11-03 + + * Changed: dropped redundant `test` namespace. + * Changed: minor adjustment in datetime parsing normalization. + * Changed: `ConstraintInterface` relaxed, setPrettyString is not required anymore. + * Changed: `AbstractConstraint` marked deprecated, will be removed in 2.0. + * Changed: `Constraint` is now extensible. + +### [1.0.0] 2015-09-21 + + * Break: `VersionConstraint` renamed to `Constraint`. + * Break: `SpecificConstraint` renamed to `AbstractConstraint`. + * Break: `LinkConstraintInterface` renamed to `ConstraintInterface`. + * Break: `VersionParser::parseNameVersionPairs` was removed. + * Changed: `VersionParser::parseConstraints` allows (but ignores) build metadata now. + * Changed: `VersionParser::parseConstraints` allows (but ignores) prefixing numeric versions with a 'v' now. + * Changed: Fixed namespace(s) of test files. + * Changed: `Comparator::compare` no longer throws `InvalidArgumentException`. + * Changed: `Constraint` now throws `InvalidArgumentException`. + +### [0.1.0] 2015-07-23 + + * Added: `Composer\Semver\Comparator`, various methods to compare versions. + * Added: various documents such as README.md, LICENSE, etc. + * Added: configuration files for Git, Travis, php-cs-fixer, phpunit. + * Break: the following namespaces were renamed: + - Namespace: `Composer\Package\Version` -> `Composer\Semver` + - Namespace: `Composer\Package\LinkConstraint` -> `Composer\Semver\Constraint` + - Namespace: `Composer\Test\Package\Version` -> `Composer\Test\Semver` + - Namespace: `Composer\Test\Package\LinkConstraint` -> `Composer\Test\Semver\Constraint` + * Changed: code style using php-cs-fixer. + +[3.3.2]: https://github.com/composer/semver/compare/3.3.1...3.3.2 +[3.3.1]: https://github.com/composer/semver/compare/3.3.0...3.3.1 +[3.3.0]: https://github.com/composer/semver/compare/3.2.9...3.3.0 +[3.2.9]: https://github.com/composer/semver/compare/3.2.8...3.2.9 +[3.2.8]: https://github.com/composer/semver/compare/3.2.7...3.2.8 +[3.2.7]: https://github.com/composer/semver/compare/3.2.6...3.2.7 +[3.2.6]: https://github.com/composer/semver/compare/3.2.5...3.2.6 +[3.2.5]: https://github.com/composer/semver/compare/3.2.4...3.2.5 +[3.2.4]: https://github.com/composer/semver/compare/3.2.3...3.2.4 +[3.2.3]: https://github.com/composer/semver/compare/3.2.2...3.2.3 +[3.2.2]: https://github.com/composer/semver/compare/3.2.1...3.2.2 +[3.2.1]: https://github.com/composer/semver/compare/3.2.0...3.2.1 +[3.2.0]: https://github.com/composer/semver/compare/3.1.0...3.2.0 +[3.1.0]: https://github.com/composer/semver/compare/3.0.1...3.1.0 +[3.0.1]: https://github.com/composer/semver/compare/3.0.0...3.0.1 +[3.0.0]: https://github.com/composer/semver/compare/2.0.0...3.0.0 +[2.0.0]: https://github.com/composer/semver/compare/1.5.1...2.0.0 +[1.7.2]: https://github.com/composer/semver/compare/1.7.1...1.7.2 +[1.7.1]: https://github.com/composer/semver/compare/1.7.0...1.7.1 +[1.7.0]: https://github.com/composer/semver/compare/1.6.0...1.7.0 +[1.6.0]: https://github.com/composer/semver/compare/1.5.2...1.6.0 +[1.5.2]: https://github.com/composer/semver/compare/1.5.1...1.5.2 +[1.5.1]: https://github.com/composer/semver/compare/1.5.0...1.5.1 +[1.5.0]: https://github.com/composer/semver/compare/1.4.2...1.5.0 +[1.4.2]: https://github.com/composer/semver/compare/1.4.1...1.4.2 +[1.4.1]: https://github.com/composer/semver/compare/1.4.0...1.4.1 +[1.4.0]: https://github.com/composer/semver/compare/1.3.0...1.4.0 +[1.3.0]: https://github.com/composer/semver/compare/1.2.0...1.3.0 +[1.2.0]: https://github.com/composer/semver/compare/1.1.0...1.2.0 +[1.1.0]: https://github.com/composer/semver/compare/1.0.0...1.1.0 +[1.0.0]: https://github.com/composer/semver/compare/0.1.0...1.0.0 +[0.1.0]: https://github.com/composer/semver/compare/5e0b9a4da...0.1.0 diff --git a/msd/vendor/composer/semver/LICENSE b/msd/vendor/composer/semver/LICENSE new file mode 100644 index 0000000..be0e0ae --- /dev/null +++ b/msd/vendor/composer/semver/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2015 Composer + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/msd/vendor/composer/semver/README.md b/msd/vendor/composer/semver/README.md new file mode 100644 index 0000000..35db99a --- /dev/null +++ b/msd/vendor/composer/semver/README.md @@ -0,0 +1,98 @@ +composer/semver +=============== + +Semver (Semantic Versioning) library that offers utilities, version constraint parsing and validation. + +Originally written as part of [composer/composer](https://github.com/composer/composer), +now extracted and made available as a stand-alone library. + +[![Continuous Integration](https://github.com/composer/semver/workflows/Continuous%20Integration/badge.svg?branch=main)](https://github.com/composer/semver/actions) + + +Installation +------------ + +Install the latest version with: + +```bash +$ composer require composer/semver +``` + + +Requirements +------------ + +* PHP 5.3.2 is required but using the latest version of PHP is highly recommended. + + +Version Comparison +------------------ + +For details on how versions are compared, refer to the [Versions](https://getcomposer.org/doc/articles/versions.md) +article in the documentation section of the [getcomposer.org](https://getcomposer.org) website. + + +Basic usage +----------- + +### Comparator + +The [`Composer\Semver\Comparator`](https://github.com/composer/semver/blob/main/src/Comparator.php) class provides the following methods for comparing versions: + +* greaterThan($v1, $v2) +* greaterThanOrEqualTo($v1, $v2) +* lessThan($v1, $v2) +* lessThanOrEqualTo($v1, $v2) +* equalTo($v1, $v2) +* notEqualTo($v1, $v2) + +Each function takes two version strings as arguments and returns a boolean. For example: + +```php +use Composer\Semver\Comparator; + +Comparator::greaterThan('1.25.0', '1.24.0'); // 1.25.0 > 1.24.0 +``` + +### Semver + +The [`Composer\Semver\Semver`](https://github.com/composer/semver/blob/main/src/Semver.php) class provides the following methods: + +* satisfies($version, $constraints) +* satisfiedBy(array $versions, $constraint) +* sort($versions) +* rsort($versions) + +### Intervals + +The [`Composer\Semver\Intervals`](https://github.com/composer/semver/blob/main/src/Intervals.php) static class provides +a few utilities to work with complex constraints or read version intervals from a constraint: + +```php +use Composer\Semver\Intervals; + +// Checks whether $candidate is a subset of $constraint +Intervals::isSubsetOf(ConstraintInterface $candidate, ConstraintInterface $constraint); + +// Checks whether $a and $b have any intersection, equivalent to $a->matches($b) +Intervals::haveIntersections(ConstraintInterface $a, ConstraintInterface $b); + +// Optimizes a complex multi constraint by merging all intervals down to the smallest +// possible multi constraint. The drawbacks are this is not very fast, and the resulting +// multi constraint will have no human readable prettyConstraint configured on it +Intervals::compactConstraint(ConstraintInterface $constraint); + +// Creates an array of numeric intervals and branch constraints representing a given constraint +Intervals::get(ConstraintInterface $constraint); + +// Clears the memoization cache when you are done processing constraints +Intervals::clear() +``` + +See the class docblocks for more details. + + +License +------- + +composer/semver is licensed under the MIT License, see the LICENSE file for details. diff --git a/msd/vendor/composer/semver/composer.json b/msd/vendor/composer/semver/composer.json new file mode 100644 index 0000000..ba78676 --- /dev/null +++ b/msd/vendor/composer/semver/composer.json @@ -0,0 +1,59 @@ +{ + "name": "composer/semver", + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "type": "library", + "license": "MIT", + "keywords": [ + "semver", + "semantic", + "versioning", + "validation" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.2 || ^5", + "phpstan/phpstan": "^1.4" + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Composer\\Semver\\": "tests" + } + }, + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "scripts": { + "test": "SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT=1 vendor/bin/simple-phpunit", + "phpstan": "@php vendor/bin/phpstan analyse" + } +} diff --git a/msd/vendor/composer/semver/src/Comparator.php b/msd/vendor/composer/semver/src/Comparator.php new file mode 100644 index 0000000..9fe97bf --- /dev/null +++ b/msd/vendor/composer/semver/src/Comparator.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver; + +use Composer\Semver\Constraint\Constraint; + +class Comparator +{ + /** + * Evaluates the expression: $version1 > $version2. + * + * @param string $version1 + * @param string $version2 + * + * @return bool + */ + public static function greaterThan($version1, $version2) + { + return self::compare($version1, '>', $version2); + } + + /** + * Evaluates the expression: $version1 >= $version2. + * + * @param string $version1 + * @param string $version2 + * + * @return bool + */ + public static function greaterThanOrEqualTo($version1, $version2) + { + return self::compare($version1, '>=', $version2); + } + + /** + * Evaluates the expression: $version1 < $version2. + * + * @param string $version1 + * @param string $version2 + * + * @return bool + */ + public static function lessThan($version1, $version2) + { + return self::compare($version1, '<', $version2); + } + + /** + * Evaluates the expression: $version1 <= $version2. + * + * @param string $version1 + * @param string $version2 + * + * @return bool + */ + public static function lessThanOrEqualTo($version1, $version2) + { + return self::compare($version1, '<=', $version2); + } + + /** + * Evaluates the expression: $version1 == $version2. + * + * @param string $version1 + * @param string $version2 + * + * @return bool + */ + public static function equalTo($version1, $version2) + { + return self::compare($version1, '==', $version2); + } + + /** + * Evaluates the expression: $version1 != $version2. + * + * @param string $version1 + * @param string $version2 + * + * @return bool + */ + public static function notEqualTo($version1, $version2) + { + return self::compare($version1, '!=', $version2); + } + + /** + * Evaluates the expression: $version1 $operator $version2. + * + * @param string $version1 + * @param string $operator + * @param string $version2 + * + * @return bool + * + * @phpstan-param Constraint::STR_OP_* $operator + */ + public static function compare($version1, $operator, $version2) + { + $constraint = new Constraint($operator, $version2); + + return $constraint->matchSpecific(new Constraint('==', $version1), true); + } +} diff --git a/msd/vendor/composer/semver/src/CompilingMatcher.php b/msd/vendor/composer/semver/src/CompilingMatcher.php new file mode 100644 index 0000000..f0fdecb --- /dev/null +++ b/msd/vendor/composer/semver/src/CompilingMatcher.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver; + +use Composer\Semver\Constraint\Constraint; +use Composer\Semver\Constraint\ConstraintInterface; + +/** + * Helper class to evaluate constraint by compiling and reusing the code to evaluate + */ +class CompilingMatcher +{ + /** + * @var array + * @phpstan-var array + */ + private static $compiledCheckerCache = array(); + /** + * @var array + * @phpstan-var array + */ + private static $resultCache = array(); + + /** @var bool */ + private static $enabled; + + /** + * @phpstan-var array + */ + private static $transOpInt = array( + Constraint::OP_EQ => Constraint::STR_OP_EQ, + Constraint::OP_LT => Constraint::STR_OP_LT, + Constraint::OP_LE => Constraint::STR_OP_LE, + Constraint::OP_GT => Constraint::STR_OP_GT, + Constraint::OP_GE => Constraint::STR_OP_GE, + Constraint::OP_NE => Constraint::STR_OP_NE, + ); + + /** + * Clears the memoization cache once you are done + * + * @return void + */ + public static function clear() + { + self::$resultCache = array(); + self::$compiledCheckerCache = array(); + } + + /** + * Evaluates the expression: $constraint match $operator $version + * + * @param ConstraintInterface $constraint + * @param int $operator + * @phpstan-param Constraint::OP_* $operator + * @param string $version + * + * @return mixed + */ + public static function match(ConstraintInterface $constraint, $operator, $version) + { + $resultCacheKey = $operator.$constraint.';'.$version; + + if (isset(self::$resultCache[$resultCacheKey])) { + return self::$resultCache[$resultCacheKey]; + } + + if (self::$enabled === null) { + self::$enabled = !\in_array('eval', explode(',', (string) ini_get('disable_functions')), true); + } + if (!self::$enabled) { + return self::$resultCache[$resultCacheKey] = $constraint->matches(new Constraint(self::$transOpInt[$operator], $version)); + } + + $cacheKey = $operator.$constraint; + if (!isset(self::$compiledCheckerCache[$cacheKey])) { + $code = $constraint->compile($operator); + self::$compiledCheckerCache[$cacheKey] = $function = eval('return function($v, $b){return '.$code.';};'); + } else { + $function = self::$compiledCheckerCache[$cacheKey]; + } + + return self::$resultCache[$resultCacheKey] = $function($version, strpos($version, 'dev-') === 0); + } +} diff --git a/msd/vendor/composer/semver/src/Constraint/Bound.php b/msd/vendor/composer/semver/src/Constraint/Bound.php new file mode 100644 index 0000000..b861fec --- /dev/null +++ b/msd/vendor/composer/semver/src/Constraint/Bound.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver\Constraint; + +class Bound +{ + /** + * @var string + */ + private $version; + + /** + * @var bool + */ + private $isInclusive; + + /** + * @param string $version + * @param bool $isInclusive + */ + public function __construct($version, $isInclusive) + { + $this->version = $version; + $this->isInclusive = $isInclusive; + } + + /** + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * @return bool + */ + public function isInclusive() + { + return $this->isInclusive; + } + + /** + * @return bool + */ + public function isZero() + { + return $this->getVersion() === '0.0.0.0-dev' && $this->isInclusive(); + } + + /** + * @return bool + */ + public function isPositiveInfinity() + { + return $this->getVersion() === PHP_INT_MAX.'.0.0.0' && !$this->isInclusive(); + } + + /** + * Compares a bound to another with a given operator. + * + * @param Bound $other + * @param string $operator + * + * @return bool + */ + public function compareTo(Bound $other, $operator) + { + if (!\in_array($operator, array('<', '>'), true)) { + throw new \InvalidArgumentException('Does not support any other operator other than > or <.'); + } + + // If they are the same it doesn't matter + if ($this == $other) { + return false; + } + + $compareResult = version_compare($this->getVersion(), $other->getVersion()); + + // Not the same version means we don't need to check if the bounds are inclusive or not + if (0 !== $compareResult) { + return (('>' === $operator) ? 1 : -1) === $compareResult; + } + + // Question we're answering here is "am I higher than $other?" + return '>' === $operator ? $other->isInclusive() : !$other->isInclusive(); + } + + public function __toString() + { + return sprintf( + '%s [%s]', + $this->getVersion(), + $this->isInclusive() ? 'inclusive' : 'exclusive' + ); + } + + /** + * @return self + */ + public static function zero() + { + return new Bound('0.0.0.0-dev', true); + } + + /** + * @return self + */ + public static function positiveInfinity() + { + return new Bound(PHP_INT_MAX.'.0.0.0', false); + } +} diff --git a/msd/vendor/composer/semver/src/Constraint/Constraint.php b/msd/vendor/composer/semver/src/Constraint/Constraint.php new file mode 100644 index 0000000..9ba02f4 --- /dev/null +++ b/msd/vendor/composer/semver/src/Constraint/Constraint.php @@ -0,0 +1,435 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver\Constraint; + +/** + * Defines a constraint. + */ +class Constraint implements ConstraintInterface +{ + /* operator integer values */ + const OP_EQ = 0; + const OP_LT = 1; + const OP_LE = 2; + const OP_GT = 3; + const OP_GE = 4; + const OP_NE = 5; + + /* operator string values */ + const STR_OP_EQ = '=='; + const STR_OP_EQ_ALT = '='; + const STR_OP_LT = '<'; + const STR_OP_LE = '<='; + const STR_OP_GT = '>'; + const STR_OP_GE = '>='; + const STR_OP_NE = '!='; + const STR_OP_NE_ALT = '<>'; + + /** + * Operator to integer translation table. + * + * @var array + * @phpstan-var array + */ + private static $transOpStr = array( + '=' => self::OP_EQ, + '==' => self::OP_EQ, + '<' => self::OP_LT, + '<=' => self::OP_LE, + '>' => self::OP_GT, + '>=' => self::OP_GE, + '<>' => self::OP_NE, + '!=' => self::OP_NE, + ); + + /** + * Integer to operator translation table. + * + * @var array + * @phpstan-var array + */ + private static $transOpInt = array( + self::OP_EQ => '==', + self::OP_LT => '<', + self::OP_LE => '<=', + self::OP_GT => '>', + self::OP_GE => '>=', + self::OP_NE => '!=', + ); + + /** + * @var int + * @phpstan-var self::OP_* + */ + protected $operator; + + /** @var string */ + protected $version; + + /** @var string|null */ + protected $prettyString; + + /** @var Bound */ + protected $lowerBound; + + /** @var Bound */ + protected $upperBound; + + /** + * Sets operator and version to compare with. + * + * @param string $operator + * @param string $version + * + * @throws \InvalidArgumentException if invalid operator is given. + * + * @phpstan-param self::STR_OP_* $operator + */ + public function __construct($operator, $version) + { + if (!isset(self::$transOpStr[$operator])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid operator "%s" given, expected one of: %s', + $operator, + implode(', ', self::getSupportedOperators()) + )); + } + + $this->operator = self::$transOpStr[$operator]; + $this->version = $version; + } + + /** + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * @return string + * + * @phpstan-return self::STR_OP_* + */ + public function getOperator() + { + return self::$transOpInt[$this->operator]; + } + + /** + * @param ConstraintInterface $provider + * + * @return bool + */ + public function matches(ConstraintInterface $provider) + { + if ($provider instanceof self) { + return $this->matchSpecific($provider); + } + + // turn matching around to find a match + return $provider->matches($this); + } + + /** + * {@inheritDoc} + */ + public function setPrettyString($prettyString) + { + $this->prettyString = $prettyString; + } + + /** + * {@inheritDoc} + */ + public function getPrettyString() + { + if ($this->prettyString) { + return $this->prettyString; + } + + return $this->__toString(); + } + + /** + * Get all supported comparison operators. + * + * @return array + * + * @phpstan-return list + */ + public static function getSupportedOperators() + { + return array_keys(self::$transOpStr); + } + + /** + * @param string $operator + * @return int + * + * @phpstan-param self::STR_OP_* $operator + * @phpstan-return self::OP_* + */ + public static function getOperatorConstant($operator) + { + return self::$transOpStr[$operator]; + } + + /** + * @param string $a + * @param string $b + * @param string $operator + * @param bool $compareBranches + * + * @throws \InvalidArgumentException if invalid operator is given. + * + * @return bool + * + * @phpstan-param self::STR_OP_* $operator + */ + public function versionCompare($a, $b, $operator, $compareBranches = false) + { + if (!isset(self::$transOpStr[$operator])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid operator "%s" given, expected one of: %s', + $operator, + implode(', ', self::getSupportedOperators()) + )); + } + + $aIsBranch = strpos($a, 'dev-') === 0; + $bIsBranch = strpos($b, 'dev-') === 0; + + if ($operator === '!=' && ($aIsBranch || $bIsBranch)) { + return $a !== $b; + } + + if ($aIsBranch && $bIsBranch) { + return $operator === '==' && $a === $b; + } + + // when branches are not comparable, we make sure dev branches never match anything + if (!$compareBranches && ($aIsBranch || $bIsBranch)) { + return false; + } + + return \version_compare($a, $b, $operator); + } + + /** + * {@inheritDoc} + */ + public function compile($otherOperator) + { + if (strpos($this->version, 'dev-') === 0) { + if (self::OP_EQ === $this->operator) { + if (self::OP_EQ === $otherOperator) { + return sprintf('$b && $v === %s', \var_export($this->version, true)); + } + if (self::OP_NE === $otherOperator) { + return sprintf('!$b || $v !== %s', \var_export($this->version, true)); + } + return 'false'; + } + + if (self::OP_NE === $this->operator) { + if (self::OP_EQ === $otherOperator) { + return sprintf('!$b || $v !== %s', \var_export($this->version, true)); + } + if (self::OP_NE === $otherOperator) { + return 'true'; + } + return '!$b'; + } + + return 'false'; + } + + if (self::OP_EQ === $this->operator) { + if (self::OP_EQ === $otherOperator) { + return sprintf('\version_compare($v, %s, \'==\')', \var_export($this->version, true)); + } + if (self::OP_NE === $otherOperator) { + return sprintf('$b || \version_compare($v, %s, \'!=\')', \var_export($this->version, true)); + } + + return sprintf('!$b && \version_compare(%s, $v, \'%s\')', \var_export($this->version, true), self::$transOpInt[$otherOperator]); + } + + if (self::OP_NE === $this->operator) { + if (self::OP_EQ === $otherOperator) { + return sprintf('$b || (!$b && \version_compare($v, %s, \'!=\'))', \var_export($this->version, true)); + } + + if (self::OP_NE === $otherOperator) { + return 'true'; + } + return '!$b'; + } + + if (self::OP_LT === $this->operator || self::OP_LE === $this->operator) { + if (self::OP_LT === $otherOperator || self::OP_LE === $otherOperator) { + return '!$b'; + } + } else { // $this->operator must be self::OP_GT || self::OP_GE here + if (self::OP_GT === $otherOperator || self::OP_GE === $otherOperator) { + return '!$b'; + } + } + + if (self::OP_NE === $otherOperator) { + return 'true'; + } + + $codeComparison = sprintf('\version_compare($v, %s, \'%s\')', \var_export($this->version, true), self::$transOpInt[$this->operator]); + if ($this->operator === self::OP_LE) { + if ($otherOperator === self::OP_GT) { + return sprintf('!$b && \version_compare($v, %s, \'!=\') && ', \var_export($this->version, true)) . $codeComparison; + } + } elseif ($this->operator === self::OP_GE) { + if ($otherOperator === self::OP_LT) { + return sprintf('!$b && \version_compare($v, %s, \'!=\') && ', \var_export($this->version, true)) . $codeComparison; + } + } + + return sprintf('!$b && %s', $codeComparison); + } + + /** + * @param Constraint $provider + * @param bool $compareBranches + * + * @return bool + */ + public function matchSpecific(Constraint $provider, $compareBranches = false) + { + $noEqualOp = str_replace('=', '', self::$transOpInt[$this->operator]); + $providerNoEqualOp = str_replace('=', '', self::$transOpInt[$provider->operator]); + + $isEqualOp = self::OP_EQ === $this->operator; + $isNonEqualOp = self::OP_NE === $this->operator; + $isProviderEqualOp = self::OP_EQ === $provider->operator; + $isProviderNonEqualOp = self::OP_NE === $provider->operator; + + // '!=' operator is match when other operator is not '==' operator or version is not match + // these kinds of comparisons always have a solution + if ($isNonEqualOp || $isProviderNonEqualOp) { + if ($isNonEqualOp && !$isProviderNonEqualOp && !$isProviderEqualOp && strpos($provider->version, 'dev-') === 0) { + return false; + } + + if ($isProviderNonEqualOp && !$isNonEqualOp && !$isEqualOp && strpos($this->version, 'dev-') === 0) { + return false; + } + + if (!$isEqualOp && !$isProviderEqualOp) { + return true; + } + return $this->versionCompare($provider->version, $this->version, '!=', $compareBranches); + } + + // an example for the condition is <= 2.0 & < 1.0 + // these kinds of comparisons always have a solution + if ($this->operator !== self::OP_EQ && $noEqualOp === $providerNoEqualOp) { + return !(strpos($this->version, 'dev-') === 0 || strpos($provider->version, 'dev-') === 0); + } + + $version1 = $isEqualOp ? $this->version : $provider->version; + $version2 = $isEqualOp ? $provider->version : $this->version; + $operator = $isEqualOp ? $provider->operator : $this->operator; + + if ($this->versionCompare($version1, $version2, self::$transOpInt[$operator], $compareBranches)) { + // special case, e.g. require >= 1.0 and provide < 1.0 + // 1.0 >= 1.0 but 1.0 is outside of the provided interval + + return !(self::$transOpInt[$provider->operator] === $providerNoEqualOp + && self::$transOpInt[$this->operator] !== $noEqualOp + && \version_compare($provider->version, $this->version, '==')); + } + + return false; + } + + /** + * @return string + */ + public function __toString() + { + return self::$transOpInt[$this->operator] . ' ' . $this->version; + } + + /** + * {@inheritDoc} + */ + public function getLowerBound() + { + $this->extractBounds(); + + return $this->lowerBound; + } + + /** + * {@inheritDoc} + */ + public function getUpperBound() + { + $this->extractBounds(); + + return $this->upperBound; + } + + /** + * @return void + */ + private function extractBounds() + { + if (null !== $this->lowerBound) { + return; + } + + // Branches + if (strpos($this->version, 'dev-') === 0) { + $this->lowerBound = Bound::zero(); + $this->upperBound = Bound::positiveInfinity(); + + return; + } + + switch ($this->operator) { + case self::OP_EQ: + $this->lowerBound = new Bound($this->version, true); + $this->upperBound = new Bound($this->version, true); + break; + case self::OP_LT: + $this->lowerBound = Bound::zero(); + $this->upperBound = new Bound($this->version, false); + break; + case self::OP_LE: + $this->lowerBound = Bound::zero(); + $this->upperBound = new Bound($this->version, true); + break; + case self::OP_GT: + $this->lowerBound = new Bound($this->version, false); + $this->upperBound = Bound::positiveInfinity(); + break; + case self::OP_GE: + $this->lowerBound = new Bound($this->version, true); + $this->upperBound = Bound::positiveInfinity(); + break; + case self::OP_NE: + $this->lowerBound = Bound::zero(); + $this->upperBound = Bound::positiveInfinity(); + break; + } + } +} diff --git a/msd/vendor/composer/semver/src/Constraint/ConstraintInterface.php b/msd/vendor/composer/semver/src/Constraint/ConstraintInterface.php new file mode 100644 index 0000000..18b92ee --- /dev/null +++ b/msd/vendor/composer/semver/src/Constraint/ConstraintInterface.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver\Constraint; + +/** + * DO NOT IMPLEMENT this interface. It is only meant for usage as a type hint + * in libraries relying on composer/semver but creating your own constraint class + * that implements this interface is not a supported use case and will cause the + * composer/semver components to return unexpected results. + */ +interface ConstraintInterface +{ + /** + * Checks whether the given constraint intersects in any way with this constraint + * + * @param ConstraintInterface $provider + * + * @return bool + */ + public function matches(ConstraintInterface $provider); + + /** + * Provides a compiled version of the constraint for the given operator + * The compiled version must be a PHP expression. + * Executor of compile version must provide 2 variables: + * - $v = the string version to compare with + * - $b = whether or not the version is a non-comparable branch (starts with "dev-") + * + * @see Constraint::OP_* for the list of available operators. + * @example return '!$b && version_compare($v, '1.0', '>')'; + * + * @param int $otherOperator one Constraint::OP_* + * + * @return string + * + * @phpstan-param Constraint::OP_* $otherOperator + */ + public function compile($otherOperator); + + /** + * @return Bound + */ + public function getUpperBound(); + + /** + * @return Bound + */ + public function getLowerBound(); + + /** + * @return string + */ + public function getPrettyString(); + + /** + * @param string|null $prettyString + * + * @return void + */ + public function setPrettyString($prettyString); + + /** + * @return string + */ + public function __toString(); +} diff --git a/msd/vendor/composer/semver/src/Constraint/MatchAllConstraint.php b/msd/vendor/composer/semver/src/Constraint/MatchAllConstraint.php new file mode 100644 index 0000000..20bafa4 --- /dev/null +++ b/msd/vendor/composer/semver/src/Constraint/MatchAllConstraint.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver\Constraint; + +/** + * Defines the absence of a constraint. + * + * This constraint matches everything. + */ +class MatchAllConstraint implements ConstraintInterface +{ + /** @var string|null */ + protected $prettyString; + + /** + * @param ConstraintInterface $provider + * + * @return bool + */ + public function matches(ConstraintInterface $provider) + { + return true; + } + + /** + * {@inheritDoc} + */ + public function compile($otherOperator) + { + return 'true'; + } + + /** + * {@inheritDoc} + */ + public function setPrettyString($prettyString) + { + $this->prettyString = $prettyString; + } + + /** + * {@inheritDoc} + */ + public function getPrettyString() + { + if ($this->prettyString) { + return $this->prettyString; + } + + return (string) $this; + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + return '*'; + } + + /** + * {@inheritDoc} + */ + public function getUpperBound() + { + return Bound::positiveInfinity(); + } + + /** + * {@inheritDoc} + */ + public function getLowerBound() + { + return Bound::zero(); + } +} diff --git a/msd/vendor/composer/semver/src/Constraint/MatchNoneConstraint.php b/msd/vendor/composer/semver/src/Constraint/MatchNoneConstraint.php new file mode 100644 index 0000000..9f6dc71 --- /dev/null +++ b/msd/vendor/composer/semver/src/Constraint/MatchNoneConstraint.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver\Constraint; + +/** + * Blackhole of constraints, nothing escapes it + */ +class MatchNoneConstraint implements ConstraintInterface +{ + /** @var string|null */ + protected $prettyString; + + /** + * @param ConstraintInterface $provider + * + * @return bool + */ + public function matches(ConstraintInterface $provider) + { + return false; + } + + /** + * {@inheritDoc} + */ + public function compile($otherOperator) + { + return 'false'; + } + + /** + * {@inheritDoc} + */ + public function setPrettyString($prettyString) + { + $this->prettyString = $prettyString; + } + + /** + * {@inheritDoc} + */ + public function getPrettyString() + { + if ($this->prettyString) { + return $this->prettyString; + } + + return (string) $this; + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + return '[]'; + } + + /** + * {@inheritDoc} + */ + public function getUpperBound() + { + return new Bound('0.0.0.0-dev', false); + } + + /** + * {@inheritDoc} + */ + public function getLowerBound() + { + return new Bound('0.0.0.0-dev', false); + } +} diff --git a/msd/vendor/composer/semver/src/Constraint/MultiConstraint.php b/msd/vendor/composer/semver/src/Constraint/MultiConstraint.php new file mode 100644 index 0000000..9ed689c --- /dev/null +++ b/msd/vendor/composer/semver/src/Constraint/MultiConstraint.php @@ -0,0 +1,325 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver\Constraint; + +/** + * Defines a conjunctive or disjunctive set of constraints. + */ +class MultiConstraint implements ConstraintInterface +{ + /** + * @var ConstraintInterface[] + * @phpstan-var non-empty-array + */ + protected $constraints; + + /** @var string|null */ + protected $prettyString; + + /** @var string|null */ + protected $string; + + /** @var bool */ + protected $conjunctive; + + /** @var Bound|null */ + protected $lowerBound; + + /** @var Bound|null */ + protected $upperBound; + + /** + * @param ConstraintInterface[] $constraints A set of constraints + * @param bool $conjunctive Whether the constraints should be treated as conjunctive or disjunctive + * + * @throws \InvalidArgumentException If less than 2 constraints are passed + */ + public function __construct(array $constraints, $conjunctive = true) + { + if (\count($constraints) < 2) { + throw new \InvalidArgumentException( + 'Must provide at least two constraints for a MultiConstraint. Use '. + 'the regular Constraint class for one constraint only or MatchAllConstraint for none. You may use '. + 'MultiConstraint::create() which optimizes and handles those cases automatically.' + ); + } + + $this->constraints = $constraints; + $this->conjunctive = $conjunctive; + } + + /** + * @return ConstraintInterface[] + */ + public function getConstraints() + { + return $this->constraints; + } + + /** + * @return bool + */ + public function isConjunctive() + { + return $this->conjunctive; + } + + /** + * @return bool + */ + public function isDisjunctive() + { + return !$this->conjunctive; + } + + /** + * {@inheritDoc} + */ + public function compile($otherOperator) + { + $parts = array(); + foreach ($this->constraints as $constraint) { + $code = $constraint->compile($otherOperator); + if ($code === 'true') { + if (!$this->conjunctive) { + return 'true'; + } + } elseif ($code === 'false') { + if ($this->conjunctive) { + return 'false'; + } + } else { + $parts[] = '('.$code.')'; + } + } + + if (!$parts) { + return $this->conjunctive ? 'true' : 'false'; + } + + return $this->conjunctive ? implode('&&', $parts) : implode('||', $parts); + } + + /** + * @param ConstraintInterface $provider + * + * @return bool + */ + public function matches(ConstraintInterface $provider) + { + if (false === $this->conjunctive) { + foreach ($this->constraints as $constraint) { + if ($provider->matches($constraint)) { + return true; + } + } + + return false; + } + + // when matching a conjunctive and a disjunctive multi constraint we have to iterate over the disjunctive one + // otherwise we'd return true if different parts of the disjunctive constraint match the conjunctive one + // which would lead to incorrect results, e.g. [>1 and <2] would match [<1 or >2] although they do not intersect + if ($provider instanceof MultiConstraint && $provider->isDisjunctive()) { + return $provider->matches($this); + } + + foreach ($this->constraints as $constraint) { + if (!$provider->matches($constraint)) { + return false; + } + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function setPrettyString($prettyString) + { + $this->prettyString = $prettyString; + } + + /** + * {@inheritDoc} + */ + public function getPrettyString() + { + if ($this->prettyString) { + return $this->prettyString; + } + + return (string) $this; + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + if ($this->string !== null) { + return $this->string; + } + + $constraints = array(); + foreach ($this->constraints as $constraint) { + $constraints[] = (string) $constraint; + } + + return $this->string = '[' . implode($this->conjunctive ? ' ' : ' || ', $constraints) . ']'; + } + + /** + * {@inheritDoc} + */ + public function getLowerBound() + { + $this->extractBounds(); + + if (null === $this->lowerBound) { + throw new \LogicException('extractBounds should have populated the lowerBound property'); + } + + return $this->lowerBound; + } + + /** + * {@inheritDoc} + */ + public function getUpperBound() + { + $this->extractBounds(); + + if (null === $this->upperBound) { + throw new \LogicException('extractBounds should have populated the upperBound property'); + } + + return $this->upperBound; + } + + /** + * Tries to optimize the constraints as much as possible, meaning + * reducing/collapsing congruent constraints etc. + * Does not necessarily return a MultiConstraint instance if + * things can be reduced to a simple constraint + * + * @param ConstraintInterface[] $constraints A set of constraints + * @param bool $conjunctive Whether the constraints should be treated as conjunctive or disjunctive + * + * @return ConstraintInterface + */ + public static function create(array $constraints, $conjunctive = true) + { + if (0 === \count($constraints)) { + return new MatchAllConstraint(); + } + + if (1 === \count($constraints)) { + return $constraints[0]; + } + + $optimized = self::optimizeConstraints($constraints, $conjunctive); + if ($optimized !== null) { + list($constraints, $conjunctive) = $optimized; + if (\count($constraints) === 1) { + return $constraints[0]; + } + } + + return new self($constraints, $conjunctive); + } + + /** + * @param ConstraintInterface[] $constraints + * @param bool $conjunctive + * @return ?array + * + * @phpstan-return array{0: list, 1: bool}|null + */ + private static function optimizeConstraints(array $constraints, $conjunctive) + { + // parse the two OR groups and if they are contiguous we collapse + // them into one constraint + // [>= 1 < 2] || [>= 2 < 3] || [>= 3 < 4] => [>= 1 < 4] + if (!$conjunctive) { + $left = $constraints[0]; + $mergedConstraints = array(); + $optimized = false; + for ($i = 1, $l = \count($constraints); $i < $l; $i++) { + $right = $constraints[$i]; + if ( + $left instanceof self + && $left->conjunctive + && $right instanceof self + && $right->conjunctive + && \count($left->constraints) === 2 + && \count($right->constraints) === 2 + && ($left0 = (string) $left->constraints[0]) + && $left0[0] === '>' && $left0[1] === '=' + && ($left1 = (string) $left->constraints[1]) + && $left1[0] === '<' + && ($right0 = (string) $right->constraints[0]) + && $right0[0] === '>' && $right0[1] === '=' + && ($right1 = (string) $right->constraints[1]) + && $right1[0] === '<' + && substr($left1, 2) === substr($right0, 3) + ) { + $optimized = true; + $left = new MultiConstraint( + array( + $left->constraints[0], + $right->constraints[1], + ), + true); + } else { + $mergedConstraints[] = $left; + $left = $right; + } + } + if ($optimized) { + $mergedConstraints[] = $left; + return array($mergedConstraints, false); + } + } + + // TODO: Here's the place to put more optimizations + + return null; + } + + /** + * @return void + */ + private function extractBounds() + { + if (null !== $this->lowerBound) { + return; + } + + foreach ($this->constraints as $constraint) { + if (null === $this->lowerBound || null === $this->upperBound) { + $this->lowerBound = $constraint->getLowerBound(); + $this->upperBound = $constraint->getUpperBound(); + continue; + } + + if ($constraint->getLowerBound()->compareTo($this->lowerBound, $this->isConjunctive() ? '>' : '<')) { + $this->lowerBound = $constraint->getLowerBound(); + } + + if ($constraint->getUpperBound()->compareTo($this->upperBound, $this->isConjunctive() ? '<' : '>')) { + $this->upperBound = $constraint->getUpperBound(); + } + } + } +} diff --git a/msd/vendor/composer/semver/src/Interval.php b/msd/vendor/composer/semver/src/Interval.php new file mode 100644 index 0000000..9b80ba8 --- /dev/null +++ b/msd/vendor/composer/semver/src/Interval.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver; + +use Composer\Semver\Constraint\Constraint; + +class Interval +{ + /** @var Constraint */ + private $start; + /** @var Constraint */ + private $end; + + public function __construct(Constraint $start, Constraint $end) + { + $this->start = $start; + $this->end = $end; + } + + /** + * @return Constraint + */ + public function getStart() + { + return $this->start; + } + + /** + * @return Constraint + */ + public function getEnd() + { + return $this->end; + } + + /** + * @return Constraint + */ + public static function fromZero() + { + static $zero; + + if (null === $zero) { + $zero = new Constraint('>=', '0.0.0.0-dev'); + } + + return $zero; + } + + /** + * @return Constraint + */ + public static function untilPositiveInfinity() + { + static $positiveInfinity; + + if (null === $positiveInfinity) { + $positiveInfinity = new Constraint('<', PHP_INT_MAX.'.0.0.0'); + } + + return $positiveInfinity; + } + + /** + * @return self + */ + public static function any() + { + return new self(self::fromZero(), self::untilPositiveInfinity()); + } + + /** + * @return array{'names': string[], 'exclude': bool} + */ + public static function anyDev() + { + // any == exclude nothing + return array('names' => array(), 'exclude' => true); + } + + /** + * @return array{'names': string[], 'exclude': bool} + */ + public static function noDev() + { + // nothing == no names included + return array('names' => array(), 'exclude' => false); + } +} diff --git a/msd/vendor/composer/semver/src/Intervals.php b/msd/vendor/composer/semver/src/Intervals.php new file mode 100644 index 0000000..3d5c594 --- /dev/null +++ b/msd/vendor/composer/semver/src/Intervals.php @@ -0,0 +1,478 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver; + +use Composer\Semver\Constraint\Constraint; +use Composer\Semver\Constraint\ConstraintInterface; +use Composer\Semver\Constraint\MatchAllConstraint; +use Composer\Semver\Constraint\MatchNoneConstraint; +use Composer\Semver\Constraint\MultiConstraint; + +/** + * Helper class generating intervals from constraints + * + * This contains utilities for: + * + * - compacting an existing constraint which can be used to combine several into one + * by creating a MultiConstraint out of the many constraints you have. + * + * - checking whether one subset is a subset of another. + * + * Note: You should call clear to free memoization memory usage when you are done using this class + */ +class Intervals +{ + /** + * @phpstan-var array + */ + private static $intervalsCache = array(); + + /** + * @phpstan-var array + */ + private static $opSortOrder = array( + '>=' => -3, + '<' => -2, + '>' => 2, + '<=' => 3, + ); + + /** + * Clears the memoization cache once you are done + * + * @return void + */ + public static function clear() + { + self::$intervalsCache = array(); + } + + /** + * Checks whether $candidate is a subset of $constraint + * + * @return bool + */ + public static function isSubsetOf(ConstraintInterface $candidate, ConstraintInterface $constraint) + { + if ($constraint instanceof MatchAllConstraint) { + return true; + } + + if ($candidate instanceof MatchNoneConstraint || $constraint instanceof MatchNoneConstraint) { + return false; + } + + $intersectionIntervals = self::get(new MultiConstraint(array($candidate, $constraint), true)); + $candidateIntervals = self::get($candidate); + if (\count($intersectionIntervals['numeric']) !== \count($candidateIntervals['numeric'])) { + return false; + } + + foreach ($intersectionIntervals['numeric'] as $index => $interval) { + if (!isset($candidateIntervals['numeric'][$index])) { + return false; + } + + if ((string) $candidateIntervals['numeric'][$index]->getStart() !== (string) $interval->getStart()) { + return false; + } + + if ((string) $candidateIntervals['numeric'][$index]->getEnd() !== (string) $interval->getEnd()) { + return false; + } + } + + if ($intersectionIntervals['branches']['exclude'] !== $candidateIntervals['branches']['exclude']) { + return false; + } + if (\count($intersectionIntervals['branches']['names']) !== \count($candidateIntervals['branches']['names'])) { + return false; + } + foreach ($intersectionIntervals['branches']['names'] as $index => $name) { + if ($name !== $candidateIntervals['branches']['names'][$index]) { + return false; + } + } + + return true; + } + + /** + * Checks whether $a and $b have any intersection, equivalent to $a->matches($b) + * + * @return bool + */ + public static function haveIntersections(ConstraintInterface $a, ConstraintInterface $b) + { + if ($a instanceof MatchAllConstraint || $b instanceof MatchAllConstraint) { + return true; + } + + if ($a instanceof MatchNoneConstraint || $b instanceof MatchNoneConstraint) { + return false; + } + + $intersectionIntervals = self::generateIntervals(new MultiConstraint(array($a, $b), true), true); + + return \count($intersectionIntervals['numeric']) > 0 || $intersectionIntervals['branches']['exclude'] || \count($intersectionIntervals['branches']['names']) > 0; + } + + /** + * Attempts to optimize a MultiConstraint + * + * When merging MultiConstraints together they can get very large, this will + * compact it by looking at the real intervals covered by all the constraints + * and then creates a new constraint containing only the smallest amount of rules + * to match the same intervals. + * + * @return ConstraintInterface + */ + public static function compactConstraint(ConstraintInterface $constraint) + { + if (!$constraint instanceof MultiConstraint) { + return $constraint; + } + + $intervals = self::generateIntervals($constraint); + $constraints = array(); + $hasNumericMatchAll = false; + + if (\count($intervals['numeric']) === 1 && (string) $intervals['numeric'][0]->getStart() === (string) Interval::fromZero() && (string) $intervals['numeric'][0]->getEnd() === (string) Interval::untilPositiveInfinity()) { + $constraints[] = $intervals['numeric'][0]->getStart(); + $hasNumericMatchAll = true; + } else { + $unEqualConstraints = array(); + for ($i = 0, $count = \count($intervals['numeric']); $i < $count; $i++) { + $interval = $intervals['numeric'][$i]; + + // if current interval ends with < N and next interval begins with > N we can swap this out for != N + // but this needs to happen as a conjunctive expression together with the start of the current interval + // and end of next interval, so [>=M, N, [>=M, !=N, getEnd()->getOperator() === '<' && $i+1 < $count) { + $nextInterval = $intervals['numeric'][$i+1]; + if ($interval->getEnd()->getVersion() === $nextInterval->getStart()->getVersion() && $nextInterval->getStart()->getOperator() === '>') { + // only add a start if we didn't already do so, can be skipped if we're looking at second + // interval in [>=M, N, P, =M, !=N] already and we only want to add !=P right now + if (\count($unEqualConstraints) === 0 && (string) $interval->getStart() !== (string) Interval::fromZero()) { + $unEqualConstraints[] = $interval->getStart(); + } + $unEqualConstraints[] = new Constraint('!=', $interval->getEnd()->getVersion()); + continue; + } + } + + if (\count($unEqualConstraints) > 0) { + // this is where the end of the following interval of a != constraint is added as explained above + if ((string) $interval->getEnd() !== (string) Interval::untilPositiveInfinity()) { + $unEqualConstraints[] = $interval->getEnd(); + } + + // count is 1 if entire constraint is just one != expression + if (\count($unEqualConstraints) > 1) { + $constraints[] = new MultiConstraint($unEqualConstraints, true); + } else { + $constraints[] = $unEqualConstraints[0]; + } + + $unEqualConstraints = array(); + continue; + } + + // convert back >= x - <= x intervals to == x + if ($interval->getStart()->getVersion() === $interval->getEnd()->getVersion() && $interval->getStart()->getOperator() === '>=' && $interval->getEnd()->getOperator() === '<=') { + $constraints[] = new Constraint('==', $interval->getStart()->getVersion()); + continue; + } + + if ((string) $interval->getStart() === (string) Interval::fromZero()) { + $constraints[] = $interval->getEnd(); + } elseif ((string) $interval->getEnd() === (string) Interval::untilPositiveInfinity()) { + $constraints[] = $interval->getStart(); + } else { + $constraints[] = new MultiConstraint(array($interval->getStart(), $interval->getEnd()), true); + } + } + } + + $devConstraints = array(); + + if (0 === \count($intervals['branches']['names'])) { + if ($intervals['branches']['exclude']) { + if ($hasNumericMatchAll) { + return new MatchAllConstraint; + } + // otherwise constraint should contain a != operator and already cover this + } + } else { + foreach ($intervals['branches']['names'] as $branchName) { + if ($intervals['branches']['exclude']) { + $devConstraints[] = new Constraint('!=', $branchName); + } else { + $devConstraints[] = new Constraint('==', $branchName); + } + } + + // excluded branches, e.g. != dev-foo are conjunctive with the interval, so + // > 2.0 != dev-foo must return a conjunctive constraint + if ($intervals['branches']['exclude']) { + if (\count($constraints) > 1) { + return new MultiConstraint(array_merge( + array(new MultiConstraint($constraints, false)), + $devConstraints + ), true); + } + + if (\count($constraints) === 1 && (string)$constraints[0] === (string)Interval::fromZero()) { + if (\count($devConstraints) > 1) { + return new MultiConstraint($devConstraints, true); + } + return $devConstraints[0]; + } + + return new MultiConstraint(array_merge($constraints, $devConstraints), true); + } + + // otherwise devConstraints contains a list of == operators for branches which are disjunctive with the + // rest of the constraint + $constraints = array_merge($constraints, $devConstraints); + } + + if (\count($constraints) > 1) { + return new MultiConstraint($constraints, false); + } + + if (\count($constraints) === 1) { + return $constraints[0]; + } + + return new MatchNoneConstraint; + } + + /** + * Creates an array of numeric intervals and branch constraints representing a given constraint + * + * if the returned numeric array is empty it means the constraint matches nothing in the numeric range (0 - +inf) + * if the returned branches array is empty it means no dev-* versions are matched + * if a constraint matches all possible dev-* versions, branches will contain Interval::anyDev() + * + * @return array + * @phpstan-return array{'numeric': Interval[], 'branches': array{'names': string[], 'exclude': bool}} + */ + public static function get(ConstraintInterface $constraint) + { + $key = (string) $constraint; + + if (!isset(self::$intervalsCache[$key])) { + self::$intervalsCache[$key] = self::generateIntervals($constraint); + } + + return self::$intervalsCache[$key]; + } + + /** + * @param bool $stopOnFirstValidInterval + * + * @phpstan-return array{'numeric': Interval[], 'branches': array{'names': string[], 'exclude': bool}} + */ + private static function generateIntervals(ConstraintInterface $constraint, $stopOnFirstValidInterval = false) + { + if ($constraint instanceof MatchAllConstraint) { + return array('numeric' => array(new Interval(Interval::fromZero(), Interval::untilPositiveInfinity())), 'branches' => Interval::anyDev()); + } + + if ($constraint instanceof MatchNoneConstraint) { + return array('numeric' => array(), 'branches' => array('names' => array(), 'exclude' => false)); + } + + if ($constraint instanceof Constraint) { + return self::generateSingleConstraintIntervals($constraint); + } + + if (!$constraint instanceof MultiConstraint) { + throw new \UnexpectedValueException('The constraint passed in should be an MatchAllConstraint, Constraint or MultiConstraint instance, got '.\get_class($constraint).'.'); + } + + $constraints = $constraint->getConstraints(); + + $numericGroups = array(); + $constraintBranches = array(); + foreach ($constraints as $c) { + $res = self::get($c); + $numericGroups[] = $res['numeric']; + $constraintBranches[] = $res['branches']; + } + + if ($constraint->isDisjunctive()) { + $branches = Interval::noDev(); + foreach ($constraintBranches as $b) { + if ($b['exclude']) { + if ($branches['exclude']) { + // disjunctive constraint, so only exclude what's excluded in all constraints + // !=a,!=b || !=b,!=c => !=b + $branches['names'] = array_intersect($branches['names'], $b['names']); + } else { + // disjunctive constraint so exclude all names which are not explicitly included in the alternative + // (==b || ==c) || !=a,!=b => !=a + $branches['exclude'] = true; + $branches['names'] = array_diff($b['names'], $branches['names']); + } + } else { + if ($branches['exclude']) { + // disjunctive constraint so exclude all names which are not explicitly included in the alternative + // !=a,!=b || (==b || ==c) => !=a + $branches['names'] = array_diff($branches['names'], $b['names']); + } else { + // disjunctive constraint, so just add all the other branches + // (==a || ==b) || ==c => ==a || ==b || ==c + $branches['names'] = array_merge($branches['names'], $b['names']); + } + } + } + } else { + $branches = Interval::anyDev(); + foreach ($constraintBranches as $b) { + if ($b['exclude']) { + if ($branches['exclude']) { + // conjunctive, so just add all branch names to be excluded + // !=a && !=b => !=a,!=b + $branches['names'] = array_merge($branches['names'], $b['names']); + } else { + // conjunctive, so only keep included names which are not excluded + // (==a||==c) && !=a,!=b => ==c + $branches['names'] = array_diff($branches['names'], $b['names']); + } + } else { + if ($branches['exclude']) { + // conjunctive, so only keep included names which are not excluded + // !=a,!=b && (==a||==c) => ==c + $branches['names'] = array_diff($b['names'], $branches['names']); + $branches['exclude'] = false; + } else { + // conjunctive, so only keep names that are included in both + // (==a||==b) && (==a||==c) => ==a + $branches['names'] = array_intersect($branches['names'], $b['names']); + } + } + } + } + + $branches['names'] = array_unique($branches['names']); + + if (\count($numericGroups) === 1) { + return array('numeric' => $numericGroups[0], 'branches' => $branches); + } + + $borders = array(); + foreach ($numericGroups as $group) { + foreach ($group as $interval) { + $borders[] = array('version' => $interval->getStart()->getVersion(), 'operator' => $interval->getStart()->getOperator(), 'side' => 'start'); + $borders[] = array('version' => $interval->getEnd()->getVersion(), 'operator' => $interval->getEnd()->getOperator(), 'side' => 'end'); + } + } + + $opSortOrder = self::$opSortOrder; + usort($borders, function ($a, $b) use ($opSortOrder) { + $order = version_compare($a['version'], $b['version']); + if ($order === 0) { + return $opSortOrder[$a['operator']] - $opSortOrder[$b['operator']]; + } + + return $order; + }); + + $activeIntervals = 0; + $intervals = array(); + $index = 0; + $activationThreshold = $constraint->isConjunctive() ? \count($numericGroups) : 1; + $start = null; + foreach ($borders as $border) { + if ($border['side'] === 'start') { + $activeIntervals++; + } else { + $activeIntervals--; + } + if (!$start && $activeIntervals >= $activationThreshold) { + $start = new Constraint($border['operator'], $border['version']); + } elseif ($start && $activeIntervals < $activationThreshold) { + // filter out invalid intervals like > x - <= x, or >= x - < x + if ( + version_compare($start->getVersion(), $border['version'], '=') + && ( + ($start->getOperator() === '>' && $border['operator'] === '<=') + || ($start->getOperator() === '>=' && $border['operator'] === '<') + ) + ) { + unset($intervals[$index]); + } else { + $intervals[$index] = new Interval($start, new Constraint($border['operator'], $border['version'])); + $index++; + + if ($stopOnFirstValidInterval) { + break; + } + } + + $start = null; + } + } + + return array('numeric' => $intervals, 'branches' => $branches); + } + + /** + * @phpstan-return array{'numeric': Interval[], 'branches': array{'names': string[], 'exclude': bool}} + */ + private static function generateSingleConstraintIntervals(Constraint $constraint) + { + $op = $constraint->getOperator(); + + // handle branch constraints first + if (strpos($constraint->getVersion(), 'dev-') === 0) { + $intervals = array(); + $branches = array('names' => array(), 'exclude' => false); + + // != dev-foo means any numeric version may match, we treat >/< like != they are not really defined for branches + if ($op === '!=') { + $intervals[] = new Interval(Interval::fromZero(), Interval::untilPositiveInfinity()); + $branches = array('names' => array($constraint->getVersion()), 'exclude' => true); + } elseif ($op === '==') { + $branches['names'][] = $constraint->getVersion(); + } + + return array( + 'numeric' => $intervals, + 'branches' => $branches, + ); + } + + if ($op[0] === '>') { // > & >= + return array('numeric' => array(new Interval($constraint, Interval::untilPositiveInfinity())), 'branches' => Interval::noDev()); + } + if ($op[0] === '<') { // < & <= + return array('numeric' => array(new Interval(Interval::fromZero(), $constraint)), 'branches' => Interval::noDev()); + } + if ($op === '!=') { + // convert !=x to intervals of 0 - x - +inf + dev* + return array('numeric' => array( + new Interval(Interval::fromZero(), new Constraint('<', $constraint->getVersion())), + new Interval(new Constraint('>', $constraint->getVersion()), Interval::untilPositiveInfinity()), + ), 'branches' => Interval::anyDev()); + } + + // convert ==x to an interval of >=x - <=x + return array('numeric' => array( + new Interval(new Constraint('>=', $constraint->getVersion()), new Constraint('<=', $constraint->getVersion())), + ), 'branches' => Interval::noDev()); + } +} diff --git a/msd/vendor/composer/semver/src/Semver.php b/msd/vendor/composer/semver/src/Semver.php new file mode 100644 index 0000000..b937837 --- /dev/null +++ b/msd/vendor/composer/semver/src/Semver.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver; + +use Composer\Semver\Constraint\Constraint; + +class Semver +{ + const SORT_ASC = 1; + const SORT_DESC = -1; + + /** @var VersionParser */ + private static $versionParser; + + /** + * Determine if given version satisfies given constraints. + * + * @param string $version + * @param string $constraints + * + * @return bool + */ + public static function satisfies($version, $constraints) + { + if (null === self::$versionParser) { + self::$versionParser = new VersionParser(); + } + + $versionParser = self::$versionParser; + $provider = new Constraint('==', $versionParser->normalize($version)); + $parsedConstraints = $versionParser->parseConstraints($constraints); + + return $parsedConstraints->matches($provider); + } + + /** + * Return all versions that satisfy given constraints. + * + * @param string[] $versions + * @param string $constraints + * + * @return string[] + */ + public static function satisfiedBy(array $versions, $constraints) + { + $versions = array_filter($versions, function ($version) use ($constraints) { + return Semver::satisfies($version, $constraints); + }); + + return array_values($versions); + } + + /** + * Sort given array of versions. + * + * @param string[] $versions + * + * @return string[] + */ + public static function sort(array $versions) + { + return self::usort($versions, self::SORT_ASC); + } + + /** + * Sort given array of versions in reverse. + * + * @param string[] $versions + * + * @return string[] + */ + public static function rsort(array $versions) + { + return self::usort($versions, self::SORT_DESC); + } + + /** + * @param string[] $versions + * @param int $direction + * + * @return string[] + */ + private static function usort(array $versions, $direction) + { + if (null === self::$versionParser) { + self::$versionParser = new VersionParser(); + } + + $versionParser = self::$versionParser; + $normalized = array(); + + // Normalize outside of usort() scope for minor performance increase. + // Creates an array of arrays: [[normalized, key], ...] + foreach ($versions as $key => $version) { + $normalizedVersion = $versionParser->normalize($version); + $normalizedVersion = $versionParser->normalizeDefaultBranch($normalizedVersion); + $normalized[] = array($normalizedVersion, $key); + } + + usort($normalized, function (array $left, array $right) use ($direction) { + if ($left[0] === $right[0]) { + return 0; + } + + if (Comparator::lessThan($left[0], $right[0])) { + return -$direction; + } + + return $direction; + }); + + // Recreate input array, using the original indexes which are now in sorted order. + $sorted = array(); + foreach ($normalized as $item) { + $sorted[] = $versions[$item[1]]; + } + + return $sorted; + } +} diff --git a/msd/vendor/composer/semver/src/VersionParser.php b/msd/vendor/composer/semver/src/VersionParser.php new file mode 100644 index 0000000..5250d0e --- /dev/null +++ b/msd/vendor/composer/semver/src/VersionParser.php @@ -0,0 +1,586 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Composer\Semver; + +use Composer\Semver\Constraint\ConstraintInterface; +use Composer\Semver\Constraint\MatchAllConstraint; +use Composer\Semver\Constraint\MultiConstraint; +use Composer\Semver\Constraint\Constraint; + +/** + * Version parser. + * + * @author Jordi Boggiano + */ +class VersionParser +{ + /** + * Regex to match pre-release data (sort of). + * + * Due to backwards compatibility: + * - Instead of enforcing hyphen, an underscore, dot or nothing at all are also accepted. + * - Only stabilities as recognized by Composer are allowed to precede a numerical identifier. + * - Numerical-only pre-release identifiers are not supported, see tests. + * + * |--------------| + * [major].[minor].[patch] -[pre-release] +[build-metadata] + * + * @var string + */ + private static $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)((?:[.-]?\d+)*+)?)?([.-]?dev)?'; + + /** @var string */ + private static $stabilitiesRegex = 'stable|RC|beta|alpha|dev'; + + /** + * Returns the stability of a version. + * + * @param string $version + * + * @return string + * @phpstan-return 'stable'|'RC'|'beta'|'alpha'|'dev' + */ + public static function parseStability($version) + { + $version = (string) preg_replace('{#.+$}', '', (string) $version); + + if (strpos($version, 'dev-') === 0 || '-dev' === substr($version, -4)) { + return 'dev'; + } + + preg_match('{' . self::$modifierRegex . '(?:\+.*)?$}i', strtolower($version), $match); + + if (!empty($match[3])) { + return 'dev'; + } + + if (!empty($match[1])) { + if ('beta' === $match[1] || 'b' === $match[1]) { + return 'beta'; + } + if ('alpha' === $match[1] || 'a' === $match[1]) { + return 'alpha'; + } + if ('rc' === $match[1]) { + return 'RC'; + } + } + + return 'stable'; + } + + /** + * @param string $stability + * + * @return string + */ + public static function normalizeStability($stability) + { + $stability = strtolower((string) $stability); + + return $stability === 'rc' ? 'RC' : $stability; + } + + /** + * Normalizes a version string to be able to perform comparisons on it. + * + * @param string $version + * @param ?string $fullVersion optional complete version string to give more context + * + * @throws \UnexpectedValueException + * + * @return string + */ + public function normalize($version, $fullVersion = null) + { + $version = trim((string) $version); + $origVersion = $version; + if (null === $fullVersion) { + $fullVersion = $version; + } + + // strip off aliasing + if (preg_match('{^([^,\s]++) ++as ++([^,\s]++)$}', $version, $match)) { + $version = $match[1]; + } + + // strip off stability flag + if (preg_match('{@(?:' . self::$stabilitiesRegex . ')$}i', $version, $match)) { + $version = substr($version, 0, strlen($version) - strlen($match[0])); + } + + // normalize master/trunk/default branches to dev-name for BC with 1.x as these used to be valid constraints + if (\in_array($version, array('master', 'trunk', 'default'), true)) { + $version = 'dev-' . $version; + } + + // if requirement is branch-like, use full name + if (stripos($version, 'dev-') === 0) { + return 'dev-' . substr($version, 4); + } + + // strip off build metadata + if (preg_match('{^([^,\s+]++)\+[^\s]++$}', $version, $match)) { + $version = $match[1]; + } + + // match classical versioning + if (preg_match('{^v?(\d{1,5})(\.\d++)?(\.\d++)?(\.\d++)?' . self::$modifierRegex . '$}i', $version, $matches)) { + $version = $matches[1] + . (!empty($matches[2]) ? $matches[2] : '.0') + . (!empty($matches[3]) ? $matches[3] : '.0') + . (!empty($matches[4]) ? $matches[4] : '.0'); + $index = 5; + // match date(time) based versioning + } elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)' . self::$modifierRegex . '$}i', $version, $matches)) { + $version = preg_replace('{\D}', '.', $matches[1]); + $index = 2; + } + + // add version modifiers if a version was matched + if (isset($index)) { + if (!empty($matches[$index])) { + if ('stable' === $matches[$index]) { + return $version; + } + $version .= '-' . $this->expandStability($matches[$index]) . (isset($matches[$index + 1]) && '' !== $matches[$index + 1] ? ltrim($matches[$index + 1], '.-') : ''); + } + + if (!empty($matches[$index + 2])) { + $version .= '-dev'; + } + + return $version; + } + + // match dev branches + if (preg_match('{(.*?)[.-]?dev$}i', $version, $match)) { + try { + $normalized = $this->normalizeBranch($match[1]); + // a branch ending with -dev is only valid if it is numeric + // if it gets prefixed with dev- it means the branch name should + // have had a dev- prefix already when passed to normalize + if (strpos($normalized, 'dev-') === false) { + return $normalized; + } + } catch (\Exception $e) { + } + } + + $extraMessage = ''; + if (preg_match('{ +as +' . preg_quote($version) . '(?:@(?:'.self::$stabilitiesRegex.'))?$}', $fullVersion)) { + $extraMessage = ' in "' . $fullVersion . '", the alias must be an exact version'; + } elseif (preg_match('{^' . preg_quote($version) . '(?:@(?:'.self::$stabilitiesRegex.'))? +as +}', $fullVersion)) { + $extraMessage = ' in "' . $fullVersion . '", the alias source must be an exact version, if it is a branch name you should prefix it with dev-'; + } + + throw new \UnexpectedValueException('Invalid version string "' . $origVersion . '"' . $extraMessage); + } + + /** + * Extract numeric prefix from alias, if it is in numeric format, suitable for version comparison. + * + * @param string $branch Branch name (e.g. 2.1.x-dev) + * + * @return string|false Numeric prefix if present (e.g. 2.1.) or false + */ + public function parseNumericAliasPrefix($branch) + { + if (preg_match('{^(?P(\d++\\.)*\d++)(?:\.x)?-dev$}i', (string) $branch, $matches)) { + return $matches['version'] . '.'; + } + + return false; + } + + /** + * Normalizes a branch name to be able to perform comparisons on it. + * + * @param string $name + * + * @return string + */ + public function normalizeBranch($name) + { + $name = trim((string) $name); + + if (preg_match('{^v?(\d++)(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?$}i', $name, $matches)) { + $version = ''; + for ($i = 1; $i < 5; ++$i) { + $version .= isset($matches[$i]) ? str_replace(array('*', 'X'), 'x', $matches[$i]) : '.x'; + } + + return str_replace('x', '9999999', $version) . '-dev'; + } + + return 'dev-' . $name; + } + + /** + * Normalizes a default branch name (i.e. master on git) to 9999999-dev. + * + * @param string $name + * + * @return string + * + * @deprecated No need to use this anymore in theory, Composer 2 does not normalize any branch names to 9999999-dev anymore + */ + public function normalizeDefaultBranch($name) + { + if ($name === 'dev-master' || $name === 'dev-default' || $name === 'dev-trunk') { + return '9999999-dev'; + } + + return (string) $name; + } + + /** + * Parses a constraint string into MultiConstraint and/or Constraint objects. + * + * @param string $constraints + * + * @return ConstraintInterface + */ + public function parseConstraints($constraints) + { + $prettyConstraint = (string) $constraints; + + $orConstraints = preg_split('{\s*\|\|?\s*}', trim((string) $constraints)); + if (false === $orConstraints) { + throw new \RuntimeException('Failed to preg_split string: '.$constraints); + } + $orGroups = array(); + + foreach ($orConstraints as $constraints) { + $andConstraints = preg_split('{(?< ,]) *(? 1) { + $constraintObjects = array(); + foreach ($andConstraints as $constraint) { + foreach ($this->parseConstraint($constraint) as $parsedConstraint) { + $constraintObjects[] = $parsedConstraint; + } + } + } else { + $constraintObjects = $this->parseConstraint($andConstraints[0]); + } + + if (1 === \count($constraintObjects)) { + $constraint = $constraintObjects[0]; + } else { + $constraint = new MultiConstraint($constraintObjects); + } + + $orGroups[] = $constraint; + } + + $constraint = MultiConstraint::create($orGroups, false); + + $constraint->setPrettyString($prettyConstraint); + + return $constraint; + } + + /** + * @param string $constraint + * + * @throws \UnexpectedValueException + * + * @return array + * + * @phpstan-return non-empty-array + */ + private function parseConstraint($constraint) + { + // strip off aliasing + if (preg_match('{^([^,\s]++) ++as ++([^,\s]++)$}', $constraint, $match)) { + $constraint = $match[1]; + } + + // strip @stability flags, and keep it for later use + if (preg_match('{^([^,\s]*?)@(' . self::$stabilitiesRegex . ')$}i', $constraint, $match)) { + $constraint = '' !== $match[1] ? $match[1] : '*'; + if ($match[2] !== 'stable') { + $stabilityModifier = $match[2]; + } + } + + // get rid of #refs as those are used by composer only + if (preg_match('{^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#.+$}i', $constraint, $match)) { + $constraint = $match[1]; + } + + if (preg_match('{^(v)?[xX*](\.[xX*])*$}i', $constraint, $match)) { + if (!empty($match[1]) || !empty($match[2])) { + return array(new Constraint('>=', '0.0.0.0-dev')); + } + + return array(new MatchAllConstraint()); + } + + $versionRegex = 'v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.(\d++))?(?:' . self::$modifierRegex . '|\.([xX*][.-]?dev))(?:\+[^\s]+)?'; + + // Tilde Range + // + // Like wildcard constraints, unsuffixed tilde constraints say that they must be greater than the previous + // version, to ensure that unstable instances of the current version are allowed. However, if a stability + // suffix is added to the constraint, then a >= match on the current version is used instead. + if (preg_match('{^~>?' . $versionRegex . '$}i', $constraint, $matches)) { + if (strpos($constraint, '~>') === 0) { + throw new \UnexpectedValueException( + 'Could not parse version constraint ' . $constraint . ': ' . + 'Invalid operator "~>", you probably meant to use the "~" operator' + ); + } + + // Work out which position in the version we are operating at + if (isset($matches[4]) && '' !== $matches[4] && null !== $matches[4]) { + $position = 4; + } elseif (isset($matches[3]) && '' !== $matches[3] && null !== $matches[3]) { + $position = 3; + } elseif (isset($matches[2]) && '' !== $matches[2] && null !== $matches[2]) { + $position = 2; + } else { + $position = 1; + } + + // when matching 2.x-dev or 3.0.x-dev we have to shift the second or third number, despite no second/third number matching above + if (!empty($matches[8])) { + $position++; + } + + // Calculate the stability suffix + $stabilitySuffix = ''; + if (empty($matches[5]) && empty($matches[7]) && empty($matches[8])) { + $stabilitySuffix .= '-dev'; + } + + $lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1)); + $lowerBound = new Constraint('>=', $lowVersion); + + // For upper bound, we increment the position of one more significance, + // but highPosition = 0 would be illegal + $highPosition = max(1, $position - 1); + $highVersion = $this->manipulateVersionString($matches, $highPosition, 1) . '-dev'; + $upperBound = new Constraint('<', $highVersion); + + return array( + $lowerBound, + $upperBound, + ); + } + + // Caret Range + // + // Allows changes that do not modify the left-most non-zero digit in the [major, minor, patch] tuple. + // In other words, this allows patch and minor updates for versions 1.0.0 and above, patch updates for + // versions 0.X >=0.1.0, and no updates for versions 0.0.X + if (preg_match('{^\^' . $versionRegex . '($)}i', $constraint, $matches)) { + // Work out which position in the version we are operating at + if ('0' !== $matches[1] || '' === $matches[2] || null === $matches[2]) { + $position = 1; + } elseif ('0' !== $matches[2] || '' === $matches[3] || null === $matches[3]) { + $position = 2; + } else { + $position = 3; + } + + // Calculate the stability suffix + $stabilitySuffix = ''; + if (empty($matches[5]) && empty($matches[7]) && empty($matches[8])) { + $stabilitySuffix .= '-dev'; + } + + $lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1)); + $lowerBound = new Constraint('>=', $lowVersion); + + // For upper bound, we increment the position of one more significance, + // but highPosition = 0 would be illegal + $highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev'; + $upperBound = new Constraint('<', $highVersion); + + return array( + $lowerBound, + $upperBound, + ); + } + + // X Range + // + // Any of X, x, or * may be used to "stand in" for one of the numeric values in the [major, minor, patch] tuple. + // A partial version range is treated as an X-Range, so the special character is in fact optional. + if (preg_match('{^v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.[xX*])++$}', $constraint, $matches)) { + if (isset($matches[3]) && '' !== $matches[3] && null !== $matches[3]) { + $position = 3; + } elseif (isset($matches[2]) && '' !== $matches[2] && null !== $matches[2]) { + $position = 2; + } else { + $position = 1; + } + + $lowVersion = $this->manipulateVersionString($matches, $position) . '-dev'; + $highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev'; + + if ($lowVersion === '0.0.0.0-dev') { + return array(new Constraint('<', $highVersion)); + } + + return array( + new Constraint('>=', $lowVersion), + new Constraint('<', $highVersion), + ); + } + + // Hyphen Range + // + // Specifies an inclusive set. If a partial version is provided as the first version in the inclusive range, + // then the missing pieces are replaced with zeroes. If a partial version is provided as the second version in + // the inclusive range, then all versions that start with the supplied parts of the tuple are accepted, but + // nothing that would be greater than the provided tuple parts. + if (preg_match('{^(?P' . $versionRegex . ') +- +(?P' . $versionRegex . ')($)}i', $constraint, $matches)) { + // Calculate the stability suffix + $lowStabilitySuffix = ''; + if (empty($matches[6]) && empty($matches[8]) && empty($matches[9])) { + $lowStabilitySuffix = '-dev'; + } + + $lowVersion = $this->normalize($matches['from']); + $lowerBound = new Constraint('>=', $lowVersion . $lowStabilitySuffix); + + $empty = function ($x) { + return ($x === 0 || $x === '0') ? false : empty($x); + }; + + if ((!$empty($matches[12]) && !$empty($matches[13])) || !empty($matches[15]) || !empty($matches[17]) || !empty($matches[18])) { + $highVersion = $this->normalize($matches['to']); + $upperBound = new Constraint('<=', $highVersion); + } else { + $highMatch = array('', $matches[11], $matches[12], $matches[13], $matches[14]); + + // validate to version + $this->normalize($matches['to']); + + $highVersion = $this->manipulateVersionString($highMatch, $empty($matches[12]) ? 1 : 2, 1) . '-dev'; + $upperBound = new Constraint('<', $highVersion); + } + + return array( + $lowerBound, + $upperBound, + ); + } + + // Basic Comparators + if (preg_match('{^(<>|!=|>=?|<=?|==?)?\s*(.*)}', $constraint, $matches)) { + try { + try { + $version = $this->normalize($matches[2]); + } catch (\UnexpectedValueException $e) { + // recover from an invalid constraint like foobar-dev which should be dev-foobar + // except if the constraint uses a known operator, in which case it must be a parse error + if (substr($matches[2], -4) === '-dev' && preg_match('{^[0-9a-zA-Z-./]+$}', $matches[2])) { + $version = $this->normalize('dev-'.substr($matches[2], 0, -4)); + } else { + throw $e; + } + } + + $op = $matches[1] ?: '='; + + if ($op !== '==' && $op !== '=' && !empty($stabilityModifier) && self::parseStability($version) === 'stable') { + $version .= '-' . $stabilityModifier; + } elseif ('<' === $op || '>=' === $op) { + if (!preg_match('/-' . self::$modifierRegex . '$/', strtolower($matches[2]))) { + if (strpos($matches[2], 'dev-') !== 0) { + $version .= '-dev'; + } + } + } + + return array(new Constraint($matches[1] ?: '=', $version)); + } catch (\Exception $e) { + } + } + + $message = 'Could not parse version constraint ' . $constraint; + if (isset($e)) { + $message .= ': ' . $e->getMessage(); + } + + throw new \UnexpectedValueException($message); + } + + /** + * Increment, decrement, or simply pad a version number. + * + * Support function for {@link parseConstraint()} + * + * @param array $matches Array with version parts in array indexes 1,2,3,4 + * @param int $position 1,2,3,4 - which segment of the version to increment/decrement + * @param int $increment + * @param string $pad The string to pad version parts after $position + * + * @return string|null The new version + * + * @phpstan-param string[] $matches + */ + private function manipulateVersionString(array $matches, $position, $increment = 0, $pad = '0') + { + for ($i = 4; $i > 0; --$i) { + if ($i > $position) { + $matches[$i] = $pad; + } elseif ($i === $position && $increment) { + $matches[$i] += $increment; + // If $matches[$i] was 0, carry the decrement + if ($matches[$i] < 0) { + $matches[$i] = $pad; + --$position; + + // Return null on a carry overflow + if ($i === 1) { + return null; + } + } + } + } + + return $matches[1] . '.' . $matches[2] . '.' . $matches[3] . '.' . $matches[4]; + } + + /** + * Expand shorthand stability string to long version. + * + * @param string $stability + * + * @return string + */ + private function expandStability($stability) + { + $stability = strtolower($stability); + + switch ($stability) { + case 'a': + return 'alpha'; + case 'b': + return 'beta'; + case 'p': + case 'pl': + return 'patch'; + case 'rc': + return 'RC'; + default: + return $stability; + } + } +} diff --git a/msd/vendor/desarrolla2/cache/.formatter.yml b/msd/vendor/desarrolla2/cache/.formatter.yml new file mode 100644 index 0000000..cc460ed --- /dev/null +++ b/msd/vendor/desarrolla2/cache/.formatter.yml @@ -0,0 +1,18 @@ +use-sort: + group: + - _main + group-type: each + sort-type: alph + sort-direction: asc + +header: | + /* + * This file is part of the Cache package. + * + * Copyright (c) Daniel González + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @author Daniel González + */ diff --git a/msd/vendor/desarrolla2/cache/.github/workflows/php.yml b/msd/vendor/desarrolla2/cache/.github/workflows/php.yml new file mode 100644 index 0000000..a411fa9 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/.github/workflows/php.yml @@ -0,0 +1,73 @@ +name: PHP + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + run: + runs-on: ubuntu-latest + services: + mysql: + image: mysql:5.7 + ports: + - 3306:3306 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + redis: + image: redis:6.0 + ports: + - 6379:6379 + mongo: + image: mongo:4.2-bionic + ports: + - 27017:27017 + memcached: + image: memcached:1.6 + ports: + - 11211:11211 + + strategy: + fail-fast: false + matrix: + include: + - php: 7.2 + composer: '--prefer-lowest' + desc: "Lowest versions" + - php: 7.4 + composer: '--prefer-lowest' + desc: "Lowest versions" + - php: 7.2 + - php: 7.3 + - php: 7.4 + coverage: '--coverage-clover /tmp/clover.xml' + - php: 8.0 + name: PHP ${{ matrix.php }} ${{ matrix.desc }} + + steps: + - uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: xdebug + extensions: apcu, mongodb, memcached + ini-values: apc.enable_cli=1,mysqli.default_host=127.0.0.1,mysqli.default_port=3306,mysqli.default_user=root + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Install dependencies + run: composer update --prefer-dist --no-progress ${{ matrix.composer }} + + - name: Run PHPUnit + run: vendor/bin/phpunit ${{ matrix.coverage }} + + - name: Upload coverage to Scrutinizer + if: ${{ matrix.coverage }} + run: > + wget https://scrutinizer-ci.com/ocular.phar -O "/tmp/ocular.phar" && + php "/tmp/ocular.phar" code-coverage:upload --format=php-clover /tmp/clover.xml diff --git a/msd/vendor/desarrolla2/cache/.gitignore b/msd/vendor/desarrolla2/cache/.gitignore new file mode 100644 index 0000000..f2067af --- /dev/null +++ b/msd/vendor/desarrolla2/cache/.gitignore @@ -0,0 +1,9 @@ +/build +/composer.lock +/tests/config.json +/vendor +/.idea +/TODO.md +*~ +*.swp +/.phpunit.result.cache \ No newline at end of file diff --git a/msd/vendor/desarrolla2/cache/.scrutinizer.yml b/msd/vendor/desarrolla2/cache/.scrutinizer.yml new file mode 100644 index 0000000..581cec7 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/.scrutinizer.yml @@ -0,0 +1,31 @@ +#language: php +checks: + php: true +filter: + excluded_paths: + - tests +build: + nodes: + analysis: + environment: + php: + version: 7.4 + pecl_extensions: + - apcu + - mongodb + - memcached + mysql: false + postgresql: false + redis: false + mongodb: false + tests: + override: + - phpcs-run src + - + command: vendor/bin/phpstan analyze --error-format=checkstyle | sed '/^\s*$/d' > phpstan-checkstyle.xml + analysis: + file: phpstan-checkstyle.xml + format: 'general-checkstyle' + - php-scrutinizer-run +tools: + external_code_coverage: true diff --git a/msd/vendor/desarrolla2/cache/LICENSE b/msd/vendor/desarrolla2/cache/LICENSE new file mode 100644 index 0000000..99ad990 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012-2013 Desarrolla2 - http://desarrolla2.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/msd/vendor/desarrolla2/cache/README.md b/msd/vendor/desarrolla2/cache/README.md new file mode 100644 index 0000000..b71bfaf --- /dev/null +++ b/msd/vendor/desarrolla2/cache/README.md @@ -0,0 +1,174 @@ +# Desarolla2 Cache + +A **simple cache** library, implementing the [PSR-16](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-16-simple-cache.md) standard using **immutable** objects. + +![life-is-hard-cache-is](https://user-images.githubusercontent.com/100821/41566888-ecd60cde-735d-11e8-893f-da42b2cd65e7.jpg) + +Caching is typically used throughout an applicatiton. Immutability ensure that modifying the cache behaviour in one +location doesn't result in unexpected behaviour due to changes in unrelated code. + +_Desarolla2 Cache aims to be the most complete, correct and best performing PSR-16 implementation available._ + +[![Latest version][ico-version]][link-packagist] +[![Latest version][ico-pre-release]][link-packagist] +[![Software License][ico-license]][link-license] +[![Build Status][ico-github-actions]][link-github-actions] +[![Coverage Status][ico-coverage]][link-scrutinizer] +[![Quality Score][ico-code-quality]][link-scrutinizer] +[![Total Downloads][ico-downloads]][link-downloads] +[![Today Downloads][ico-today-downloads]][link-downloads] +[![Gitter][ico-gitter]][link-gitter] + + +## Installation + +``` +composer require desarrolla2/cache +``` + +## Usage + + +``` php +use Desarrolla2\Cache\Memory as Cache; + +$cache = new Cache(); + +$value = $cache->get('key'); + +if (!isset($value)) { + $value = do_something(); + $cache->set('key', $value, 3600); +} + +echo $value; +``` + +## Adapters + +* [Apcu](docs/implementations/apcu.md) +* [File](docs/implementations/file.md) +* [Memcached](docs/implementations/memcached.md) +* [Memory](docs/implementations/memory.md) +* [MongoDB](docs/implementations/mongodb.md) +* [Mysqli](docs/implementations/mysqli.md) +* [NotCache](docs/implementations/notcache.md) +* [PhpFile](docs/implementations/phpfile.md) +* [Predis](docs/implementations/predis.md) + +The following implementation allows you to combine cache adapters. + +* [Chain](docs/implementations/chain.md) + +[Other implementations][todo-implementations] are planned. Please vote or +provide a PR to speed up the process of adding the to this library. + +[todo-implementations]: https://github.com/desarrolla2/Cache/issues?q=is%3Aissue+is%3Aopen+label%3Aadapter + +### Options + +You can set options for cache using the `withOption` or `withOptions` method. +Note that all cache objects are immutable, setting an option creates a new +object. + +#### TTL + +All cache implementations support the `ttl` option. This sets the default +time (in seconds) that cache will survive. It defaults to one hour (3600 +seconds). + +Setting the TTL to 0 or a negative number, means the cache should live forever. + +## Methods + +Each cache implementation has the following `Psr\SimpleCache\CacheInterface` +methods: + +##### `get(string $key [, mixed $default])` +Retrieve the value corresponding to a provided key + +##### `has(string $key)` +Retrieve the if value corresponding to a provided key exist + +##### `set(string $key, mixed $value [, int $ttl])` +Add a value to the cache under a unique key + +##### `delete(string $key)` +Delete a value from the cache + +##### `clear()` +Clear all cache + +##### `getMultiple(array $keys)` +Obtains multiple cache items by their unique keys + +##### `setMultiple(array $values [, int $ttl])` +Persists a set of key => value pairs in the cache + +##### `deleteMultiple(array $keys)` +Deletes multiple cache items in a single operation + +. + +The `Desarrolla2\Cache\CacheInterface` also has the following methods: + +##### `withOption(string $key, string $value)` +Set option for implementation. Creates a new instance. + +##### `withOptions(array $options)` +Set multiple options for implementation. Creates a new instance. + +##### `getOption(string $key)` +Get option for implementation. + + +## Packers + +Cache objects typically hold a `Desarrolla2\Cache\Packer\PackerInterface` +object. By default, packing is done using `serialize` and `unserialize`. + +Available packers are: + +* `SerializePacker` using `serialize` and `unserialize` +* `JsonPacker` using `json_encode` and `json_decode` +* `NopPacker` does no packing +* `MongoDBBinaryPacker` using `serialize` and `unserialize` to store as [BSON Binary](http://php.net/manual/en/class.mongodb-bson-binary.php) + +#### PSR-16 incompatible packers + +The `JsonPacker` does not fully comply with PSR-16, as packing and +unpacking an object will probably not result in an object of the same class. + +The `NopPacker` is intended when caching string data only (like HTML output) or +if the caching backend supports structured data. Using it when storing objects +will might give unexpected results. + +## Contributors + +[![Daniel González](https://avatars1.githubusercontent.com/u/661529?v=3&s=80)](https://github.com/desarrolla2) +Twitter: [@desarrolla2](https://twitter.com/desarrolla2)\ +[![Arnold Daniels](https://avatars3.githubusercontent.com/u/100821?v=3&s=80)](https://github.com/jasny) +Twitter: [@ArnoldDaniels](https://twitter.com/ArnoldDaniels) + +[ico-version]: https://img.shields.io/packagist/v/desarrolla2/Cache.svg?style=flat-square +[ico-pre-release]: https://img.shields.io/packagist/vpre/desarrolla2/Cache.svg?style=flat-square +[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square +[ico-travis]: https://img.shields.io/travis/desarrolla2/Cache/master.svg?style=flat-square +[ico-coveralls]: https://img.shields.io/coveralls/desarrolla2/Cache/master.svg?style=flat-square +[ico-code-quality]: https://img.shields.io/scrutinizer/g/desarrolla2/cache.svg?style=flat-square +[ico-coverage]: https://scrutinizer-ci.com/g/desarrolla2/Cache/badges/coverage.png?b=master +[ico-sensiolabs]: https://img.shields.io/sensiolabs/i/5f139261-1ac1-4559-846a-723e09319a88.svg?style=flat-square +[ico-downloads]: https://img.shields.io/packagist/dt/desarrolla2/cache.svg?style=flat-square +[ico-today-downloads]: https://img.shields.io/packagist/dd/desarrolla2/cache.svg?style=flat-square +[ico-gitter]: https://img.shields.io/badge/GITTER-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat-square +[ico-github-actions]: https://github.com/desarrolla2/Cache/workflows/PHP/badge.svg + +[link-packagist]: https://packagist.org/packages/desarrolla2/cache +[link-license]: http://hassankhan.mit-license.org +[link-travis]: https://travis-ci.org/desarrolla2/Cache +[link-github-actions]: https://github.com/desarrolla2/Cache/actions +[link-coveralls]: https://coveralls.io/github/desarrolla2/Cache +[link-scrutinizer]: https://scrutinizer-ci.com/g/desarrolla2/cache +[link-sensiolabs]: https://insight.sensiolabs.com/projects/5f139261-1ac1-4559-846a-723e09319a88 +[link-downloads]: https://packagist.org/packages/desarrolla2/cache +[link-gitter]: https://gitter.im/desarrolla2/Cache?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge diff --git a/msd/vendor/desarrolla2/cache/build.xml b/msd/vendor/desarrolla2/cache/build.xml new file mode 100644 index 0000000..c303f73 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/build.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msd/vendor/desarrolla2/cache/composer.json b/msd/vendor/desarrolla2/cache/composer.json new file mode 100644 index 0000000..71b858f --- /dev/null +++ b/msd/vendor/desarrolla2/cache/composer.json @@ -0,0 +1,67 @@ +{ + "name": "desarrolla2/cache", + "description": "Provides an cache interface for several adapters Apc, Apcu, File, Mongo, Memcache, Memcached, Mysql, Mongo, Redis is supported.", + "keywords": [ + "cache", + "simple-cache", + "psr-16", + "apc", + "apcu", + "file", + "memcached", + "memcache", + "mysql", + "mongo", + "redis" + ], + "type": "library", + "license": "MIT", + "homepage": "https://github.com/desarrolla2/Cache/", + "authors": [ + { + "name": "Daniel González", + "homepage": "http://desarrolla2.com/" + }, + { + "name": "Arnold Daniels", + "homepage": "https://jasny.net/" + } + ], + "provide": { + "psr/simple-cache-implementation": "1.0" + }, + "require": { + "php": ">=7.2.0", + "psr/simple-cache": "^1.0" + }, + "require-dev": { + "ext-apcu": "*", + "ext-json": "*", + "ext-mysqli": "*", + "ext-memcached": "*", + "predis/predis": "~1.0.0", + "mongodb/mongodb": "^1.3", + "cache/integration-tests": "dev-master", + "phpunit/phpunit": "^8.3 || ^9.0", + "phpstan/phpstan": "^0.12.29", + "symfony/phpunit-bridge": "^5.2", + "mikey179/vfsstream": "v1.6.8" + }, + "autoload": { + "psr-4": { + "Desarrolla2\\Cache\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Desarrolla2\\Test\\Cache\\": "tests/" + } + }, + "scripts": { + "test": [ + "phpstan analyse", + "phpunit --colors=always", + "phpcs -p src" + ] + } +} diff --git a/msd/vendor/desarrolla2/cache/docs/implementations/apcu.md b/msd/vendor/desarrolla2/cache/docs/implementations/apcu.md new file mode 100644 index 0000000..711f42d --- /dev/null +++ b/msd/vendor/desarrolla2/cache/docs/implementations/apcu.md @@ -0,0 +1,25 @@ +# Apcu + +Use [APCu cache](http://php.net/manual/en/book.apcu.php) to cache to shared +memory. + +``` php +use Desarrolla2\Cache\Apcu as ApcuCache; + +$cache = new ApcuCache(); +``` + +_Note: by default APCu uses the time at the beginning of a request for ttl. In +some cases, like with a long running script, this can be a problem. You can +change this behaviour `ini_set('apc.use_request_time', false)`._ + +### Options + +| name | type | default | | +| --------- | ---- | ------- | ------------------------------------- | +| ttl | int | null | Maximum time to live in seconds | +| prefix | string | "" | Key prefix | + +### Packer + +By default the [`NopPacker`](../packers/nop.md) is used. diff --git a/msd/vendor/desarrolla2/cache/docs/implementations/chain.md b/msd/vendor/desarrolla2/cache/docs/implementations/chain.md new file mode 100644 index 0000000..4aa5c11 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/docs/implementations/chain.md @@ -0,0 +1,37 @@ +# Chain + +The Cache chain allows you to use multiple implementations to store cache. For +instance, you can use both fast volatile (in-memory) storage and slower +non-volatile (disk) storage. Alternatively you can have a local storage +as well as a shared storage service. + +``` php +use Desarrolla2\Cache\Chain as CacheChain; +use Desarrolla2\Cache\Memory as MemoryCache; +use Desarrolla2\Cache\Predis as PredisCache; + +$cache = new CacheChain([ + (new MemoryCache())->withOption('ttl', 3600), + (new PredisCache())->withOption('ttl', 10800) +]); +``` + +The Chain cache implementation doesn't use any option. It uses the `Nop` packer +by default. + +Typically it's useful to specify a maximum `ttl` for each implementation. This +means that the volatile memory only holds items that are used often. + +The following actions propogate to all cache adapters in the chain + +* `set` +* `setMultiple` +* `delete` +* `deleteMultiple` +* `clear` + +For the following actions all nodes are tried in sequence + +* `has` +* `get` +* `getMultiple` \ No newline at end of file diff --git a/msd/vendor/desarrolla2/cache/docs/implementations/file.md b/msd/vendor/desarrolla2/cache/docs/implementations/file.md new file mode 100644 index 0000000..1902f23 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/docs/implementations/file.md @@ -0,0 +1,82 @@ +# File + +Save the cache as file to on the filesystem. + +You must pass a cache directory to the constructor. + +``` php +use Desarrolla2\Cache\File as FileCache; + +$cache = new FileCache(sys_get_temp_dir() . '/cache'); +``` + +### Options + +| name | type | default | | +| ------------ | --------------------------------- | -------------- | ------------------------------------- | +| ttl | int | null | Maximum time to live in seconds | +| ttl-strategy | string ('embed', 'file', 'mtime') | "embed" | Strategy to store the TTL | +| prefix | string | "" | Key prefix | +| filename | string or callable | "%s.php.cache" | Filename as sprintf format | + +#### TTL strategy option + +The ttl strategy determines how the TTL is stored. Typical filesystems don't +allow custom file properties, so we'll have to use one of these strategies: + +| strategy | | +| -------- | ----------------------------------------------- | +| embed | Embed the TTL as first line of the file | +| file | Create a TTL file in addition to the cache file | +| mtime | Use [mtime][] + max ttl | + +The 'mtime' strategy is not PSR-16 compliant, as the TTL passed to the `set()` +method is ignored. Only the `ttl` option for is used on `get()` and `has()`. + +[mtime]: https://www.unixtutorial.org/2008/04/atime-ctime-mtime-in-unix-filesystems/ + +#### Filename option + +The `filename` will be parsed using `sprintf` where '%s' is substituted with +the key. The default extension is automatically determined based on the +packer. + +Instead of a string, `filename` may also be set to a callable, like a callable +object or closure. In that case the callable will be called to create a +filename as + + $filename = $callable($key); + +##### BasicFilename + +The library comes with invokable object as callable for the filename. The +`BasicFilename` object works as described above. + +##### TrieFilename + +The `TrieFilename` object will create a prefix tree directory structure. This +is useful where a lot of cache files would cause to many files in a directory. + +Specify the `sprintf` format and the directory level to the constructor when +creating a `TrieFilename` object. + +``` php +use Desarrolla2\Cache\File as FileCache; +use Desarrolla2\Cache\File\TrieFilename; + +$callback = new TrieFilename('%s.php.cache', 2); + +$cache = (new FileCache(sys_get_temp_dir() . '/cache')) + ->withOption('filename', $callback); +``` + +In this case, adding an item with key `foobar` would be create a file at + + /tmp/cache/f/fo/foobar.php.cache + +### Packer + +By default the [`SerializePacker`](../packers/serialize.md) is used. The +[`NopPacker`](../packers/nop.md) can be used if the values are strings. +Other packers, like the [`JsonPacker`](../packers/json.md) are also +useful with file cache. diff --git a/msd/vendor/desarrolla2/cache/docs/implementations/memcached.md b/msd/vendor/desarrolla2/cache/docs/implementations/memcached.md new file mode 100644 index 0000000..8e00eb0 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/docs/implementations/memcached.md @@ -0,0 +1,28 @@ +# Memcached + +Store cache to [Memcached](https://memcached.org/). Memcached is a high +performance distributed caching system. + +``` php +use Desarrolla2\Cache\Memcached as MemcachedCache; +use Memcached; + +$server = new Memcached(); +// configure it here + +$cache = new MemcachedCache($server); +``` + +This implementation uses the [memcached](https://php.net/memcached) php +extension. The (alternative) memcache extension is not supported. + +### Options + +| name | type | default | | +| --------- | ---- | ------- | ------------------------------------- | +| ttl | int | null | Maximum time to live in seconds | +| prefix | string | "" | Key prefix | + +### Packer + +By default the [`NopPacker`](../packers/nop.md) is used. diff --git a/msd/vendor/desarrolla2/cache/docs/implementations/memory.md b/msd/vendor/desarrolla2/cache/docs/implementations/memory.md new file mode 100644 index 0000000..03d1262 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/docs/implementations/memory.md @@ -0,0 +1,23 @@ +# Memory + +Store the cache in process memory _(in other words in an array)_. Cache Memory +is removed when the PHP process exist. Also it is not shared between different +processes. + +``` php +use Desarrolla2\Cache\Memory as MemoryCache; + +$cache = new MemoryCache(); +``` + +### Options + +| name | type | default | | +| --------- | ---- | ------- | ------------------------------------- | +| ttl | int | null | Maximum time to live in seconds | +| limit | int | null | Maximum items in cache | +| prefix | string | "" | Key prefix | + +### Packer + +By default the [`SerializePacker`](../packers/serialize.md) is used. diff --git a/msd/vendor/desarrolla2/cache/docs/implementations/mongodb.md b/msd/vendor/desarrolla2/cache/docs/implementations/mongodb.md new file mode 100644 index 0000000..3bad03d --- /dev/null +++ b/msd/vendor/desarrolla2/cache/docs/implementations/mongodb.md @@ -0,0 +1,45 @@ +# Mongo + +Use it to store the cache in a Mongo database. Requires the mongodb extension +and the [mongodb/mongodb](https://github.com/mongodb/mongo-php-library) +library. + +You must pass a `MongoDB\Collection` object to the cache constructor. + +``` php +selectDatabase('mycache'); +$collection = $database->selectCollection('cache'); + +$cache = new MongoCache($collection); +``` + +MonoDB will always automatically create the database and collection if needed. + +### Options + +| name | type | default | | +| --------- | ---- | ------- | ------------------------------------- | +| initialize | bool | true | Enable auto-initialize | +| ttl | int | null | Maximum time to live in seconds | +| prefix | string | "" | Key prefix | + +#### Initialize option + +If `initialize` is enabled, the cache implementation will automatically create +a [ttl index](https://docs.mongodb.com/manual/core/index-ttl/). In production +it's better to disable auto-initialization and create the ttl index explicitly +when setting up the database. This prevents a `createIndex()` call on each +request. + +### Packer + +By default the [`MongoDBBinaryPacker`](../packers/mongodbbinary.md) is used. It +serializes the data and stores it in a [Binary BSON variable](http://php.net/manual/en/class.mongodb-bson-binary.php). +If the data is a UTF-8 string of simple array or stdClass object, it may be +useful to use the [`NopPacker`](../packers/nop.md) instead. diff --git a/msd/vendor/desarrolla2/cache/docs/implementations/mysqli.md b/msd/vendor/desarrolla2/cache/docs/implementations/mysqli.md new file mode 100644 index 0000000..df676e3 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/docs/implementations/mysqli.md @@ -0,0 +1,47 @@ +# Mysqli + +Cache to a [MySQL database](https://www.mysql.com/) using the +[mysqli](http://php.net/manual/en/book.mysqli.php) PHP extension. + +You must pass a `mysqli` connection object to the constructor. + +``` php +withOption('filename', $callback); +``` + +In this case, adding an item with key `foobar` would be create a file at + + /tmp/cache/f/fo/foobar.php + +### Packer + +By default the [`NopPacker`](../packers/nop.md) is used. Other packers should +not be used. + +[read more]: https://medium.com/@dylanwenzlau/500x-faster-caching-than-redis-memcache-apc-in-php-hhvm-dcd26e8447ad diff --git a/msd/vendor/desarrolla2/cache/docs/implementations/predis.md b/msd/vendor/desarrolla2/cache/docs/implementations/predis.md new file mode 100644 index 0000000..d29fa96 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/docs/implementations/predis.md @@ -0,0 +1,31 @@ +# Predis + +Cache using a [redis server](https://redis.io/). Redis is an open source, +in-memory data structure store, used as a database, cache and message broker. + +You must provide a `Predis\Client` object to the constructor. + +```php +use Desarrolla2\Cache\Predis as PredisCache; +use Predis\Client as PredisClient; + +$client = new PredisClient('tcp://localhost:6379'); +$cache = new PredisCache($client); +``` + +### Installation + +Requires the [`predis`](https://github.com/nrk/predis/wiki) library. + + composer require predis/predis + +### Options + +| name | type | default | | +| --------- | ---- | ------- | ------------------------------------- | +| ttl | int | null | Maximum time to live in seconds | +| prefix | string | "" | Key prefix | + +### Packer + +By default the [`SerializePacker`](../packers/serialize.md) is used. diff --git a/msd/vendor/desarrolla2/cache/docs/performance.md b/msd/vendor/desarrolla2/cache/docs/performance.md new file mode 100644 index 0000000..4b01b2e --- /dev/null +++ b/msd/vendor/desarrolla2/cache/docs/performance.md @@ -0,0 +1,40 @@ +# Performance test + +Here are my performance tests, you can view the results ordered from faster to slower. + +| Adapter | 10.000 set | 10.000 has | 10.000 get | +| :-------------- | -----------: | -----------: | ---------: | +| NoCache | 0.0637 | 0.0482 | 0.0488 | +| Apcu | 0.0961 | 0.0556 | 0.0770 | +| File | 0.6881 | 0.3426 | 0.3107 | +| Mongo | 13.8144 | 30.0203 | 24.4214 | + + +## how i run the test? + +The test its the same for all Adapters and look like this. + +``` php + set(md5($i), md5($i), 3600); + } + $timer->mark('10.000 set'); + for ($i = 1; $i <= 10000; $i++) { + $cache->has(md5($i)); + } + $timer->mark('10.000 has'); + for ($i = 1; $i <= 10000; $i++) { + $cache->get(md5($i)); + } + $timer->mark('10.000 get'); + +``` + + if you want run the tests them execute. + +``` sh + php test/performance/AdapterName.php +``` \ No newline at end of file diff --git a/msd/vendor/desarrolla2/cache/phpcs.xml b/msd/vendor/desarrolla2/cache/phpcs.xml new file mode 100644 index 0000000..7f2b9e7 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/phpcs.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/msd/vendor/desarrolla2/cache/phpstan.neon b/msd/vendor/desarrolla2/cache/phpstan.neon new file mode 100644 index 0000000..b38d126 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/phpstan.neon @@ -0,0 +1,8 @@ +parameters: + level: 3 + paths: + - src + reportUnmatchedIgnoredErrors: false + ignoreErrors: + - /^Variable property access/ + diff --git a/msd/vendor/desarrolla2/cache/phpunit.xml.dist b/msd/vendor/desarrolla2/cache/phpunit.xml.dist new file mode 100644 index 0000000..c49dbde --- /dev/null +++ b/msd/vendor/desarrolla2/cache/phpunit.xml.dist @@ -0,0 +1,39 @@ + + + + + + ./tests + + + + + + ./src + + + + + + + + + + + + + + + + + diff --git a/msd/vendor/desarrolla2/cache/src/AbstractCache.php b/msd/vendor/desarrolla2/cache/src/AbstractCache.php new file mode 100644 index 0000000..e2864f9 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/AbstractCache.php @@ -0,0 +1,296 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache; + +use Desarrolla2\Cache\Option\PrefixTrait as PrefixOption; +use Desarrolla2\Cache\Option\TtlTrait as TtlOption; +use Desarrolla2\Cache\Packer\PackingTrait as Packing; +use Desarrolla2\Cache\Exception\InvalidArgumentException; +use DateTimeImmutable; +use DateInterval; +use Traversable; + +/** + * AbstractAdapter + */ +abstract class AbstractCache implements CacheInterface +{ + use PrefixOption; + use TtlOption; + use Packing; + + /** + * Make a clone of this object. + * + * @return static + */ + protected function cloneSelf(): self + { + return clone $this; + } + + /** + * {@inheritdoc} + */ + public function withOption(string $key, $value): self + { + return $this->withOptions([$key => $value]); + } + + /** + * {@inheritdoc} + */ + public function withOptions(array $options): self + { + $cache = $this->cloneSelf(); + + foreach ($options as $key => $value) { + $method = "set" . str_replace('-', '', $key) . "Option"; + + if (empty($key) || !method_exists($cache, $method)) { + throw new InvalidArgumentException("unknown option '$key'"); + } + + $cache->$method($value); + } + + return $cache; + } + + /** + * {@inheritdoc} + */ + public function getOption($key) + { + $method = "get" . str_replace('-', '', $key) . "Option"; + + if (empty($key) || !method_exists($this, $method)) { + throw new InvalidArgumentException("unknown option '$key'"); + } + + return $this->$method(); + } + + + /** + * Validate the key + * + * @param string $key + * @return void + * @throws InvalidArgumentException + */ + protected function assertKey($key): void + { + if (!is_string($key)) { + $type = (is_object($key) ? get_class($key) . ' ' : '') . gettype($key); + throw new InvalidArgumentException("Expected key to be a string, not $type"); + } + + if ($key === '' || preg_match('~[{}()/\\\\@:]~', $key)) { + throw new InvalidArgumentException("Invalid key '$key'"); + } + } + + /** + * Assert that the keys are an array or traversable + * + * @param iterable $subject + * @param string $msg + * @return void + * @throws InvalidArgumentException if subject are not iterable + */ + protected function assertIterable($subject, $msg): void + { + $iterable = function_exists('is_iterable') + ? is_iterable($subject) + : is_array($subject) || $subject instanceof Traversable; + + if (!$iterable) { + throw new InvalidArgumentException($msg); + } + } + + /** + * Turn the key into a cache identifier + * + * @param string $key + * @return string + * @throws InvalidArgumentException + */ + protected function keyToId($key): string + { + $this->assertKey($key); + + return sprintf('%s%s', $this->prefix, $key); + } + + /** + * Create a map with keys and ids + * + * @param iterable $keys + * @return array + * @throws InvalidArgumentException + */ + protected function mapKeysToIds($keys): array + { + $this->assertIterable($keys, 'keys not iterable'); + + $map = []; + + foreach ($keys as $key) { + $id = $this->keyToId($key); + $map[$id] = $key; + } + + return $map; + } + + + /** + * Pack all values and turn keys into ids + * + * @param iterable $values + * @return array + */ + protected function packValues(iterable $values): array + { + $packed = []; + + foreach ($values as $key => $value) { + $id = $this->keyToId(is_int($key) ? (string)$key : $key); + $packed[$id] = $this->pack($value); + } + + return $packed; + } + + + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + $this->assertIterable($keys, 'keys not iterable'); + + $result = []; + + foreach ($keys as $key) { + $result[$key] = $this->get($key, $default); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + $this->assertIterable($values, 'values not iterable'); + + $success = true; + + foreach ($values as $key => $value) { + $success = $this->set(is_int($key) ? (string)$key : $key, $value, $ttl) && $success; + } + + return $success; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + $this->assertIterable($keys, 'keys not iterable'); + + $success = true; + + foreach ($keys as $key) { + $success = $this->delete($key) && $success; + } + + return $success; + } + + + /** + * Get the current time. + * + * @return int + */ + protected function currentTimestamp(): int + { + return time(); + } + + /** + * Convert TTL to seconds from now + * + * @param null|int|DateInterval $ttl + * @return int|null + * @throws InvalidArgumentException + */ + protected function ttlToSeconds($ttl): ?int + { + if (!isset($ttl)) { + return $this->ttl; + } + + if ($ttl instanceof DateInterval) { + $reference = new DateTimeImmutable(); + $endTime = $reference->add($ttl); + + $ttl = $endTime->getTimestamp() - $reference->getTimestamp(); + } + + if (!is_int($ttl)) { + $type = (is_object($ttl) ? get_class($ttl) . ' ' : '') . gettype($ttl); + throw new InvalidArgumentException("ttl should be of type int or DateInterval, not $type"); + } + + return isset($this->ttl) ? min($ttl, $this->ttl) : $ttl; + } + + /** + * Convert TTL to epoch timestamp + * + * @param null|int|DateInterval $ttl + * @return int|null + * @throws InvalidArgumentException + */ + protected function ttlToTimestamp($ttl): ?int + { + if (!isset($ttl)) { + return isset($this->ttl) ? time() + $this->ttl : null; + } + + if (is_int($ttl)) { + return time() + (isset($this->ttl) ? min($ttl, $this->ttl) : $ttl); + } + + if ($ttl instanceof DateInterval) { + $timestamp = (new DateTimeImmutable())->add($ttl)->getTimestamp(); + + return isset($this->ttl) ? min($timestamp, time() + $this->ttl) : $timestamp; + } + + $type = (is_object($ttl) ? get_class($ttl) . ' ' : '') . gettype($ttl); + throw new InvalidArgumentException("ttl should be of type int or DateInterval, not $type"); + } +} diff --git a/msd/vendor/desarrolla2/cache/src/AbstractFile.php b/msd/vendor/desarrolla2/cache/src/AbstractFile.php new file mode 100644 index 0000000..ac215fb --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/AbstractFile.php @@ -0,0 +1,216 @@ + + * @author Arnold Daniels + */ + +namespace Desarrolla2\Cache; + +use Desarrolla2\Cache\Exception\InvalidArgumentException; +use Desarrolla2\Cache\Option\FilenameTrait as FilenameOption; + +/** + * Abstract class for using files as cache. + * + * @package Desarrolla2\Cache + */ +abstract class AbstractFile extends AbstractCache +{ + use FilenameOption; + + /** + * @var string + */ + protected $cacheDir; + + + /** + * Class constructor + * + * @param string|null $cacheDir + */ + public function __construct(?string $cacheDir = null) + { + if (!$cacheDir) { + $cacheDir = realpath(sys_get_temp_dir()) . DIRECTORY_SEPARATOR . 'cache'; + if(!is_dir($cacheDir)) { + mkdir($cacheDir, 0777, true); + } + } + + $this->cacheDir = rtrim($cacheDir, '/'); + } + + /** + * Validate the key + * + * @param string $key + * @return void + * @throws InvalidArgumentException + */ + protected function assertKey($key): void + { + parent::assertKey($key); + + if (strpos($key, '*')) { + throw new InvalidArgumentException("Key may not contain the character '*'"); + } + } + + + /** + * Get the contents of the cache file. + * + * @param string $cacheFile + * @return string + */ + protected function readFile(string $cacheFile): string + { + return file_get_contents($cacheFile); + } + + /** + * Read the first line of the cache file. + * + * @param string $cacheFile + * @return string + */ + protected function readLine(string $cacheFile): string + { + $fp = fopen($cacheFile, 'r'); + $line = fgets($fp); + fclose($fp); + + return $line; + } + + /** + * Create a cache file + * + * @param string $cacheFile + * @param string $contents + * @return bool + */ + protected function writeFile(string $cacheFile, string $contents): bool + { + $dir = dirname($cacheFile); + + if ($dir !== $this->cacheDir && !is_dir($dir)) { + mkdir($dir, 0775, true); + } + + return (bool)file_put_contents($cacheFile, $contents); + } + + /** + * Delete a cache file + * + * @param string $file + * @return bool + */ + protected function deleteFile(string $file): bool + { + return !is_file($file) || unlink($file); + } + + /** + * Remove all files from a directory. + */ + protected function removeFiles(string $dir): bool + { + $success = true; + + $generator = $this->getFilenameOption(); + $objects = $this->streamSafeGlob($dir, $generator('*')); + + foreach ($objects as $object) { + $success = $this->deleteFile($object) && $success; + } + + return $success; + } + + /** + * Recursive delete an empty directory. + * + * @param string $dir + */ + protected function removeRecursively(string $dir): bool + { + $success = $this->removeFiles($dir); + + $objects = $this->streamSafeGlob($dir, '*'); + + foreach ($objects as $object) { + if (!is_dir($object)) { + continue; + } + + if (is_link($object)) { + unlink($object); + } else { + $success = $this->removeRecursively($object) && $success; + rmdir($object); + } + } + + return $success; + } + + + /** + * {@inheritdoc} + */ + public function delete($key) + { + $cacheFile = $this->getFilename($key); + + return $this->deleteFile($cacheFile); + } + + /** + * Delete cache directory. + * + * {@inheritdoc} + */ + public function clear() + { + $this->removeRecursively($this->cacheDir); + + return true; + } + + /** + * Glob that is safe with streams (vfs for example) + * + * @param string $directory + * @param string $filePattern + * @return array + */ + protected function streamSafeGlob(string $directory, string $filePattern): array + { + $filePattern = basename($filePattern); + $files = scandir($directory); + $found = []; + + foreach ($files as $filename) { + if (in_array($filename, ['.', '..'])) { + continue; + } + + if (fnmatch($filePattern, $filename) || fnmatch($filePattern . '.ttl', $filename)) { + $found[] = "{$directory}/{$filename}"; + } + } + + return $found; + } +} diff --git a/msd/vendor/desarrolla2/cache/src/Apcu.php b/msd/vendor/desarrolla2/cache/src/Apcu.php new file mode 100644 index 0000000..844c8ca --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Apcu.php @@ -0,0 +1,88 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache; + +use Desarrolla2\Cache\Exception\CacheException; +use Desarrolla2\Cache\Packer\PackerInterface; +use Desarrolla2\Cache\Packer\NopPacker; + +/** + * Apcu + */ +class Apcu extends AbstractCache +{ + /** + * Create the default packer for this cache implementation + * + * @return PackerInterface + */ + protected static function createDefaultPacker(): PackerInterface + { + return new NopPacker(); + } + + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + $ttlSeconds = $this->ttlToSeconds($ttl); + + if (isset($ttlSeconds) && $ttlSeconds <= 0) { + return $this->delete($key); + } + + return apcu_store($this->keyToId($key), $this->pack($value), $ttlSeconds ?? 0); + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + $packed = apcu_fetch($this->keyToId($key), $success); + + return $success ? $this->unpack($packed) : $default; + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + return apcu_exists($this->keyToId($key)); + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + $id = $this->keyToId($key); + + return apcu_delete($id) || !apcu_exists($id); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return apcu_clear_cache(); + } +} diff --git a/msd/vendor/desarrolla2/cache/src/CacheInterface.php b/msd/vendor/desarrolla2/cache/src/CacheInterface.php new file mode 100644 index 0000000..17c3694 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/CacheInterface.php @@ -0,0 +1,58 @@ + + * @author Arnold Daniels + */ + +namespace Desarrolla2\Cache; + +use Psr\SimpleCache\CacheInterface as PsrCacheInterface; +use Desarrolla2\Cache\Packer\PackerInterface; +use Desarrolla2\Cache\KeyMaker\KeyMakerInterface; + +/** + * CacheInterface + */ +interface CacheInterface extends PsrCacheInterface +{ + /** + * Set option for cache + * + * @param string $key + * @param mixed $value + * @return static + */ + public function withOption(string $key, $value); + + /** + * Set multiple options for cache + * + * @param array $options + * @return static + */ + public function withOptions(array $options); + + /** + * Get option for cache + * + * @param string $key + * @return mixed + */ + public function getOption($key); + + /** + * Set the packer + * + * @param PackerInterface $packer + * @return static + */ + public function withPacker(PackerInterface $packer); +} diff --git a/msd/vendor/desarrolla2/cache/src/Chain.php b/msd/vendor/desarrolla2/cache/src/Chain.php new file mode 100644 index 0000000..117940f --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Chain.php @@ -0,0 +1,192 @@ + + * @author Arnold Daniels + */ + +namespace Desarrolla2\Cache; + +use Desarrolla2\Cache\Packer\NopPacker; +use Desarrolla2\Cache\Packer\PackerInterface; +use Desarrolla2\Cache\Exception\InvalidArgumentException; + +/** + * Use multiple cache adapters. + */ +class Chain extends AbstractCache +{ + /** + * @var CacheInterface[] + */ + protected $adapters; + + /** + * Create the default packer for this cache implementation + * + * @return PackerInterface + */ + protected static function createDefaultPacker(): PackerInterface + { + return new NopPacker(); + } + + + /** + * Chain constructor. + * + * @param CacheInterface[] $adapters Fastest to slowest + */ + public function __construct(array $adapters) + { + foreach ($adapters as $adapter) { + if (!$adapter instanceof CacheInterface) { + throw new InvalidArgumentException("All adapters should be a cache implementation"); + } + } + + $this->adapters = $adapters; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + $success = true; + + foreach ($this->adapters as $adapter) { + $success = $adapter->set($key, $value, $ttl) && $success; + } + + return $success; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + $success = true; + + foreach ($this->adapters as $adapter) { + $success = $adapter->setMultiple($values, $ttl) && $success; + } + + return $success; + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + foreach ($this->adapters as $adapter) { + $result = $adapter->get($key); // Not using $default as we want to get null if the adapter doesn't have it + + if (isset($result)) { + return $result; + } + } + + return $default; + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + $this->assertIterable($keys, 'keys are not iterable'); + + $missing = []; + $values = []; + + foreach ($keys as $key) { + $this->assertKey($key); + + $missing[] = $key; + $values[$key] = $default; + } + + foreach ($this->adapters as $adapter) { + if (empty($missing)) { + break; + } + + $found = []; + foreach ($adapter->getMultiple($missing) as $key => $value) { + if (isset($value)) { + $found[$key] = $value; + } + } + + $values = array_merge($values, $found); + $missing = array_values(array_diff($missing, array_keys($found))); + } + + return $values; + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + foreach ($this->adapters as $adapter) { + if ($adapter->has($key)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + $success = true; + + foreach ($this->adapters as $adapter) { + $success = $adapter->delete($key) && $success; + } + + return $success; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + $success = true; + + foreach ($this->adapters as $adapter) { + $success = $adapter->deleteMultiple($keys) && $success; + } + + return $success; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $success = true; + + foreach ($this->adapters as $adapter) { + $success = $adapter->clear() && $success; + } + + return $success; + } +} diff --git a/msd/vendor/desarrolla2/cache/src/Exception/BadMethodCallException.php b/msd/vendor/desarrolla2/cache/src/Exception/BadMethodCallException.php new file mode 100644 index 0000000..c1089b1 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Exception/BadMethodCallException.php @@ -0,0 +1,25 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\Exception; + +use Psr\SimpleCache\CacheException as PsrCacheException; + +/** + * Exception bad method calls + */ +class BadMethodCallException extends \BadMethodCallException implements PsrCacheException +{ +} diff --git a/msd/vendor/desarrolla2/cache/src/Exception/CacheException.php b/msd/vendor/desarrolla2/cache/src/Exception/CacheException.php new file mode 100644 index 0000000..1706f3a --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Exception/CacheException.php @@ -0,0 +1,25 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\Exception; + +use Psr\SimpleCache\CacheException as PsrCacheException; + +/** + * Interface used for all types of exceptions thrown by the implementing library. + */ +class CacheException extends \RuntimeException implements PsrCacheException +{ +} diff --git a/msd/vendor/desarrolla2/cache/src/Exception/InvalidArgumentException.php b/msd/vendor/desarrolla2/cache/src/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..2b1e92b --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Exception/InvalidArgumentException.php @@ -0,0 +1,25 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\Exception; + +use Psr\SimpleCache\InvalidArgumentException as PsrInvalidArgumentException; + +/** + * Exception for invalid cache arguments. + */ +class InvalidArgumentException extends \InvalidArgumentException implements PsrInvalidArgumentException +{ +} diff --git a/msd/vendor/desarrolla2/cache/src/Exception/UnexpectedValueException.php b/msd/vendor/desarrolla2/cache/src/Exception/UnexpectedValueException.php new file mode 100644 index 0000000..34e188e --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Exception/UnexpectedValueException.php @@ -0,0 +1,25 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\Exception; + +use Psr\SimpleCache\CacheException as PsrCacheException; + +/** + * Exception for unexpected values when reading from cache. + */ +class UnexpectedValueException extends \UnexpectedValueException implements PsrCacheException +{ +} diff --git a/msd/vendor/desarrolla2/cache/src/File.php b/msd/vendor/desarrolla2/cache/src/File.php new file mode 100644 index 0000000..912ff8e --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/File.php @@ -0,0 +1,175 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache; + +use Desarrolla2\Cache\Exception\InvalidArgumentException; +use Desarrolla2\Cache\Exception\UnexpectedValueException; +use Desarrolla2\Cache\Packer\PackerInterface; +use Desarrolla2\Cache\Packer\SerializePacker; + +/** + * Cache file. + */ +class File extends AbstractFile +{ + /** + * @var string 'embed', 'file', 'mtime' + */ + protected $ttlStrategy = 'embed'; + + /** + * Create the default packer for this cache implementation + * + * @return PackerInterface + */ + protected static function createDefaultPacker(): PackerInterface + { + return new SerializePacker(); + } + + /** + * Set TTL strategy + * + * @param string $strategy + */ + protected function setTtlStrategyOption($strategy) + { + if (!in_array($strategy, ['embed', 'file', 'mtime'])) { + throw new InvalidArgumentException("Unknown strategy '$strategy', should be 'embed', 'file' or 'mtime'"); + } + + $this->ttlStrategy = $strategy; + } + + /** + * Get TTL strategy + * + * @return string + */ + protected function getTtlStrategyOption(): string + { + return $this->ttlStrategy; + } + + + /** + * Get the TTL using one of the strategies + * + * @param string $cacheFile + * @return int + */ + protected function getTtl(string $cacheFile) + { + switch ($this->ttlStrategy) { + case 'embed': + return (int)$this->readLine($cacheFile); + case 'file': + return file_exists("$cacheFile.ttl") + ? (int)file_get_contents("$cacheFile.ttl") + : PHP_INT_MAX; + case 'mtime': + return $this->getTtl($cacheFile) > 0 ? filemtime($cacheFile) + $this->ttl : PHP_INT_MAX; + } + + throw new \InvalidArgumentException("Invalid TTL strategy '{$this->ttlStrategy}'"); + } + + /** + * Set the TTL using one of the strategies + * + * @param int|null $expiration + * @param string $contents + * @param string $cacheFile + * @return string The (modified) contents + */ + protected function setTtl($expiration, $contents, $cacheFile) + { + switch ($this->ttlStrategy) { + case 'embed': + $contents = ($expiration ?? PHP_INT_MAX) . "\n" . $contents; + break; + case 'file': + if ($expiration !== null) { + file_put_contents("$cacheFile.ttl", $expiration); + } + break; + case 'mtime': + // nothing + break; + } + + return $contents; + } + + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + if (!$this->has($key)) { + return $default; + } + + $cacheFile = $this->getFilename($key); + $packed = $this->readFile($cacheFile); + + if ($this->ttlStrategy === 'embed') { + $packed = substr($packed, strpos($packed, "\n") + 1); + } + + return $this->unpack($packed); + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + $cacheFile = $this->getFilename($key); + + if (!file_exists($cacheFile)) { + return false; + } + + $ttl = $this->getTtl($cacheFile); + + if ($ttl <= time()) { + $this->deleteFile($cacheFile); + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + $cacheFile = $this->getFilename($key); + $packed = $this->pack($value); + + if (!is_string($packed)) { + throw new UnexpectedValueException("Packer must create a string for the data to be cached to file"); + } + + $contents = $this->setTtl($this->ttlToTimestamp($ttl), $packed, $cacheFile); + + return $this->writeFile($cacheFile, $contents); + } +} diff --git a/msd/vendor/desarrolla2/cache/src/File/BasicFilename.php b/msd/vendor/desarrolla2/cache/src/File/BasicFilename.php new file mode 100644 index 0000000..13c63c4 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/File/BasicFilename.php @@ -0,0 +1,68 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\File; + +/** + * Create a path for a key + */ +class BasicFilename +{ + /** + * @var string + */ + protected $format; + + /** + * BasicFilename constructor. + * + * @param string $format + */ + public function __construct(string $format) + { + $this->format = $format; + } + + /** + * Get the format + * + * @return string + */ + public function getFormat(): string + { + return $this->format; + } + + /** + * Create the path for a key + * + * @param string $key + * @return string + */ + public function __invoke(string $key): string + { + return sprintf($this->format, $key ?: '*'); + } + + /** + * Cast to string + * + * @return string + */ + public function __toString(): string + { + return $this->getFormat(); + } +} \ No newline at end of file diff --git a/msd/vendor/desarrolla2/cache/src/File/TrieFilename.php b/msd/vendor/desarrolla2/cache/src/File/TrieFilename.php new file mode 100644 index 0000000..4009519 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/File/TrieFilename.php @@ -0,0 +1,121 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\File; + +/** + * Create a path for a key as prefix tree directory structure. + * + * @see https://en.wikipedia.org/wiki/Trie + */ +class TrieFilename +{ + /** + * @var string + */ + protected $format; + + /** + * @var int + */ + protected $levels; + + /** + * @var bool + */ + protected $hash; + + + /** + * TrieFilename constructor. + * + * @param string $format + * @param int $levels The depth of the structure + * @param bool $hash MD5 hash the key to get a better spread + */ + public function __construct(string $format, int $levels = 1, bool $hash = false) + { + $this->format = $format; + $this->levels = $levels; + $this->hash = $hash; + } + + /** + * Get the format + * + * @return string + */ + public function getFormat(): string + { + return $this->format; + } + + /** + * Get the depth of the structure + * + * @return int + */ + public function getLevels(): int + { + return $this->levels; + } + + /** + * Will the key be hashed to create the trie. + * + * @return bool + */ + public function isHashed(): bool + { + return $this->hash; + } + + + /** + * Create the path for a key + * + * @param string $key + * @return string + */ + public function __invoke(string $key): string + { + if (empty($key)) { + return $this->wildcardPath(); + } + + $dirname = $this->hash ? base_convert(md5($key), 16, 36) : $key; + $filename = sprintf($this->format, $key); + + $path = ''; + + for ($length = 1; $length <= $this->levels; $length++) { + $path .= substr($dirname, 0, $length) . DIRECTORY_SEPARATOR; + } + + return $path . $filename; + } + + /** + * Get a path for all files (using glob) + * + * @return string + */ + protected function wildcardPath(): string + { + $filename = sprintf($this->format, '*'); + + return str_repeat('*' . DIRECTORY_SEPARATOR, $this->levels) . $filename; + } +} diff --git a/msd/vendor/desarrolla2/cache/src/Memcached.php b/msd/vendor/desarrolla2/cache/src/Memcached.php new file mode 100644 index 0000000..c59b10b --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Memcached.php @@ -0,0 +1,218 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache; + +use Desarrolla2\Cache\Exception\InvalidArgumentException; +use Desarrolla2\Cache\Packer\PackerInterface; +use Desarrolla2\Cache\Packer\NopPacker; +use Memcached as MemcachedServer; + +/** + * Memcached + */ +class Memcached extends AbstractCache +{ + /** + * @var MemcachedServer + */ + protected $server; + + /** + * @param MemcachedServer $server + */ + public function __construct(MemcachedServer $server) + { + $this->server = $server; + } + + + /** + * Create the default packer for this cache implementation + * + * @return PackerInterface + */ + protected static function createDefaultPacker(): PackerInterface + { + return new NopPacker(); + } + + /** + * Validate the key + * + * @param string $key + * @return void + * @throws InvalidArgumentException + */ + protected function assertKey($key): void + { + parent::assertKey($key); + + if (strlen($key) > 250) { + throw new InvalidArgumentException("Key to long, max 250 characters"); + } + } + + /** + * Pack all values and turn keys into ids + * + * @param iterable $values + * @return array + */ + protected function packValues(iterable $values): array + { + $packed = []; + + foreach ($values as $key => $value) { + $this->assertKey(is_int($key) ? (string)$key : $key); + $packed[$key] = $this->pack($value); + } + + return $packed; + } + + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + $this->assertKey($key); + + $data = $this->server->get($key); + + if ($this->server->getResultCode() !== MemcachedServer::RES_SUCCESS) { + return $default; + } + + return $this->unpack($data); + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + $this->assertKey($key); + $this->server->get($key); + + $result = $this->server->getResultCode(); + + return $result === MemcachedServer::RES_SUCCESS; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + $this->assertKey($key); + + $packed = $this->pack($value); + $ttlTime = $this->ttlToMemcachedTime($ttl); + + if ($ttlTime === false) { + return $this->delete($key); + } + + $success = $this->server->set($key, $packed, $ttlTime); + + return $success; + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + $this->server->delete($this->keyToId($key)); + + $result = $this->server->getResultCode(); + + return $result === MemcachedServer::RES_SUCCESS || $result === MemcachedServer::RES_NOTFOUND; + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + $this->assertIterable($keys, 'keys not iterable'); + $keysArr = is_array($keys) ? $keys : iterator_to_array($keys, false); + array_walk($keysArr, [$this, 'assertKey']); + + $result = $this->server->getMulti($keysArr); + + if ($result === false) { + return false; + } + + $items = array_fill_keys($keysArr, $default); + + foreach ($result as $key => $value) { + $items[$key] = $this->unpack($value); + } + + return $items; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + $this->assertIterable($values, 'values not iterable'); + + $packed = $this->packValues($values); + $ttlTime = $this->ttlToMemcachedTime($ttl); + + if ($ttlTime === false) { + return $this->server->deleteMulti(array_keys($packed)); + } + + return $this->server->setMulti($packed, $ttlTime); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->server->flush(); + } + + + /** + * Convert ttl to timestamp or seconds. + * + * @see http://php.net/manual/en/memcached.expiration.php + * + * @param null|int|\DateInterval $ttl + * @return int|null + * @throws InvalidArgumentException + */ + protected function ttlToMemcachedTime($ttl) + { + $seconds = $this->ttlToSeconds($ttl); + + if ($seconds <= 0) { + return isset($seconds) ? false : 0; + } + + /* 2592000 seconds = 30 days */ + return $seconds <= 2592000 ? $seconds : $this->ttlToTimestamp($ttl); + } +} diff --git a/msd/vendor/desarrolla2/cache/src/Memory.php b/msd/vendor/desarrolla2/cache/src/Memory.php new file mode 100644 index 0000000..10f7f20 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Memory.php @@ -0,0 +1,165 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache; + +use Desarrolla2\Cache\Packer\PackerInterface; +use Desarrolla2\Cache\Packer\SerializePacker; + +/** + * Memory + */ +class Memory extends AbstractCache +{ + /** + * Limit the amount of entries + * @var int + */ + protected $limit = PHP_INT_MAX; + + + /** + * @var array + */ + protected $cache = []; + + /** + * @var array + */ + protected $cacheTtl = []; + + + /** + * Create the default packer for this cache implementation. + * {@internal NopPacker might fail PSR-16, as cached objects would change} + * + * @return PackerInterface + */ + protected static function createDefaultPacker(): PackerInterface + { + return new SerializePacker(); + } + + /** + * Make a clone of this object. + * Set by cache reference, thus using the same pool. + * + * @return static + */ + protected function cloneSelf(): AbstractCache + { + $clone = clone $this; + + $clone->cache =& $this->cache; + $clone->cacheTtl =& $this->cacheTtl; + + return $clone; + } + + /** + * Set the max number of items + * + * @param int $limit + */ + protected function setLimitOption($limit) + { + $this->limit = (int)$limit ?: PHP_INT_MAX; + } + + /** + * Get the max number of items + * + * @return int + */ + protected function getLimitOption() + { + return $this->limit; + } + + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + if (!$this->has($key)) { + return $default; + } + + $id = $this->keyToId($key); + + return $this->unpack($this->cache[$id]); + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + $id = $this->keyToId($key); + + if (!isset($this->cacheTtl[$id])) { + return false; + } + + if ($this->cacheTtl[$id] <= time()) { + unset($this->cache[$id], $this->cacheTtl[$id]); + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + if (count($this->cache) >= $this->limit) { + $deleteKey = key($this->cache); + unset($this->cache[$deleteKey], $this->cacheTtl[$deleteKey]); + } + + $id = $this->keyToId($key); + + $this->cache[$id] = $this->pack($value); + $this->cacheTtl[$id] = $this->ttlToTimestamp($ttl) ?? PHP_INT_MAX; + + return true; + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + $id = $this->keyToId($key); + unset($this->cache[$id], $this->cacheTtl[$id]); + + return true; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->cache = []; + $this->cacheTtl = []; + + return true; + } +} diff --git a/msd/vendor/desarrolla2/cache/src/MongoDB.php b/msd/vendor/desarrolla2/cache/src/MongoDB.php new file mode 100644 index 0000000..dab7494 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/MongoDB.php @@ -0,0 +1,273 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache; + +use Desarrolla2\Cache\Packer\PackerInterface; +use Desarrolla2\Cache\Packer\MongoDBBinaryPacker; +use Desarrolla2\Cache\Option\InitializeTrait as InitializeOption; +use MongoDB\Collection; +use MongoDB\BSON\UTCDatetime as BSONUTCDateTime; +use MongoDB\Driver\Exception\RuntimeException as MongoDBRuntimeException; + +/** + * MongoDB cache implementation + */ +class MongoDB extends AbstractCache +{ + use InitializeOption; + + /** + * @var Collection + */ + protected $collection; + + /** + * Class constructor + * + * @param Collection $collection + */ + public function __construct(Collection $collection) + { + $this->collection = $collection; + } + + /** + * Initialize the DB collection. + * Set TTL index. + */ + protected function initialize(): void + { + $this->collection->createIndex(['ttl' => 1], ['expireAfterSeconds' => 0]); + } + + + /** + * Create the default packer for this cache implementation. + * + * @return PackerInterface + */ + protected static function createDefaultPacker(): PackerInterface + { + return new MongoDBBinaryPacker(); + } + + /** + * Get filter for key and ttl. + * + * @param string|iterable $key + * @return array + */ + protected function filter($key) + { + if (is_array($key)) { + $key = ['$in' => $key]; + } + + return [ + '_id' => $key, + '$or' => [ + ['ttl' => ['$gt' => new BSONUTCDateTime($this->currentTimestamp() * 1000)]], + ['ttl' => null] + ] + ]; + } + + /** + * {@inheritdoc } + */ + public function get($key, $default = null) + { + $filter = $this->filter($this->keyToId($key)); + + try { + $data = $this->collection->findOne($filter); + } catch (MongoDBRuntimeException $e) { + return $default; + } + + return isset($data) ? $this->unpack($data['value']) : $default; + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + $idKeyPairs = $this->mapKeysToIds($keys); + + if (empty($idKeyPairs)) { + return []; + } + + $filter = $this->filter(array_keys($idKeyPairs)); + $items = array_fill_keys(array_values($idKeyPairs), $default); + + try { + $rows = $this->collection->find($filter); + } catch (MongoDBRuntimeException $e) { + return $items; + } + + foreach ($rows as $row) { + $id = $row['_id']; + $key = $idKeyPairs[$id]; + + $items[$key] = $this->unpack($row['value']); + } + + return $items; + } + + /** + * {@inheritdoc } + */ + public function has($key) + { + $filter = $this->filter($this->keyToId($key)); + + try { + $count = $this->collection->count($filter); + } catch (MongoDBRuntimeException $e) { + return false; + } + + return $count > 0; + } + + /** + * {@inheritdoc } + */ + public function set($key, $value, $ttl = null) + { + $id = $this->keyToId($key); + + $item = [ + '_id' => $id, + 'ttl' => $this->getTtlBSON($ttl), + 'value' => $this->pack($value) + ]; + + try { + $this->collection->replaceOne(['_id' => $id], $item, ['upsert' => true]); + } catch (MongoDBRuntimeException $e) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + $this->assertIterable($values, 'values not iterable'); + + if (empty($values)) { + return true; + } + + $bsonTtl = $this->getTtlBSON($ttl); + $items = []; + + foreach ($values as $key => $value) { + $id = $this->keyToId(is_int($key) ? (string)$key : $key); + + $items[] = [ + 'replaceOne' => [ + ['_id' => $id], + [ + '_id' => $id, + 'ttl' => $bsonTtl, + 'value' => $this->pack($value) + ], + [ 'upsert' => true ] + ] + ]; + } + + try { + $this->collection->bulkWrite($items); + } catch (MongoDBRuntimeException $e) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + $id = $this->keyToId($key); + + try { + $this->collection->deleteOne(['_id' => $id]); + } catch (MongoDBRuntimeException $e) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + $idKeyPairs = $this->mapKeysToIds($keys); + + try { + if (!empty($idKeyPairs)) { + $this->collection->deleteMany(['_id' => ['$in' => array_keys($idKeyPairs)]]); + } + } catch (MongoDBRuntimeException $e) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + try { + $this->collection->drop(); + } catch (MongoDBRuntimeException $e) { + return false; + } + + $this->requireInitialization(); + + return true; + } + + + /** + * Get TTL as Date type BSON object + * + * @param null|int|\DateInterval $ttl + * @return BSONUTCDatetime|null + */ + protected function getTtlBSON($ttl): ?BSONUTCDatetime + { + return isset($ttl) ? new BSONUTCDateTime($this->ttlToTimestamp($ttl) * 1000) : null; + } +} diff --git a/msd/vendor/desarrolla2/cache/src/Mysqli.php b/msd/vendor/desarrolla2/cache/src/Mysqli.php new file mode 100644 index 0000000..0a2679a --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Mysqli.php @@ -0,0 +1,312 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache; + +use Desarrolla2\Cache\Option\InitializeTrait; +use mysqli as Server; +use Desarrolla2\Cache\Packer\PackerInterface; +use Desarrolla2\Cache\Packer\SerializePacker; + +/** + * Mysqli cache adapter. + * + * Errors are silently ignored but exceptions are **not** caught. Beware when using `mysqli_report()` to throw a + * `mysqli_sql_exception` on error. + */ +class Mysqli extends AbstractCache +{ + use InitializeTrait; + + /** + * @var Server + */ + protected $server; + + /** + * @var string + */ + protected $table = 'cache'; + + + /** + * Class constructor + * + * @param Server $server + */ + public function __construct(Server $server) + { + $this->server = $server; + } + + + /** + * Initialize table. + * Automatically delete old cache. + */ + protected function initialize(): void + { + if ($this->initialized !== false) { + return; + } + + $this->query( + "CREATE TABLE IF NOT EXISTS `{table}` " + . "( `key` VARCHAR(255), `value` BLOB, `ttl` BIGINT UNSIGNED, PRIMARY KEY (`key`) )" + ); + + $this->query( + "CREATE EVENT IF NOT EXISTS `apply_ttl_{$this->table}` ON SCHEDULE EVERY 1 HOUR DO BEGIN" + . " DELETE FROM {table} WHERE `ttl` < UNIX_TIMESTAMP();" + . " END" + ); + + $this->initialized = true; + } + + /** + * Create the default packer for this cache implementation. + * + * @return PackerInterface + */ + protected static function createDefaultPacker(): PackerInterface + { + return new SerializePacker(); + } + + + /** + * Set the table name + * + * @param string $table + */ + public function setTableOption(string $table) + { + $this->table = $table; + $this->requireInitialization(); + } + + /** + * Get the table name + * + * @return string + */ + public function getTableOption(): string + { + return $this->table; + } + + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + $this->initialize(); + + $result = $this->query( + 'SELECT `value` FROM {table} WHERE `key` = ? AND (`ttl` > ? OR `ttl` IS NULL) LIMIT 1', + 'si', + $this->keyToId($key), + $this->currentTimestamp() + ); + + $row = $result !== false ? $result->fetch_row() : null; + + return $row ? $this->unpack($row[0]) : $default; + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + $idKeyPairs = $this->mapKeysToIds($keys); + + if (empty($idKeyPairs)) { + return []; + } + + $this->initialize(); + + $values = array_fill_keys(array_values($idKeyPairs), $default); + + $placeholders = rtrim(str_repeat('?, ', count($idKeyPairs)), ', '); + $paramTypes = str_repeat('s', count($idKeyPairs)) . 'i'; + $params = array_keys($idKeyPairs); + $params[] = $this->currentTimestamp(); + + $result = $this->query( + "SELECT `key`, `value` FROM {table} WHERE `key` IN ($placeholders) AND (`ttl` > ? OR `ttl` IS NULL)", + $paramTypes, + ...$params + ); + + while (([$id, $value] = $result->fetch_row())) { + $key = $idKeyPairs[$id]; + $values[$key] = $this->unpack($value); + } + + return $values; + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + $this->initialize(); + + $result = $this->query( + 'SELECT COUNT(`key`) FROM {table} WHERE `key` = ? AND (`ttl` > ? OR `ttl` IS NULL) LIMIT 1', + 'si', + $this->keyToId($key), + $this->currentTimestamp() + ); + + [$count] = $result ? $result->fetch_row() : [null]; + + return isset($count) && $count > 0; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + $this->initialize(); + + $result = $this->query( + 'REPLACE INTO {table} (`key`, `value`, `ttl`) VALUES (?, ?, ?)', + 'ssi', + $this->keyToId($key), + $this->pack($value), + $this->ttlToTimestamp($ttl) + ); + + return $result !== false; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + $this->assertIterable($values, 'values not iterable'); + + if (empty($values)) { + return true; + } + + $this->initialize(); + + $count = 0; + $params = []; + $timeTtl = $this->ttlToTimestamp($ttl); + + foreach ($values as $key => $value) { + $count++; + $params[] = $this->keyToId(is_int($key) ? (string)$key : $key); + $params[] = $this->pack($value); + $params[] = $timeTtl; + } + + $query = 'REPLACE INTO {table} (`key`, `value`, `ttl`) VALUES ' + . rtrim(str_repeat('(?, ?, ?), ', $count), ', '); + + return (bool)$this->query($query, str_repeat('ssi', $count), ...$params); + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + $this->initialize(); + + return (bool)$this->query( + 'DELETE FROM {table} WHERE `key` = ?', + 's', + $this->keyToId($key) + ); + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + $idKeyPairs = $this->mapKeysToIds($keys); + + if (empty($idKeyPairs)) { + return true; + } + + $this->initialize(); + + $placeholders = rtrim(str_repeat('?, ', count($idKeyPairs)), ', '); + $paramTypes = str_repeat('s', count($idKeyPairs)); + + return (bool)$this->query( + "DELETE FROM {table} WHERE `key` IN ($placeholders)", + $paramTypes, + ...array_keys($idKeyPairs) + ); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->initialize(); + return (bool)$this->query('TRUNCATE {table}'); + } + + + /** + * Query the MySQL server + * + * @param string $query + * @param string $types + * @param mixed[] $params + * @return \mysqli_result|bool + */ + protected function query($query, $types = '', ...$params) + { + $sql = str_replace('{table}', $this->table, $query); + + if ($params === []) { + $ret = $this->server->query($sql); + } else { + $statement = $this->server->prepare($sql); + + if ($statement !== false) { + $statement->bind_param($types, ...$params); + + $ret = $statement->execute(); + $ret = $ret ? ($statement->get_result() ?: true) : false; + } else { + $ret = false; + } + } + + if ($this->server->error) { + trigger_error($this->server->error . " $sql", E_USER_NOTICE); + } + + return $ret; + } +} diff --git a/msd/vendor/desarrolla2/cache/src/NotCache.php b/msd/vendor/desarrolla2/cache/src/NotCache.php new file mode 100644 index 0000000..e8506e3 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/NotCache.php @@ -0,0 +1,93 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache; + +use Desarrolla2\Cache\AbstractCache; +use Desarrolla2\Cache\Packer\PackerInterface; +use Desarrolla2\Cache\Packer\NopPacker; + +/** + * Dummy cache handler + */ +class NotCache extends AbstractCache +{ + /** + * Create the default packer for this cache implementation. + * + * @return PackerInterface + */ + protected static function createDefaultPacker(): PackerInterface + { + return new NopPacker(); + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return true; + } +} diff --git a/msd/vendor/desarrolla2/cache/src/Option/FilenameTrait.php b/msd/vendor/desarrolla2/cache/src/Option/FilenameTrait.php new file mode 100644 index 0000000..39dd415 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Option/FilenameTrait.php @@ -0,0 +1,91 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\Option; + +use TypeError; +use Desarrolla2\Cache\File\BasicFilename; + +/** + * Use filename generator + */ +trait FilenameTrait +{ + /** + * @var callable + */ + protected $filename; + + + /** + * Filename format or callable. + * The filename format will be applied using sprintf, replacing `%s` with the key. + * + * @param string|callable $filename + * @return void + */ + protected function setFilenameOption($filename): void + { + if (is_string($filename)) { + $filename = new BasicFilename($filename); + } + + if (!is_callable($filename)) { + throw new TypeError("Filename should be a string or callable"); + } + + $this->filename = $filename; + } + + /** + * Get the filename callable + * + * @return callable + */ + protected function getFilenameOption(): callable + { + if (!isset($this->filename)) { + $this->filename = new BasicFilename('%s.' . $this->getPacker()->getType()); + } + + return $this->filename; + } + + /** + * Create a filename based on the key + * + * @param string|mixed $key + * @return string + */ + protected function getFilename($key): string + { + $id = $this->keyToId($key); + $generator = $this->getFilenameOption(); + + return $this->cacheDir . DIRECTORY_SEPARATOR . $generator($id); + } + + /** + * Get a wildcard for all files + * + * @return string + */ + protected function getWildcard(): string + { + $generator = $this->getFilenameOption(); + + return $this->cacheDir . DIRECTORY_SEPARATOR . $generator(''); + } +} diff --git a/msd/vendor/desarrolla2/cache/src/Option/InitializeTrait.php b/msd/vendor/desarrolla2/cache/src/Option/InitializeTrait.php new file mode 100644 index 0000000..b302a17 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Option/InitializeTrait.php @@ -0,0 +1,65 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\Option; + +/** + * Auto initialize the cache + */ +trait InitializeTrait +{ + /** + * Is cache initialized + * @var bool|null + */ + protected $initialized = false; + + + /** + * Enable/disable initialization + * + * @param bool $enabled + */ + public function setInitializeOption(bool $enabled) + { + $this->initialized = $enabled ? (bool)$this->initialized : null; + } + + /** + * Should initialize + * + * @return bool + */ + protected function getInitializeOption(): bool + { + return $this->initialized !== null; + } + + /** + * Mark as initialization required (if enabled) + */ + protected function requireInitialization() + { + $this->initialized = isset($this->initialized) ? false : null; + } + + + /** + * Initialize + * + * @return void + */ + abstract protected function initialize(): void; +} diff --git a/msd/vendor/desarrolla2/cache/src/Option/PrefixTrait.php b/msd/vendor/desarrolla2/cache/src/Option/PrefixTrait.php new file mode 100644 index 0000000..63d5273 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Option/PrefixTrait.php @@ -0,0 +1,49 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\Option; + +/** + * Prefix option + */ +trait PrefixTrait +{ + /** + * @var string + */ + protected $prefix = ''; + + + /** + * Set the key prefix + * + * @param string $prefix + * @return void + */ + protected function setPrefixOption(string $prefix): void + { + $this->prefix = $prefix; + } + + /** + * Get the key prefix + * + * @return string + */ + protected function getPrefixOption(): string + { + return $this->prefix; + } +} diff --git a/msd/vendor/desarrolla2/cache/src/Option/TtlTrait.php b/msd/vendor/desarrolla2/cache/src/Option/TtlTrait.php new file mode 100644 index 0000000..c6e2fc2 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Option/TtlTrait.php @@ -0,0 +1,54 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\Option; + +use Desarrolla2\Cache\Exception\InvalidArgumentException; + +/** + * TTL option + */ +trait TtlTrait +{ + /** + * @var int|null + */ + protected $ttl = null; + + /** + * Set the maximum time to live (ttl) + * + * @param int|null $value Seconds or null to live forever + * @throws InvalidArgumentException + */ + protected function setTtlOption(?int $value): void + { + if (isset($value) && $value < 1) { + throw new InvalidArgumentException('ttl cant be lower than 1'); + } + + $this->ttl = $value; + } + + /** + * Get the maximum time to live (ttl) + * + * @return int|null + */ + protected function getTtlOption(): ?int + { + return $this->ttl; + } +} \ No newline at end of file diff --git a/msd/vendor/desarrolla2/cache/src/Packer/JsonPacker.php b/msd/vendor/desarrolla2/cache/src/Packer/JsonPacker.php new file mode 100644 index 0000000..68e7bb5 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Packer/JsonPacker.php @@ -0,0 +1,69 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\Packer; + +use Desarrolla2\Cache\Packer\PackerInterface; +use Desarrolla2\Cache\Exception\InvalidArgumentException; + +/** + * Pack value through serialization + */ +class JsonPacker implements PackerInterface +{ + /** + * Get cache type (might be used as file extension) + * + * @return string + */ + public function getType() + { + return 'json'; + } + + /** + * Pack the value + * + * @param mixed $value + * @return string + */ + public function pack($value) + { + return json_encode($value); + } + + /** + * Unpack the value + * + * @param string $packed + * @return mixed + * @throws InvalidArgumentException + */ + public function unpack($packed) + { + if (!is_string($packed)) { + throw new InvalidArgumentException("packed value should be a string"); + } + + $ret = json_decode($packed); + + if (!isset($ret) && json_last_error()) { + throw new \UnexpectedValueException("packed value is not a valid JSON string"); + } + + return $ret; + } +} diff --git a/msd/vendor/desarrolla2/cache/src/Packer/MongoDBBinaryPacker.php b/msd/vendor/desarrolla2/cache/src/Packer/MongoDBBinaryPacker.php new file mode 100644 index 0000000..c813853 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Packer/MongoDBBinaryPacker.php @@ -0,0 +1,77 @@ + + * @author Arnold Daniels + */ + +namespace Desarrolla2\Cache\Packer; + +use Desarrolla2\Cache\Packer\PackerInterface; +use MongoDB\BSON\Binary; + +/** + * Pack as BSON binary + * + * @todo Don't use serialize when packer chain is here. + */ +class MongoDBBinaryPacker implements PackerInterface +{ + /** + * @var array + */ + protected $options; + + /** + * SerializePacker constructor + * + * @param array $options Any options to be provided to unserialize() + */ + public function __construct(array $options = ['allowed_classes' => true]) + { + $this->options = $options; + } + + /** + * Get cache type (might be used as file extension) + * + * @return string + */ + public function getType() + { + return 'bson'; + } + + /** + * Pack the value + * + * @param mixed $value + * @return string + */ + public function pack($value) + { + return new Binary(serialize($value), Binary::TYPE_GENERIC); + } + + /** + * Unpack the value + * + * @param string $packed + * @return string + * @throws \UnexpectedValueException if he value can't be unpacked + */ + public function unpack($packed) + { + if (!$packed instanceof Binary) { + throw new \InvalidArgumentException("packed value should be BSON binary"); + } + + return unserialize((string)$packed, $this->options); + } +} \ No newline at end of file diff --git a/msd/vendor/desarrolla2/cache/src/Packer/NopPacker.php b/msd/vendor/desarrolla2/cache/src/Packer/NopPacker.php new file mode 100644 index 0000000..4a56114 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Packer/NopPacker.php @@ -0,0 +1,57 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\Packer; + +use Desarrolla2\Cache\Packer\PackerInterface; + +/** + * Don't pack, just straight passthrough + */ +class NopPacker implements PackerInterface +{ + /** + * Get cache type (might be used as file extension) + * + * @return string + */ + public function getType() + { + return 'data'; + } + + /** + * Pack the value + * + * @param mixed $value + * @return mixed + */ + public function pack($value) + { + return $value; + } + + /** + * Unpack the value + * + * @param mixed $packed + * @return mixed + */ + public function unpack($packed) + { + return $packed; + } +} diff --git a/msd/vendor/desarrolla2/cache/src/Packer/PackerInterface.php b/msd/vendor/desarrolla2/cache/src/Packer/PackerInterface.php new file mode 100644 index 0000000..24cb1bf --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Packer/PackerInterface.php @@ -0,0 +1,47 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\Packer; + +/** + * Interface for packer / unpacker + */ +interface PackerInterface +{ + /** + * Get cache type (might be used as file extension) + * + * @return string + */ + public function getType(); + + /** + * Pack the value + * + * @param mixed $value + * @return string|mixed + */ + public function pack($value); + + /** + * Unpack the value + * + * @param string|mixed $packed + * @return string + * @throws \UnexpectedValueException if the value can't be unpacked + */ + public function unpack($packed); +} diff --git a/msd/vendor/desarrolla2/cache/src/Packer/PackingTrait.php b/msd/vendor/desarrolla2/cache/src/Packer/PackingTrait.php new file mode 100644 index 0000000..d68b808 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Packer/PackingTrait.php @@ -0,0 +1,86 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\Packer; + +/** + * Support packing for Caching adapter + */ +trait PackingTrait +{ + /** + * @var PackerInterface + */ + protected $packer; + + + /** + * Create the default packer for this cache implementation + * + * @return PackerInterface + */ + abstract protected static function createDefaultPacker(): PackerInterface; + + /** + * Set a packer to pack (serialialize) and unpack (unserialize) the data. + * + * @param PackerInterface $packer + * @return static + */ + public function withPacker(PackerInterface $packer) + { + $cache = $this->cloneSelf(); + $cache->packer = $packer; + + return $cache; + } + + /** + * Get the packer + * + * @return PackerInterface + */ + protected function getPacker(): PackerInterface + { + if (!isset($this->packer)) { + $this->packer = static::createDefaultPacker(); + } + + return $this->packer; + } + + /** + * Pack the value + * + * @param mixed $value + * @return string|mixed + */ + protected function pack($value) + { + return $this->getPacker()->pack($value); + } + + /** + * Unpack the data to retrieve the value + * + * @param string|mixed $packed + * @return mixed + * @throws \UnexpectedValueException + */ + protected function unpack($packed) + { + return $this->getPacker()->unpack($packed); + } +} diff --git a/msd/vendor/desarrolla2/cache/src/Packer/SerializePacker.php b/msd/vendor/desarrolla2/cache/src/Packer/SerializePacker.php new file mode 100644 index 0000000..43a910d --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Packer/SerializePacker.php @@ -0,0 +1,78 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache\Packer; + +use Desarrolla2\Cache\Packer\PackerInterface; +use Desarrolla2\Cache\Exception\InvalidArgumentException; + +/** + * Pack value through serialization + */ +class SerializePacker implements PackerInterface +{ + /** + * @var array + */ + protected $options; + + /** + * SerializePacker constructor + * + * @param array $options Any options to be provided to unserialize() + */ + public function __construct(array $options = ['allowed_classes' => true]) + { + $this->options = $options; + } + + /** + * Get cache type (might be used as file extension) + * + * @return string + */ + public function getType() + { + return 'php.cache'; + } + + /** + * Pack the value + * + * @param mixed $value + * @return string + */ + public function pack($value) + { + return serialize($value); + } + + /** + * Unpack the value + * + * @param string $packed + * @return string + * @throws \UnexpectedValueException if he value can't be unpacked + */ + public function unpack($packed) + { + if (!is_string($packed)) { + throw new InvalidArgumentException("packed value should be a string"); + } + + return unserialize($packed, $this->options); + } +} diff --git a/msd/vendor/desarrolla2/cache/src/PhpFile.php b/msd/vendor/desarrolla2/cache/src/PhpFile.php new file mode 100644 index 0000000..fc28f3a --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/PhpFile.php @@ -0,0 +1,111 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache; + +use Desarrolla2\Cache\AbstractFile; +use Desarrolla2\Cache\Packer\PackerInterface; +use Desarrolla2\Cache\Packer\SerializePacker; +use Desarrolla2\Cache\File\BasicFilename; + +/** + * Cache file as PHP script. + */ +class PhpFile extends AbstractFile +{ + /** + * Create the default packer for this cache implementation. + * + * @return PackerInterface + */ + protected static function createDefaultPacker(): PackerInterface + { + return new SerializePacker(); + } + + /** + * Get the filename callable + * + * @return callable + */ + protected function getFilenameOption(): callable + { + if (!isset($this->filename)) { + $this->filename = new BasicFilename('%s.php'); + } + + return $this->filename; + } + + /** + * Create a PHP script returning the cached value + * + * @param mixed $value + * @param int|null $ttl + * @return string + */ + public function createScript($value, ?int $ttl): string + { + $macro = var_export($value, true); + + if (strpos($macro, 'stdClass::__set_state') !== false) { + $macro = preg_replace_callback("/('([^'\\\\]++|''\\.)')|stdClass::__set_state/", $macro, function($match) { + return empty($match[1]) ? '(object)' : $match[1]; + }); + } + + return $ttl !== null + ? "getFilename($key); + + if (!file_exists($cacheFile)) { + return $default; + } + + $packed = include $cacheFile; + + return $packed === false ? $default : $this->unpack($packed); + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + return $this->get($key) !== null; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + $cacheFile = $this->getFilename($key); + + $packed = $this->pack($value); + $script = $this->createScript($packed, $this->ttlToTimestamp($ttl)); + + return $this->writeFile($cacheFile, $script); + } +} diff --git a/msd/vendor/desarrolla2/cache/src/Predis.php b/msd/vendor/desarrolla2/cache/src/Predis.php new file mode 100644 index 0000000..f51cf15 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/src/Predis.php @@ -0,0 +1,245 @@ + + * @author Arnold Daniels + */ + +declare(strict_types=1); + +namespace Desarrolla2\Cache; + +use Desarrolla2\Cache\AbstractCache; +use Desarrolla2\Cache\Exception\UnexpectedValueException; +use Desarrolla2\Cache\Packer\PackerInterface; +use Desarrolla2\Cache\Packer\SerializePacker; +use Predis\Client; +use Predis\Response\ServerException; +use Predis\Response\Status; +use Predis\Response\ErrorInterface; + +/** + * Predis cache adapter. + * + * Errors are silently ignored but ServerExceptions are **not** caught. To PSR-16 compliant disable the `exception` + * option. + */ +class Predis extends AbstractCache +{ + /** + * @var Client + */ + protected $predis; + + /** + * Class constructor + * @see predis documentation about how know your configuration https://github.com/nrk/predis + * + * @param Client $client + */ + public function __construct(Client $client) + { + $this->predis = $client; + } + + /** + * Create the default packer for this cache implementation. + * + * @return PackerInterface + */ + protected static function createDefaultPacker(): PackerInterface + { + return new SerializePacker(); + } + + + /** + * Run a predis command. + * + * @param string $cmd + * @param mixed ...$args + * @return mixed|bool + */ + protected function execCommand(string $cmd, ...$args) + { + $command = $this->predis->createCommand($cmd, $args); + $response = $this->predis->executeCommand($command); + + if ($response instanceof ErrorInterface) { + return false; + } + + if ($response instanceof Status) { + return $response->getPayload() === 'OK'; + } + + return $response; + } + + /** + * Set multiple (mset) with expire + * + * @param array $dictionary + * @param int|null $ttlSeconds + * @return bool + */ + protected function msetExpire(array $dictionary, ?int $ttlSeconds): bool + { + if (empty($dictionary)) { + return true; + } + + if (!isset($ttlSeconds)) { + return $this->execCommand('MSET', $dictionary); + } + + $transaction = $this->predis->transaction(); + + foreach ($dictionary as $key => $value) { + $transaction->set($key, $value, 'EX', $ttlSeconds); + } + + try { + $responses = $transaction->execute(); + } catch (ServerException $e) { + return false; + } + + $ok = array_reduce($responses, function($ok, $response) { + return $ok && $response instanceof Status && $response->getPayload() === 'OK'; + }, true); + + return $ok; + } + + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + $id = $this->keyToId($key); + $response = $this->execCommand('GET', $id); + + return !empty($response) ? $this->unpack($response) : $default; + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + $idKeyPairs = $this->mapKeysToIds($keys); + $ids = array_keys($idKeyPairs); + + $response = $this->execCommand('MGET', $ids); + + if ($response === false) { + return false; + } + + $items = []; + $packedItems = array_combine(array_values($idKeyPairs), $response); + + foreach ($packedItems as $key => $packed) { + $items[$key] = isset($packed) ? $this->unpack($packed) : $default; + } + + return $items; + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + return $this->execCommand('EXISTS', $this->keyToId($key)); + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + $id = $this->keyToId($key); + $packed = $this->pack($value); + + if (!is_string($packed)) { + throw new UnexpectedValueException("Packer must create a string for the data"); + } + + $ttlSeconds = $this->ttlToSeconds($ttl); + + if (isset($ttlSeconds) && $ttlSeconds <= 0) { + return $this->execCommand('DEL', [$id]); + } + + return !isset($ttlSeconds) + ? $this->execCommand('SET', $id, $packed) + : $this->execCommand('SETEX', $id, $ttlSeconds, $packed); + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + $this->assertIterable($values, 'values not iterable'); + + $dictionary = []; + + foreach ($values as $key => $value) { + $id = $this->keyToId(is_int($key) ? (string)$key : $key); + $packed = $this->pack($value); + + if (!is_string($packed)) { + throw new UnexpectedValueException("Packer must create a string for the data"); + } + + $dictionary[$id] = $packed; + } + + $ttlSeconds = $this->ttlToSeconds($ttl); + + if (isset($ttlSeconds) && $ttlSeconds <= 0) { + return $this->execCommand('DEL', array_keys($dictionary)); + } + + return $this->msetExpire($dictionary, $ttlSeconds); + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + $id = $this->keyToId($key); + + return $this->execCommand('DEL', [$id]) !== false; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + $ids = array_keys($this->mapKeysToIds($keys)); + + return empty($ids) || $this->execCommand('DEL', $ids) !== false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->execCommand('FLUSHDB'); + } +} diff --git a/msd/vendor/desarrolla2/cache/tests/AbstractCacheTest.php b/msd/vendor/desarrolla2/cache/tests/AbstractCacheTest.php new file mode 100644 index 0000000..0746d31 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/AbstractCacheTest.php @@ -0,0 +1,93 @@ + + */ + +namespace Desarrolla2\Test\Cache; + +use Cache\IntegrationTests\SimpleCacheTest; +use Desarrolla2\Cache\Exception\InvalidArgumentException; + +/** + * AbstractCacheTest + */ +abstract class AbstractCacheTest extends SimpleCacheTest +{ + /** + * @return array + */ + public function dataProviderForOptions() + { + return [ + ['ttl', 100], + ['prefix', 'test'] + ]; + } + + /** + * @dataProvider dataProviderForOptions + * + * @param string $key + * @param mixed $value + */ + public function testWithOption($key, $value) + { + $cache = $this->cache->withOption($key, $value); + $this->assertEquals($value, $cache->getOption($key)); + + // Check immutability + $this->assertNotSame($this->cache, $cache); + $this->assertNotEquals($value, $this->cache->getOption($key)); + } + + public function testWithOptions() + { + $data = $this->dataProviderForOptions(); + $options = array_combine(array_column($data, 0), array_column($data, 1)); + + $cache = $this->cache->withOptions($options); + + foreach ($options as $key => $value) { + $this->assertEquals($value, $cache->getOption($key)); + } + + // Check immutability + $this->assertNotSame($this->cache, $cache); + + foreach ($options as $key => $value) { + $this->assertNotEquals($value, $this->cache->getOption($key)); + } + } + + + /** + * @return array + */ + public function dataProviderForOptionsException() + { + return [ + ['ttl', 0, InvalidArgumentException::class], + ['foo', 'bar', InvalidArgumentException::class] + ]; + } + + /** + * @dataProvider dataProviderForOptionsException + * + * @param string $key + * @param mixed $value + * @param string $expectedException + */ + public function testWithOptionException($key, $value, $expectedException) + { + $this->expectException($expectedException); + $this->createSimpleCache()->withOption($key, $value); + } +} diff --git a/msd/vendor/desarrolla2/cache/tests/ApcuCacheTest.php b/msd/vendor/desarrolla2/cache/tests/ApcuCacheTest.php new file mode 100644 index 0000000..3c5db66 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/ApcuCacheTest.php @@ -0,0 +1,44 @@ + + */ + +namespace Desarrolla2\Test\Cache; + +use Desarrolla2\Cache\Apcu as ApcuCache; + +/** + * ApcuCacheTest + */ +class ApcuCacheTest extends AbstractCacheTest +{ + public static function setUpBeforeClass(): void + { + // Required to check the TTL for new entries + ini_set('apc.use_request_time', false); + } + + public function createSimpleCache() + { + if (!extension_loaded('apcu')) { + $this->markTestSkipped( + 'The APCu extension is not available.' + ); + } + if (!ini_get('apc.enable_cli')) { + $this->markTestSkipped( + 'You need to enable apc.enable_cli' + ); + } + + return new ApcuCache(); + } +} diff --git a/msd/vendor/desarrolla2/cache/tests/ChainTest.php b/msd/vendor/desarrolla2/cache/tests/ChainTest.php new file mode 100644 index 0000000..e58d41f --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/ChainTest.php @@ -0,0 +1,216 @@ + + */ + +namespace Desarrolla2\Test\Cache; + +use Desarrolla2\Cache\Chain as CacheChain; +use Desarrolla2\Cache\Memory as MemoryCache; + +/** + * ChainTest + */ +class ChainTest extends AbstractCacheTest +{ + public function createSimpleCache() + { + $adapters = [new MemoryCache()]; // For the general PSR-16 tests, we don't need more than 1 adapter + + return new CacheChain($adapters); + } + + + public function tearDown(): void + { + // No need to clear cache, as the adapters don't persist between tests. + } + + + public function testChainSet() + { + $adapter1 = $this->createMock(MemoryCache::class); + $adapter1->expects($this->once())->method('set')->with("foo", "bar", 300); + + $adapter2 = $this->createMock(MemoryCache::class); + $adapter2->expects($this->once())->method('set')->with("foo", "bar", 300); + + $cache = new CacheChain([$adapter1, $adapter2]); + + $cache->set("foo", "bar", 300); + } + + public function testChainSetMultiple() + { + $adapter1 = $this->createMock(MemoryCache::class); + $adapter1->expects($this->once())->method('setMultiple')->with(["foo" => 1, "bar" => 2], 300); + + $adapter2 = $this->createMock(MemoryCache::class); + $adapter2->expects($this->once())->method('setMultiple')->with(["foo" => 1, "bar" => 2], 300); + + $cache = new CacheChain([$adapter1, $adapter2]); + + $cache->setMultiple(["foo" => 1, "bar" => 2], 300); + } + + + public function testChainGetFirst() + { + $adapter1 = $this->createMock(MemoryCache::class); + $adapter1->expects($this->once())->method('get')->with("foo")->willReturn("bar"); + + $adapter2 = $this->createMock(MemoryCache::class); + $adapter2->expects($this->never())->method('get'); + + $cache = new CacheChain([$adapter1, $adapter2]); + + $this->assertEquals("bar", $cache->get("foo", 42)); + } + + public function testChainGetSecond() + { + $adapter1 = $this->createMock(MemoryCache::class); + $adapter1->expects($this->once())->method('get')->with("foo")->willReturn(null); + + $adapter2 = $this->createMock(MemoryCache::class); + $adapter2->expects($this->once())->method('get')->with("foo")->willReturn("car"); + + $cache = new CacheChain([$adapter1, $adapter2]); + + $this->assertEquals("car", $cache->get("foo", 42)); + } + + public function testChainGetNone() + { + $adapter1 = $this->createMock(MemoryCache::class); + $adapter1->expects($this->once())->method('get')->with("foo")->willReturn(null); + + $adapter2 = $this->createMock(MemoryCache::class); + $adapter2->expects($this->once())->method('get')->with("foo")->willReturn(null); + + $cache = new CacheChain([$adapter1, $adapter2]); + + $this->assertEquals(42, $cache->get("foo", 42)); + } + + + public function testChainGetMultipleFirst() + { + $adapter1 = $this->createMock(MemoryCache::class); + $adapter1->expects($this->once())->method('getMultiple')->with(["foo", "bar"]) + ->willReturn(["foo" => 1, "bar" => 2]); + + $adapter2 = $this->createMock(MemoryCache::class); + $adapter2->expects($this->never())->method('getMultiple'); + + $cache = new CacheChain([$adapter1, $adapter2]); + + $this->assertEquals(["foo" => 1, "bar" => 2], $cache->getMultiple(["foo", "bar"])); + } + + public function testChainGetMultipleMixed() + { + $adapter1 = $this->createMock(MemoryCache::class); + $adapter1->expects($this->once())->method('getMultiple') + ->with($this->equalTo(["foo", "bar", "wux", "lot"])) + ->willReturn(["foo" => null, "bar" => 2, "wux" => null, "lot" => null]); + + $adapter2 = $this->createMock(MemoryCache::class); + $adapter2->expects($this->once())->method('getMultiple') + ->with($this->equalTo(["foo", "wux", "lot"])) + ->willReturn(["foo" => 11, "wux" => 15, "lot" => null]); + + $cache = new CacheChain([$adapter1, $adapter2]); + + $expected = ["foo" => 11, "bar" => 2, "wux" => 15, "lot" => 42]; + $this->assertEquals($expected, $cache->getMultiple(["foo", "bar", "wux", "lot"], 42)); + } + + + public function testChainHasFirst() + { + $adapter1 = $this->createMock(MemoryCache::class); + $adapter1->expects($this->once())->method('has')->with("foo")->willReturn(true); + + $adapter2 = $this->createMock(MemoryCache::class); + $adapter2->expects($this->never())->method('has'); + + $cache = new CacheChain([$adapter1, $adapter2]); + + $this->assertTrue($cache->has("foo")); + } + + public function testChainHasSecond() + { + $adapter1 = $this->createMock(MemoryCache::class); + $adapter1->expects($this->once())->method('has')->with("foo")->willReturn(false); + + $adapter2 = $this->createMock(MemoryCache::class); + $adapter2->expects($this->once())->method('has')->with("foo")->willReturn(true); + + $cache = new CacheChain([$adapter1, $adapter2]); + + $this->assertTrue($cache->has("foo")); + } + + public function testChainHasNone() + { + $adapter1 = $this->createMock(MemoryCache::class); + $adapter1->expects($this->once())->method('has')->with("foo")->willReturn(false); + + $adapter2 = $this->createMock(MemoryCache::class); + $adapter2->expects($this->once())->method('has')->with("foo")->willReturn(false); + + $cache = new CacheChain([$adapter1, $adapter2]); + + $this->assertFalse($cache->has("foo")); + } + + + public function testChainDelete() + { + $adapter1 = $this->createMock(MemoryCache::class); + $adapter1->expects($this->once())->method('delete')->with("foo"); + + $adapter2 = $this->createMock(MemoryCache::class); + $adapter2->expects($this->once())->method('delete')->with("foo"); + + $cache = new CacheChain([$adapter1, $adapter2]); + + $cache->delete("foo"); + } + + public function testChainDeleteMultiple() + { + $adapter1 = $this->createMock(MemoryCache::class); + $adapter1->expects($this->once())->method('deleteMultiple')->with(["foo", "bar"]); + + $adapter2 = $this->createMock(MemoryCache::class); + $adapter2->expects($this->once())->method('deleteMultiple')->with(["foo", "bar"]); + + $cache = new CacheChain([$adapter1, $adapter2]); + + $cache->deleteMultiple(["foo", "bar"]); + } + + public function testChainClear() + { + $adapter1 = $this->createMock(MemoryCache::class); + $adapter1->expects($this->once())->method('clear'); + + $adapter2 = $this->createMock(MemoryCache::class); + $adapter2->expects($this->once())->method('clear'); + + $cache = new CacheChain([$adapter1, $adapter2]); + + $cache->clear(); + } +} diff --git a/msd/vendor/desarrolla2/cache/tests/FileTest.php b/msd/vendor/desarrolla2/cache/tests/FileTest.php new file mode 100644 index 0000000..6c5d5ac --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/FileTest.php @@ -0,0 +1,40 @@ + + */ + +namespace Desarrolla2\Test\Cache; + +use Desarrolla2\Cache\File as FileCache; +use org\bovigo\vfs\vfsStream; +use org\bovigo\vfs\vfsStreamDirectory; + +/** + * FileTest + */ +class FileTest extends AbstractCacheTest +{ + /** + * @var vfsStreamDirectory + */ + private $root; + + protected $skippedTests = [ + 'testBasicUsageWithLongKey' => 'Only support keys up to 64 bytes' + ]; + + public function createSimpleCache() + { + $this->root = vfsStream::setup('cache'); + + return new FileCache(vfsStream::url('cache')); + } +} diff --git a/msd/vendor/desarrolla2/cache/tests/FileTrieTest.php b/msd/vendor/desarrolla2/cache/tests/FileTrieTest.php new file mode 100644 index 0000000..c137d1c --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/FileTrieTest.php @@ -0,0 +1,43 @@ + + * @author Arnold Daniels + */ + +namespace Desarrolla2\Test\Cache; + +use Desarrolla2\Cache\File as FileCache; +use Desarrolla2\Cache\File\TrieFilename; +use org\bovigo\vfs\vfsStream; +use org\bovigo\vfs\vfsStreamDirectory; + +/** + * FileTest with Trie structure + */ +class FileTrieTest extends AbstractCacheTest +{ + /** + * @var vfsStreamDirectory + */ + private $root; + + protected $skippedTests = [ + 'testBasicUsageWithLongKey' => 'Only support keys up to 64 bytes' + ]; + + public function createSimpleCache() + { + $this->root = vfsStream::setup('cache'); + + return (new FileCache(vfsStream::url('cache'))) + ->withOption('filename', new TrieFilename('%s.php.cache',4)); + } +} diff --git a/msd/vendor/desarrolla2/cache/tests/FileTtlFileTest.php b/msd/vendor/desarrolla2/cache/tests/FileTtlFileTest.php new file mode 100644 index 0000000..2b526a1 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/FileTtlFileTest.php @@ -0,0 +1,42 @@ + + * @author Arnold Daniels + */ + +namespace Desarrolla2\Test\Cache; + +use Desarrolla2\Cache\File as FileCache; +use org\bovigo\vfs\vfsStream; +use org\bovigo\vfs\vfsStreamDirectory; + +/** + * FileTest + */ +class FileTtlFileTest extends AbstractCacheTest +{ + /** + * @var vfsStreamDirectory + */ + private $root; + + protected $skippedTests = [ + 'testBasicUsageWithLongKey' => 'Only support keys up to 64 bytes' + ]; + + public function createSimpleCache() + { + $this->root = vfsStream::setup('cache'); + + return (new FileCache(vfsStream::url('cache'))) + ->withOption('ttl-strategy', 'file'); + } +} diff --git a/msd/vendor/desarrolla2/cache/tests/MemcachedTest.php b/msd/vendor/desarrolla2/cache/tests/MemcachedTest.php new file mode 100644 index 0000000..9b1ccd0 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/MemcachedTest.php @@ -0,0 +1,47 @@ + + */ + +namespace Desarrolla2\Test\Cache; + +use Desarrolla2\Cache\Memcached as MemcachedCache; +use Memcached; + +/** + * MemcachedTest + */ +class MemcachedTest extends AbstractCacheTest +{ + protected $skippedTests = [ + 'testBasicUsageWithLongKey' => 'Only support keys up to 250 bytes' + ]; + + public function createSimpleCache() + { + if (!extension_loaded('memcached') || !class_exists('\Memcached')) { + $this->markTestSkipped( + 'The Memcached extension is not available.' + ); + } + + list($host, $port) = explode(':', CACHE_TESTS_MEMCACHED_SERVER) + [1 => 11211]; + + $adapter = new Memcached(); + $adapter->addServer($host, (int)$port); + + if (!$adapter->flush()) { + $this->markTestSkipped("Unable to flush Memcached; not running?"); + } + + return new MemcachedCache($adapter); + } +} diff --git a/msd/vendor/desarrolla2/cache/tests/MemoryTest.php b/msd/vendor/desarrolla2/cache/tests/MemoryTest.php new file mode 100644 index 0000000..d189293 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/MemoryTest.php @@ -0,0 +1,44 @@ + + */ + +namespace Desarrolla2\Test\Cache; + +use Desarrolla2\Cache\Memory as MemoryCache; + +/** + * MemoryTest + */ +class MemoryTest extends AbstractCacheTest +{ + public function createSimpleCache() + { + return new MemoryCache(); + } + + public function tearDown(): void + { + // No need to clear cache, as the adapters don't persist between tests. + } + + public function testExceededLimit() + { + $cache = $this->createSimpleCache()->withOption('limit', 1); + + $cache->set('foo', 1); + $this->assertTrue($cache->has('foo')); + + $cache->set('bar', 1); + $this->assertFalse($cache->has('foo')); + $this->assertTrue($cache->has('bar')); + } +} diff --git a/msd/vendor/desarrolla2/cache/tests/MongoDBTest.php b/msd/vendor/desarrolla2/cache/tests/MongoDBTest.php new file mode 100644 index 0000000..bcc6597 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/MongoDBTest.php @@ -0,0 +1,53 @@ + + */ + +namespace Desarrolla2\Test\Cache; + +use Desarrolla2\Cache\MongoDB as MongoDBCache; +use MongoDB\Client; + +/** + * MongoDBTest + */ +class MongoDBTest extends AbstractCacheTest +{ + /** + * @var Client + */ + protected static $client; + + /** + * Use one client per test, as the MongoDB extension leaves connections open + */ + public static function setUpBeforeClass(): void + { + if (!extension_loaded('mongodb')) { + return; + } + + self::$client = new Client(CACHE_TESTS_MONGO_DSN); + self::$client->listDatabases(); // Fail if unable to connect + } + + public function createSimpleCache() + { + if (!isset(self::$client)) { + $this->markTestSkipped('The mongodb extension is not available'); + } + + $collection = self::$client->selectCollection(CACHE_TESTS_MONGO_DATABASE, 'cache'); + + return (new MongoDBCache($collection)) + ->withOption('initialize', false); + } +} diff --git a/msd/vendor/desarrolla2/cache/tests/MysqliTest.php b/msd/vendor/desarrolla2/cache/tests/MysqliTest.php new file mode 100644 index 0000000..c486c2f --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/MysqliTest.php @@ -0,0 +1,75 @@ + + */ + +namespace Desarrolla2\Test\Cache; + +use Desarrolla2\Cache\Mysqli as MysqliCache; + +/** + * MysqliTest + */ +class MysqliTest extends AbstractCacheTest +{ + /** + * @var \mysqli + */ + protected static $mysqli; + + protected $skippedTests = [ + 'testBasicUsageWithLongKey' => 'Only support keys up to 255 bytes' + ]; + + public static function setUpBeforeClass(): void + { + if (class_exists('mysqli')) { + static::$mysqli = new \mysqli( + ini_get('mysqli.default_host') ?: 'localhost', + ini_get('mysqli.default_user') ?: 'root' + ); + } + } + + public function init(): void + { + if (!class_exists('mysqli')) { + $this->markTestSkipped("mysqli extension not loaded"); + } + + try { + static::$mysqli->query('CREATE DATABASE IF NOT EXISTS `' . CACHE_TESTS_MYSQLI_DATABASE . '`'); + static::$mysqli->select_db(CACHE_TESTS_MYSQLI_DATABASE); + + static::$mysqli->query("CREATE TABLE IF NOT EXISTS `cache` " + ."( `key` VARCHAR(255), `value` BLOB, `ttl` INT UNSIGNED, PRIMARY KEY (`key`) )"); + } catch (\Exception $e) { + $this->markTestSkipped("skipping mysqli test; " . $e->getMessage()); + } + + if (static::$mysqli->error) { + $this->markTestSkipped(static::$mysqli->error); + } + } + + public function createSimpleCache() + { + $this->init(); + + return (new MysqliCache(static::$mysqli)) + ->withOption('initialize', false); + } + + public static function tearDownAfterClass(): void + { + static::$mysqli->query('DROP DATABASE IF EXISTS `' . CACHE_TESTS_MYSQLI_DATABASE . '`'); + static::$mysqli->close(); + } +} diff --git a/msd/vendor/desarrolla2/cache/tests/NotCacheTest.php b/msd/vendor/desarrolla2/cache/tests/NotCacheTest.php new file mode 100644 index 0000000..b4d3a55 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/NotCacheTest.php @@ -0,0 +1,88 @@ + + */ + +namespace Desarrolla2\Test\Cache; + +use Desarrolla2\Cache\NotCache as NotCache; +use PHPUnit\Framework\TestCase; + +/** + * NotCacheTest + */ +class NotCacheTest extends TestCase +{ + /** + * @var \Desarrolla2\Cache\NotCache + */ + protected $cache; + + public function setUp(): void + { + $this->cache = new NotCache(); + } + + /** + * @return array + */ + public function dataProvider() + { + return array( + array(), + ); + } + + /** + * @dataProvider dataProvider + */ + public function testHas() + { + $this->cache->set('key', 'value'); + $this->assertFalse($this->cache->has('key')); + } + + /** + * @dataProvider dataProvider + */ + public function testGet() + { + $this->cache->set('key', 'value'); + $this->assertFalse($this->cache->get('key', false)); + } + + /** + * @dataProvider dataProvider + */ + public function testSet() + { + $this->assertFalse($this->cache->set('key', 'value')); + } + + /** + * @dataProvider dataProvider + */ + public function testDelete() + { + $this->assertTrue($this->cache->delete('key')); + } + + /** + * @dataProvider dataProvider + */ + public function testWithOption() + { + $cache = $this->cache->withOption('ttl', 3600); + $this->assertSame(3600, $cache->getOption('ttl')); + + $this->assertNotSame($this->cache, $cache); + } +} diff --git a/msd/vendor/desarrolla2/cache/tests/PhpFileTest.php b/msd/vendor/desarrolla2/cache/tests/PhpFileTest.php new file mode 100644 index 0000000..df03f94 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/PhpFileTest.php @@ -0,0 +1,40 @@ + + */ + +namespace Desarrolla2\Test\Cache; + +use Desarrolla2\Cache\PhpFile as PhpFileCache; +use org\bovigo\vfs\vfsStream; +use org\bovigo\vfs\vfsStreamDirectory; + +/** + * FileTest with PhpPacker + */ +class PhpFileTest extends AbstractCacheTest +{ + /** + * @var vfsStreamDirectory + */ + private $root; + + protected $skippedTests = [ + 'testBasicUsageWithLongKey' => 'Only support keys up to 64 bytes' + ]; + + public function createSimpleCache() + { + $this->root = vfsStream::setup('cache'); + + return new PhpFileCache(vfsStream::url('cache')); + } +} diff --git a/msd/vendor/desarrolla2/cache/tests/PredisTest.php b/msd/vendor/desarrolla2/cache/tests/PredisTest.php new file mode 100644 index 0000000..0f9645b --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/PredisTest.php @@ -0,0 +1,52 @@ + + */ + +namespace Desarrolla2\Test\Cache; + +use Desarrolla2\Cache\Predis as PredisCache; +use Predis\Client; +use Predis\Connection\ConnectionException; + +/** + * PredisTest + */ +class PredisTest extends AbstractCacheTest +{ + /** + * @var Client + */ + protected $client; + + public function createSimpleCache() + { + if (!class_exists('Predis\Client')) { + $this->markTestSkipped('The predis library is not available'); + } + + try { + $this->client = new Client(CACHE_TESTS_PREDIS_DSN, ['exceptions' => false]); + $this->client->connect(); + } catch (ConnectionException $e) { + $this->markTestSkipped($e->getMessage()); + } + + return new PredisCache($this->client); + } + + public function tearDown(): void + { + parent::tearDown(); + + $this->client->disconnect(); + } +} diff --git a/msd/vendor/desarrolla2/cache/tests/performance/Apc.php b/msd/vendor/desarrolla2/cache/tests/performance/Apc.php new file mode 100644 index 0000000..df019dc --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/performance/Apc.php @@ -0,0 +1,21 @@ + + */ + +require_once __DIR__.'/../bootstrap.php'; + +use Desarrolla2\Cache\Cache; +use Desarrolla2\Cache\Adapter\Apcu; + +$cache = new Cache(new Apcu()); + +require_once __DIR__.'/common.php'; diff --git a/msd/vendor/desarrolla2/cache/tests/performance/File.php b/msd/vendor/desarrolla2/cache/tests/performance/File.php new file mode 100644 index 0000000..506bef9 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/performance/File.php @@ -0,0 +1,21 @@ + + */ + +require_once __DIR__.'/../bootstrap.php'; + +use Desarrolla2\Cache\Cache; +use Desarrolla2\Cache\Adapter\File; + +$cache = new Cache(new File('/tmp')); + +require_once __DIR__.'/common.php'; diff --git a/msd/vendor/desarrolla2/cache/tests/performance/Mongo.php b/msd/vendor/desarrolla2/cache/tests/performance/Mongo.php new file mode 100644 index 0000000..c67b866 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/performance/Mongo.php @@ -0,0 +1,21 @@ + + */ + +require_once __DIR__.'/../bootstrap.php'; + +use Desarrolla2\Cache\Cache; +use Desarrolla2\Cache\Adapter\Mongo; + +$cache = new Cache(new Mongo('mongodb://localhost:27017')); + +require_once __DIR__.'/common.php'; diff --git a/msd/vendor/desarrolla2/cache/tests/performance/NoCache.php b/msd/vendor/desarrolla2/cache/tests/performance/NoCache.php new file mode 100644 index 0000000..b84e030 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/performance/NoCache.php @@ -0,0 +1,21 @@ + + */ + +require_once __DIR__.'/../bootstrap.php'; + +use Desarrolla2\Cache\Cache; +use Desarrolla2\Cache\Adapter\NotCache; + +$cache = new Cache(new NotCache()); + +require_once __DIR__.'/common.php'; diff --git a/msd/vendor/desarrolla2/cache/tests/performance/common.php b/msd/vendor/desarrolla2/cache/tests/performance/common.php new file mode 100644 index 0000000..9085438 --- /dev/null +++ b/msd/vendor/desarrolla2/cache/tests/performance/common.php @@ -0,0 +1,43 @@ + + */ + + +//build test data outside of timing loop +$data = []; +for ($i = 1; $i <= 10000; $i++) { + $data[$i] = md5($i); +} + +$timer = new \Desarrolla2\Timer\Timer(new \Desarrolla2\Timer\Formatter\Human()); +for ($i = 1; $i <= 10000; $i++) { + $cache->set($data[$i], $data[$i], 3600); +} +$timer->mark('10.000 set'); +for ($i = 1; $i <= 10000; $i++) { + $cache->has($data[$i]); +} +$timer->mark('10.000 has'); +for ($i = 1; $i <= 10000; $i++) { + $cache->get($data[$i]); +} +$timer->mark('10.000 get'); +for ($i = 1; $i <= 10000; $i++) { + $cache->has($data[$i]); + $cache->get($data[$i]); +} +$timer->mark('10.000 has+get combos'); + +$benchmarks = $timer->getAll(); +foreach ($benchmarks as $benchmark) { + ld($benchmark); +} diff --git a/msd/vendor/league/flysystem-sftp/ConnectionProvider.php b/msd/vendor/league/flysystem-sftp/ConnectionProvider.php new file mode 100644 index 0000000..21f83bc --- /dev/null +++ b/msd/vendor/league/flysystem-sftp/ConnectionProvider.php @@ -0,0 +1,12 @@ +succeedAfter = $succeedAfter; + } + + public function isConnected(SFTP $connection): bool + { + if ($this->numberOfTimesChecked >= $this->succeedAfter) { + return true; + } + + $this->numberOfTimesChecked++; + + return false; + } +} diff --git a/msd/vendor/league/flysystem-sftp/README.md b/msd/vendor/league/flysystem-sftp/README.md new file mode 100644 index 0000000..8aa043f --- /dev/null +++ b/msd/vendor/league/flysystem-sftp/README.md @@ -0,0 +1,7 @@ +## Sub-split of Flysystem for SFTP using phpseclib2. + +```bash +composer require league/flysystem-sftp +``` + +View the [documentation](https://flysystem.thephpleague.com/v2/docs/adapter/sftp/). diff --git a/msd/vendor/league/flysystem-sftp/SftpAdapter.php b/msd/vendor/league/flysystem-sftp/SftpAdapter.php new file mode 100644 index 0000000..b806bf5 --- /dev/null +++ b/msd/vendor/league/flysystem-sftp/SftpAdapter.php @@ -0,0 +1,334 @@ +connectionProvider = $connectionProvider; + $this->prefixer = new PathPrefixer($root); + $this->visibilityConverter = $visibilityConverter ?: new PortableVisibilityConverter(); + $this->mimeTypeDetector = $mimeTypeDetector ?: new FinfoMimeTypeDetector(); + } + + public function fileExists(string $path): bool + { + $location = $this->prefixer->prefixPath($path); + + return $this->connectionProvider->provideConnection()->is_file($location); + } + + /** + * @param string $path + * @param string|resource $contents + * @param Config $config + * + * @throws FilesystemException + */ + private function upload(string $path, $contents, Config $config): void + { + $this->ensureParentDirectoryExists($path, $config); + $connection = $this->connectionProvider->provideConnection(); + $location = $this->prefixer->prefixPath($path); + + if ( ! $connection->put($location, $contents, SFTP::SOURCE_STRING)) { + throw UnableToWriteFile::atLocation($path, 'not able to write the file'); + } + + if ($visibility = $config->get(Config::OPTION_VISIBILITY)) { + $this->setVisibility($path, $visibility); + } + } + + private function ensureParentDirectoryExists(string $path, Config $config): void + { + $parentDirectory = dirname($path); + + if ($parentDirectory === '' || $parentDirectory === '.') { + return; + } + + /** @var string $visibility */ + $visibility = $config->get(Config::OPTION_DIRECTORY_VISIBILITY); + $this->makeDirectory($parentDirectory, $visibility); + } + + private function makeDirectory(string $directory, ?string $visibility): void + { + $location = $this->prefixer->prefixPath($directory); + $connection = $this->connectionProvider->provideConnection(); + + if ($connection->is_dir($location)) { + return; + } + + $mode = $visibility ? $this->visibilityConverter->forDirectory( + $visibility + ) : $this->visibilityConverter->defaultForDirectories(); + + if ( ! $connection->mkdir($location, $mode, true)) { + throw UnableToCreateDirectory::atLocation($directory); + } + } + + public function write(string $path, string $contents, Config $config): void + { + try { + $this->upload($path, $contents, $config); + } catch (UnableToWriteFile $exception) { + throw $exception; + } catch (Throwable $exception) { + throw UnableToWriteFile::atLocation($path, '', $exception); + } + } + + public function writeStream(string $path, $contents, Config $config): void + { + try { + $this->upload($path, $contents, $config); + } catch (UnableToWriteFile $exception) { + throw $exception; + } catch (Throwable $exception) { + throw UnableToWriteFile::atLocation($path, '', $exception); + } + } + + public function read(string $path): string + { + $location = $this->prefixer->prefixPath($path); + $connection = $this->connectionProvider->provideConnection(); + $contents = $connection->get($location); + + if ( ! is_string($contents)) { + throw UnableToReadFile::fromLocation($path); + } + + return $contents; + } + + public function readStream(string $path) + { + $location = $this->prefixer->prefixPath($path); + $connection = $this->connectionProvider->provideConnection(); + /** @var resource $readStream */ + $readStream = fopen('php://temp', 'w+'); + + if ( ! $connection->get($location, $readStream)) { + fclose($readStream); + throw UnableToReadFile::fromLocation($path); + } + + rewind($readStream); + + return $readStream; + } + + public function delete(string $path): void + { + $location = $this->prefixer->prefixPath($path); + $connection = $this->connectionProvider->provideConnection(); + $connection->delete($location); + } + + public function deleteDirectory(string $path): void + { + $location = $this->prefixer->prefixPath($path); + $connection = $this->connectionProvider->provideConnection(); + $connection->delete(rtrim($location, '/') . '/'); + } + + public function createDirectory(string $path, Config $config): void + { + $this->makeDirectory($path, $config->get(Config::OPTION_DIRECTORY_VISIBILITY)); + } + + public function setVisibility(string $path, string $visibility): void + { + $location = $this->prefixer->prefixPath($path); + $connection = $this->connectionProvider->provideConnection(); + $mode = $this->visibilityConverter->forFile($visibility); + + if ( ! $connection->chmod($mode, $location, false)) { + throw UnableToSetVisibility::atLocation($path); + } + } + + private function fetchFileMetadata(string $path, string $type): FileAttributes + { + $location = $this->prefixer->prefixPath($path); + $connection = $this->connectionProvider->provideConnection(); + $stat = $connection->stat($location); + + if ( ! is_array($stat)) { + throw UnableToRetrieveMetadata::create($path, $type); + } + + $attributes = $this->convertListingToAttributes($path, $stat); + + if ( ! $attributes instanceof FileAttributes) { + throw UnableToRetrieveMetadata::create($path, $type, 'path is not a file'); + } + + return $attributes; + } + + public function mimeType(string $path): FileAttributes + { + try { + $contents = $this->read($path); + $mimetype = $this->mimeTypeDetector->detectMimeType($path, $contents); + } catch (Throwable $exception) { + throw UnableToRetrieveMetadata::mimeType($path, '', $exception); + } + + if ($mimetype === null) { + throw UnableToRetrieveMetadata::mimeType($path, 'Unknown.'); + } + + return new FileAttributes($path, null, null, null, $mimetype); + } + + public function lastModified(string $path): FileAttributes + { + return $this->fetchFileMetadata($path, FileAttributes::ATTRIBUTE_LAST_MODIFIED); + } + + public function fileSize(string $path): FileAttributes + { + return $this->fetchFileMetadata($path, FileAttributes::ATTRIBUTE_FILE_SIZE); + } + + public function visibility(string $path): FileAttributes + { + return $this->fetchFileMetadata($path, FileAttributes::ATTRIBUTE_VISIBILITY); + } + + public function listContents(string $path, bool $deep): iterable + { + $connection = $this->connectionProvider->provideConnection(); + $location = $this->prefixer->prefixPath(rtrim($path, '/')) . '/'; + $listing = $connection->rawlist($location, false); + + if ($listing === false) { + return; + } + + foreach ($listing as $filename => $attributes) { + if ($filename === '.' || $filename === '..') { + continue; + } + + // Ensure numeric keys are strings. + $filename = (string) $filename; + $path = $this->prefixer->stripPrefix($location . ltrim($filename, '/')); + $attributes = $this->convertListingToAttributes($path, $attributes); + yield $attributes; + + if ($deep && $attributes->isDir()) { + foreach ($this->listContents($attributes->path(), true) as $child) { + yield $child; + } + } + } + } + + private function convertListingToAttributes(string $path, array $attributes): StorageAttributes + { + $permissions = $attributes['permissions'] & 0777; + $lastModified = $attributes['mtime'] ?? null; + + if ($attributes['type'] === NET_SFTP_TYPE_DIRECTORY) { + return new DirectoryAttributes( + ltrim($path, '/'), + $this->visibilityConverter->inverseForDirectory($permissions), + $lastModified + ); + } + + return new FileAttributes( + $path, + $attributes['size'], + $this->visibilityConverter->inverseForFile($permissions), + $lastModified + ); + } + + public function move(string $source, string $destination, Config $config): void + { + $sourceLocation = $this->prefixer->prefixPath($source); + $destinationLocation = $this->prefixer->prefixPath($destination); + $connection = $this->connectionProvider->provideConnection(); + + try { + $this->ensureParentDirectoryExists($destination, $config); + } catch (Throwable $exception) { + throw UnableToMoveFile::fromLocationTo($source, $destination, $exception); + } + + if ( ! $connection->rename($sourceLocation, $destinationLocation)) { + throw UnableToMoveFile::fromLocationTo($source, $destination); + } + } + + public function copy(string $source, string $destination, Config $config): void + { + try { + $readStream = $this->readStream($source); + $visibility = $this->visibility($source)->visibility(); + $this->writeStream($destination, $readStream, new Config(compact('visibility'))); + } catch (Throwable $exception) { + if (isset($readStream) && is_resource($readStream)) { + @fclose($readStream); + } + throw UnableToCopyFile::fromLocationTo($source, $destination, $exception); + } + } +} diff --git a/msd/vendor/league/flysystem-sftp/SftpConnectionProvider.php b/msd/vendor/league/flysystem-sftp/SftpConnectionProvider.php new file mode 100644 index 0000000..3996425 --- /dev/null +++ b/msd/vendor/league/flysystem-sftp/SftpConnectionProvider.php @@ -0,0 +1,236 @@ +host = $host; + $this->username = $username; + $this->password = $password; + $this->privateKey = $privateKey; + $this->passphrase = $passphrase; + $this->useAgent = $useAgent; + $this->port = $port; + $this->timeout = $timeout; + $this->hostFingerprint = $hostFingerprint; + $this->connectivityChecker = $connectivityChecker ?: new SimpleConnectivityChecker(); + $this->maxTries = $maxTries; + } + + public function provideConnection(): SFTP + { + $tries = 0; + start: + + $connection = $this->connection instanceof SFTP + ? $this->connection + : $this->setupConnection(); + + if ( ! $this->connectivityChecker->isConnected($connection)) { + $connection->disconnect(); + $this->connection = null; + + if ($tries < $this->maxTries) { + $tries++; + goto start; + } + + throw UnableToConnectToSftpHost::atHostname($this->host); + } + + return $this->connection = $connection; + } + + private function setupConnection(): SFTP + { + $connection = new SFTP($this->host, $this->port, $this->timeout); + $connection->disableStatCache(); + + try { + $this->checkFingerprint($connection); + $this->authenticate($connection); + } catch (Throwable $exception) { + $connection->disconnect(); + throw $exception; + } + + return $connection; + } + + private function checkFingerprint(SFTP $connection): void + { + if ( ! $this->hostFingerprint) { + return; + } + + $publicKey = $connection->getServerPublicHostKey(); + + if ($publicKey === false) { + throw UnableToEstablishAuthenticityOfHost::becauseTheAuthenticityCantBeEstablished($this->host); + } + + $fingerprint = $this->getFingerprintFromPublicKey($publicKey); + + if (0 !== strcasecmp($this->hostFingerprint, $fingerprint)) { + throw UnableToEstablishAuthenticityOfHost::becauseTheAuthenticityCantBeEstablished($this->host); + } + } + + private function getFingerprintFromPublicKey(string $publicKey): string + { + $content = explode(' ', $publicKey, 3); + + return implode(':', str_split(md5(base64_decode($content[1])), 2)); + } + + private function authenticate(SFTP $connection): void + { + if ($this->privateKey !== null) { + $this->authenticateWithPrivateKey($connection); + } elseif ($this->useAgent) { + $this->authenticateWithAgent($connection); + } elseif ( ! $connection->login($this->username, $this->password)) { + throw UnableToAuthenticate::withPassword(); + } + } + + public static function fromArray(array $options): SftpConnectionProvider + { + return new SftpConnectionProvider( + $options['host'], + $options['username'], + $options['password'] ?? null, + $options['privateKey'] ?? null, + $options['passphrase'] ?? null, + $options['port'] ?? 22, + $options['useAgent'] ?? false, + $options['timeout'] ?? 10, + $options['maxTries'] ?? 4, + $options['hostFingerprint'] ?? null, + $options['connectivityChecker'] ?? null + ); + } + + private function authenticateWithPrivateKey(SFTP $connection): void + { + $privateKey = $this->loadPrivateKey(); + + if ($connection->login($this->username, $privateKey)) { + return; + } + + if ($this->password !== null && $connection->login($this->username, $this->password)) { + return; + } + + throw UnableToAuthenticate::withPrivateKey(); + } + + private function loadPrivateKey(): RSA + { + if ("---" !== substr($this->privateKey, 0, 3) && is_file($this->privateKey)) { + $this->privateKey = file_get_contents($this->privateKey); + } + + $key = new RSA(); + + if ($this->passphrase !== null) { + $key->setPassword($this->passphrase); + } + + if ( ! $key->loadKey($this->privateKey)) { + throw new UnableToLoadPrivateKey(); + } + + return $key; + } + + private function authenticateWithAgent(SFTP $connection): void + { + $agent = new Agent(); + + if ( ! $connection->login($this->username, $agent)) { + throw UnableToAuthenticate::withSshAgent(); + } + } +} diff --git a/msd/vendor/league/flysystem-sftp/SimpleConnectivityChecker.php b/msd/vendor/league/flysystem-sftp/SimpleConnectivityChecker.php new file mode 100644 index 0000000..dadc662 --- /dev/null +++ b/msd/vendor/league/flysystem-sftp/SimpleConnectivityChecker.php @@ -0,0 +1,15 @@ +isConnected(); + } +} diff --git a/msd/vendor/league/flysystem-sftp/StubSftpConnectionProvider.php b/msd/vendor/league/flysystem-sftp/StubSftpConnectionProvider.php new file mode 100644 index 0000000..f3f3eb9 --- /dev/null +++ b/msd/vendor/league/flysystem-sftp/StubSftpConnectionProvider.php @@ -0,0 +1,59 @@ +host = $host; + $this->username = $username; + $this->password = $password; + $this->port = $port; + } + + public function provideConnection(): SFTP + { + if ( ! $this->connection instanceof SFTP) { + $connection = new SftpStub($this->host, $this->port); + $connection->login($this->username, $this->password); + + $this->connection = $connection; + } + + return $this->connection; + } +} diff --git a/msd/vendor/league/flysystem-sftp/UnableToAuthenticate.php b/msd/vendor/league/flysystem-sftp/UnableToAuthenticate.php new file mode 100644 index 0000000..1f4f389 --- /dev/null +++ b/msd/vendor/league/flysystem-sftp/UnableToAuthenticate.php @@ -0,0 +1,26 @@ +options = $options; + } + + /** + * @param mixed $default + * + * @return mixed + */ + public function get(string $property, $default = null) + { + return $this->options[$property] ?? $default; + } + + public function extend(array $options): Config + { + return new Config(array_merge($this->options, $options)); + } + + public function withDefaults(array $defaults): Config + { + return new Config($this->options + $defaults); + } +} diff --git a/msd/vendor/league/flysystem/src/CorruptedPathDetected.php b/msd/vendor/league/flysystem/src/CorruptedPathDetected.php new file mode 100644 index 0000000..86ac07d --- /dev/null +++ b/msd/vendor/league/flysystem/src/CorruptedPathDetected.php @@ -0,0 +1,13 @@ +path = $path; + $this->visibility = $visibility; + $this->lastModified = $lastModified; + $this->extraMetadata = $extraMetadata; + } + + public function path(): string + { + return $this->path; + } + + public function type(): string + { + return StorageAttributes::TYPE_DIRECTORY; + } + + public function visibility(): ?string + { + return $this->visibility; + } + + public function lastModified(): ?int + { + return $this->lastModified; + } + + public function extraMetadata(): array + { + return $this->extraMetadata; + } + + public function isFile(): bool + { + return false; + } + + public function isDir(): bool + { + return true; + } + + public function withPath(string $path): StorageAttributes + { + $clone = clone $this; + $clone->path = $path; + + return $clone; + } + + public static function fromArray(array $attributes): StorageAttributes + { + return new DirectoryAttributes( + $attributes[StorageAttributes::ATTRIBUTE_PATH], + $attributes[StorageAttributes::ATTRIBUTE_VISIBILITY] ?? null, + $attributes[StorageAttributes::ATTRIBUTE_LAST_MODIFIED] ?? null, + $attributes[StorageAttributes::ATTRIBUTE_EXTRA_METADATA] ?? [] + ); + } + + /** + * @inheritDoc + */ + public function jsonSerialize(): array + { + return [ + StorageAttributes::ATTRIBUTE_TYPE => $this->type, + StorageAttributes::ATTRIBUTE_PATH => $this->path, + StorageAttributes::ATTRIBUTE_VISIBILITY => $this->visibility, + StorageAttributes::ATTRIBUTE_LAST_MODIFIED => $this->lastModified, + StorageAttributes::ATTRIBUTE_EXTRA_METADATA => $this->extraMetadata, + ]; + } +} diff --git a/msd/vendor/league/flysystem/src/DirectoryListing.php b/msd/vendor/league/flysystem/src/DirectoryListing.php new file mode 100644 index 0000000..f9ad277 --- /dev/null +++ b/msd/vendor/league/flysystem/src/DirectoryListing.php @@ -0,0 +1,84 @@ + + */ + private $listing; + + /** + * @param iterable $listing + */ + public function __construct(iterable $listing) + { + $this->listing = $listing; + } + + public function filter(callable $filter): DirectoryListing + { + $generator = (static function (iterable $listing) use ($filter): Generator { + foreach ($listing as $item) { + if ($filter($item)) { + yield $item; + } + } + })($this->listing); + + return new DirectoryListing($generator); + } + + public function map(callable $mapper): DirectoryListing + { + $generator = (static function (iterable $listing) use ($mapper): Generator { + foreach ($listing as $item) { + yield $mapper($item); + } + })($this->listing); + + return new DirectoryListing($generator); + } + + public function sortByPath(): DirectoryListing + { + $listing = $this->toArray(); + + usort($listing, function (StorageAttributes $a, StorageAttributes $b) { + return $a->path() <=> $b->path(); + }); + + return new DirectoryListing($listing); + } + + /** + * @return Traversable + */ + public function getIterator(): Traversable + { + return $this->listing instanceof Traversable + ? $this->listing + : new ArrayIterator($this->listing); + } + + /** + * @return T[] + */ + public function toArray(): array + { + return $this->listing instanceof Traversable + ? iterator_to_array($this->listing, false) + : (array) $this->listing; + } +} diff --git a/msd/vendor/league/flysystem/src/FileAttributes.php b/msd/vendor/league/flysystem/src/FileAttributes.php new file mode 100644 index 0000000..db29217 --- /dev/null +++ b/msd/vendor/league/flysystem/src/FileAttributes.php @@ -0,0 +1,139 @@ +path = $path; + $this->fileSize = $fileSize; + $this->visibility = $visibility; + $this->lastModified = $lastModified; + $this->mimeType = $mimeType; + $this->extraMetadata = $extraMetadata; + } + + public function type(): string + { + return $this->type; + } + + public function path(): string + { + return $this->path; + } + + public function fileSize(): ?int + { + return $this->fileSize; + } + + public function visibility(): ?string + { + return $this->visibility; + } + + public function lastModified(): ?int + { + return $this->lastModified; + } + + public function mimeType(): ?string + { + return $this->mimeType; + } + + public function extraMetadata(): array + { + return $this->extraMetadata; + } + + public function isFile(): bool + { + return true; + } + + public function isDir(): bool + { + return false; + } + + public function withPath(string $path): StorageAttributes + { + $clone = clone $this; + $clone->path = $path; + + return $clone; + } + + public static function fromArray(array $attributes): StorageAttributes + { + return new FileAttributes( + $attributes[StorageAttributes::ATTRIBUTE_PATH], + $attributes[StorageAttributes::ATTRIBUTE_FILE_SIZE] ?? null, + $attributes[StorageAttributes::ATTRIBUTE_VISIBILITY] ?? null, + $attributes[StorageAttributes::ATTRIBUTE_LAST_MODIFIED] ?? null, + $attributes[StorageAttributes::ATTRIBUTE_MIME_TYPE] ?? null, + $attributes[StorageAttributes::ATTRIBUTE_EXTRA_METADATA] ?? [] + ); + } + + public function jsonSerialize(): array + { + return [ + StorageAttributes::ATTRIBUTE_TYPE => self::TYPE_FILE, + StorageAttributes::ATTRIBUTE_PATH => $this->path, + StorageAttributes::ATTRIBUTE_FILE_SIZE => $this->fileSize, + StorageAttributes::ATTRIBUTE_VISIBILITY => $this->visibility, + StorageAttributes::ATTRIBUTE_LAST_MODIFIED => $this->lastModified, + StorageAttributes::ATTRIBUTE_MIME_TYPE => $this->mimeType, + StorageAttributes::ATTRIBUTE_EXTRA_METADATA => $this->extraMetadata, + ]; + } +} diff --git a/msd/vendor/league/flysystem/src/Filesystem.php b/msd/vendor/league/flysystem/src/Filesystem.php new file mode 100644 index 0000000..d878796 --- /dev/null +++ b/msd/vendor/league/flysystem/src/Filesystem.php @@ -0,0 +1,163 @@ +adapter = $adapter; + $this->config = new Config($config); + $this->pathNormalizer = $pathNormalizer ?: new WhitespacePathNormalizer(); + } + + public function fileExists(string $location): bool + { + return $this->adapter->fileExists($this->pathNormalizer->normalizePath($location)); + } + + public function write(string $location, string $contents, array $config = []): void + { + $this->adapter->write( + $this->pathNormalizer->normalizePath($location), + $contents, + $this->config->extend($config) + ); + } + + public function writeStream(string $location, $contents, array $config = []): void + { + /* @var resource $contents */ + $this->assertIsResource($contents); + $this->rewindStream($contents); + $this->adapter->writeStream( + $this->pathNormalizer->normalizePath($location), + $contents, + $this->config->extend($config) + ); + } + + public function read(string $location): string + { + return $this->adapter->read($this->pathNormalizer->normalizePath($location)); + } + + public function readStream(string $location) + { + return $this->adapter->readStream($this->pathNormalizer->normalizePath($location)); + } + + public function delete(string $location): void + { + $this->adapter->delete($this->pathNormalizer->normalizePath($location)); + } + + public function deleteDirectory(string $location): void + { + $this->adapter->deleteDirectory($this->pathNormalizer->normalizePath($location)); + } + + public function createDirectory(string $location, array $config = []): void + { + $this->adapter->createDirectory( + $this->pathNormalizer->normalizePath($location), + $this->config->extend($config) + ); + } + + public function listContents(string $location, bool $deep = self::LIST_SHALLOW): DirectoryListing + { + $path = $this->pathNormalizer->normalizePath($location); + + return new DirectoryListing($this->adapter->listContents($path, $deep)); + } + + public function move(string $source, string $destination, array $config = []): void + { + $this->adapter->move( + $this->pathNormalizer->normalizePath($source), + $this->pathNormalizer->normalizePath($destination), + $this->config->extend($config) + ); + } + + public function copy(string $source, string $destination, array $config = []): void + { + $this->adapter->copy( + $this->pathNormalizer->normalizePath($source), + $this->pathNormalizer->normalizePath($destination), + $this->config->extend($config) + ); + } + + public function lastModified(string $path): int + { + return $this->adapter->lastModified($this->pathNormalizer->normalizePath($path))->lastModified(); + } + + public function fileSize(string $path): int + { + return $this->adapter->fileSize($this->pathNormalizer->normalizePath($path))->fileSize(); + } + + public function mimeType(string $path): string + { + return $this->adapter->mimeType($this->pathNormalizer->normalizePath($path))->mimeType(); + } + + public function setVisibility(string $path, string $visibility): void + { + $this->adapter->setVisibility($this->pathNormalizer->normalizePath($path), $visibility); + } + + public function visibility(string $path): string + { + return $this->adapter->visibility($this->pathNormalizer->normalizePath($path))->visibility(); + } + + /** + * @param mixed $contents + */ + private function assertIsResource($contents): void + { + if (is_resource($contents) === false) { + throw new InvalidStreamProvided( + "Invalid stream provided, expected stream resource, received " . gettype($contents) + ); + } elseif ($type = get_resource_type($contents) !== 'stream') { + throw new InvalidStreamProvided( + "Invalid stream provided, expected stream resource, received resource of type " . $type + ); + } + } + + /** + * @param resource $resource + */ + private function rewindStream($resource): void + { + if (ftell($resource) !== 0 && stream_get_meta_data($resource)['seekable']) { + rewind($resource); + } + } +} diff --git a/msd/vendor/league/flysystem/src/FilesystemAdapter.php b/msd/vendor/league/flysystem/src/FilesystemAdapter.php new file mode 100644 index 0000000..1d5afe6 --- /dev/null +++ b/msd/vendor/league/flysystem/src/FilesystemAdapter.php @@ -0,0 +1,108 @@ + + * + * @throws FilesystemException + */ + public function listContents(string $path, bool $deep): iterable; + + /** + * @throws UnableToMoveFile + * @throws FilesystemException + */ + public function move(string $source, string $destination, Config $config): void; + + /** + * @throws UnableToCopyFile + * @throws FilesystemException + */ + public function copy(string $source, string $destination, Config $config): void; +} diff --git a/msd/vendor/league/flysystem/src/FilesystemException.php b/msd/vendor/league/flysystem/src/FilesystemException.php new file mode 100644 index 0000000..7abfa1e --- /dev/null +++ b/msd/vendor/league/flysystem/src/FilesystemException.php @@ -0,0 +1,11 @@ + + * + * @throws FilesystemException + */ + public function listContents(string $location, bool $deep = self::LIST_SHALLOW): DirectoryListing; + + /** + * @throws UnableToRetrieveMetadata + * @throws FilesystemException + */ + public function lastModified(string $path): int; + + /** + * @throws UnableToRetrieveMetadata + * @throws FilesystemException + */ + public function fileSize(string $path): int; + + /** + * @throws UnableToRetrieveMetadata + * @throws FilesystemException + */ + public function mimeType(string $path): string; + + /** + * @throws UnableToRetrieveMetadata + * @throws FilesystemException + */ + public function visibility(string $path): string; +} diff --git a/msd/vendor/league/flysystem/src/FilesystemWriter.php b/msd/vendor/league/flysystem/src/FilesystemWriter.php new file mode 100644 index 0000000..ece400d --- /dev/null +++ b/msd/vendor/league/flysystem/src/FilesystemWriter.php @@ -0,0 +1,58 @@ +prefixer = new PathPrefixer($location, DIRECTORY_SEPARATOR); + $this->writeFlags = $writeFlags; + $this->linkHandling = $linkHandling; + $this->visibility = $visibility ?: new PortableVisibilityConverter(); + $this->ensureDirectoryExists($location, $this->visibility->defaultForDirectories()); + $this->mimeTypeDetector = $mimeTypeDetector ?: new FinfoMimeTypeDetector(); + } + + public function write(string $path, string $contents, Config $config): void + { + $this->writeToFile($path, $contents, $config); + } + + public function writeStream(string $path, $contents, Config $config): void + { + $this->writeToFile($path, $contents, $config); + } + + /** + * @param resource|string $contents + */ + private function writeToFile(string $path, $contents, Config $config): void + { + $prefixedLocation = $this->prefixer->prefixPath($path); + $this->ensureDirectoryExists( + dirname($prefixedLocation), + $this->resolveDirectoryVisibility($config->get(Config::OPTION_DIRECTORY_VISIBILITY)) + ); + error_clear_last(); + + if (@file_put_contents($prefixedLocation, $contents, $this->writeFlags) === false) { + throw UnableToWriteFile::atLocation($path, error_get_last()['message'] ?? ''); + } + + if ($visibility = $config->get(Config::OPTION_VISIBILITY)) { + $this->setVisibility($path, (string) $visibility); + } + } + + public function delete(string $path): void + { + $location = $this->prefixer->prefixPath($path); + + if ( ! file_exists($location)) { + return; + } + + error_clear_last(); + + if ( ! @unlink($location)) { + throw UnableToDeleteFile::atLocation($location, error_get_last()['message'] ?? ''); + } + } + + public function deleteDirectory(string $prefix): void + { + $location = $this->prefixer->prefixPath($prefix); + + if ( ! is_dir($location)) { + return; + } + + $contents = $this->listDirectoryRecursively($location, RecursiveIteratorIterator::CHILD_FIRST); + + /** @var SplFileInfo $file */ + foreach ($contents as $file) { + if ( ! $this->deleteFileInfoObject($file)) { + throw UnableToDeleteDirectory::atLocation($prefix, "Unable to delete file at " . $file->getPathname()); + } + } + + unset($contents); + + if ( ! @rmdir($location)) { + throw UnableToDeleteDirectory::atLocation($prefix, error_get_last()['message'] ?? ''); + } + } + + private function listDirectoryRecursively( + string $path, + int $mode = RecursiveIteratorIterator::SELF_FIRST + ): Generator { + yield from new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS), + $mode + ); + } + + protected function deleteFileInfoObject(SplFileInfo $file): bool + { + switch ($file->getType()) { + case 'dir': + return @rmdir((string) $file->getRealPath()); + case 'link': + return @unlink((string) $file->getPathname()); + default: + return @unlink((string) $file->getRealPath()); + } + } + + public function listContents(string $path, bool $deep): iterable + { + $location = $this->prefixer->prefixPath($path); + + if ( ! is_dir($location)) { + return; + } + + /** @var SplFileInfo[] $iterator */ + $iterator = $deep ? $this->listDirectoryRecursively($location) : $this->listDirectory($location); + + foreach ($iterator as $fileInfo) { + if ($fileInfo->isLink()) { + if ($this->linkHandling & self::SKIP_LINKS) { + continue; + } + throw SymbolicLinkEncountered::atLocation($fileInfo->getPathname()); + } + + $path = $this->prefixer->stripPrefix($fileInfo->getPathname()); + $lastModified = $fileInfo->getMTime(); + $isDirectory = $fileInfo->isDir(); + $permissions = octdec(substr(sprintf('%o', $fileInfo->getPerms()), -4)); + $visibility = $isDirectory ? $this->visibility->inverseForDirectory($permissions) : $this->visibility->inverseForFile($permissions); + + yield $isDirectory ? new DirectoryAttributes($path, $visibility, $lastModified) : new FileAttributes( + str_replace('\\', '/', $path), + $fileInfo->getSize(), + $visibility, + $lastModified + ); + } + } + + public function move(string $source, string $destination, Config $config): void + { + $sourcePath = $this->prefixer->prefixPath($source); + $destinationPath = $this->prefixer->prefixPath($destination); + $this->ensureDirectoryExists( + dirname($destinationPath), + $this->resolveDirectoryVisibility($config->get(Config::OPTION_DIRECTORY_VISIBILITY)) + ); + + if ( ! @rename($sourcePath, $destinationPath)) { + throw UnableToMoveFile::fromLocationTo($sourcePath, $destinationPath); + } + } + + public function copy(string $source, string $destination, Config $config): void + { + $sourcePath = $this->prefixer->prefixPath($source); + $destinationPath = $this->prefixer->prefixPath($destination); + $this->ensureDirectoryExists( + dirname($destinationPath), + $this->resolveDirectoryVisibility($config->get(Config::OPTION_DIRECTORY_VISIBILITY)) + ); + + if ( ! @copy($sourcePath, $destinationPath)) { + throw UnableToCopyFile::fromLocationTo($sourcePath, $destinationPath); + } + } + + public function read(string $path): string + { + $location = $this->prefixer->prefixPath($path); + error_clear_last(); + $contents = @file_get_contents($location); + + if ($contents === false) { + throw UnableToReadFile::fromLocation($path, error_get_last()['message'] ?? ''); + } + + return $contents; + } + + public function readStream(string $path) + { + $location = $this->prefixer->prefixPath($path); + error_clear_last(); + $contents = @fopen($location, 'rb'); + + if ($contents === false) { + throw UnableToReadFile::fromLocation($path, error_get_last()['message'] ?? ''); + } + + return $contents; + } + + protected function ensureDirectoryExists(string $dirname, int $visibility): void + { + if (is_dir($dirname)) { + return; + } + + error_clear_last(); + + if ( ! @mkdir($dirname, $visibility, true)) { + $mkdirError = error_get_last(); + } + + clearstatcache(true, $dirname); + + if ( ! is_dir($dirname)) { + $errorMessage = isset($mkdirError['message']) ? $mkdirError['message'] : ''; + + throw UnableToCreateDirectory::atLocation($dirname, $errorMessage); + } + } + + public function fileExists(string $location): bool + { + $location = $this->prefixer->prefixPath($location); + + return is_file($location); + } + + public function createDirectory(string $path, Config $config): void + { + $location = $this->prefixer->prefixPath($path); + $visibility = $config->get(Config::OPTION_VISIBILITY, $config->get(Config::OPTION_DIRECTORY_VISIBILITY)); + $permissions = $this->resolveDirectoryVisibility($visibility); + + if (is_dir($location)) { + $this->setPermissions($location, $permissions); + + return; + } + + error_clear_last(); + + if ( ! @mkdir($location, $permissions, true)) { + throw UnableToCreateDirectory::atLocation($path, error_get_last()['message'] ?? ''); + } + } + + public function setVisibility(string $path, string $visibility): void + { + $path = $this->prefixer->prefixPath($path); + $visibility = is_dir($path) ? $this->visibility->forDirectory($visibility) : $this->visibility->forFile( + $visibility + ); + + $this->setPermissions($path, $visibility); + } + + public function visibility(string $path): FileAttributes + { + $location = $this->prefixer->prefixPath($path); + clearstatcache(false, $location); + error_clear_last(); + $fileperms = @fileperms($location); + + if ($fileperms === false) { + throw UnableToRetrieveMetadata::visibility($path, error_get_last()['message'] ?? ''); + } + + $permissions = $fileperms & 0777; + $visibility = $this->visibility->inverseForFile($permissions); + + return new FileAttributes($path, null, $visibility); + } + + private function resolveDirectoryVisibility(?string $visibility): int + { + return $visibility === null ? $this->visibility->defaultForDirectories() : $this->visibility->forDirectory( + $visibility + ); + } + + public function mimeType(string $path): FileAttributes + { + $location = $this->prefixer->prefixPath($path); + error_clear_last(); + $mimeType = $this->mimeTypeDetector->detectMimeTypeFromFile($location); + + if ($mimeType === null) { + throw UnableToRetrieveMetadata::mimeType($path, error_get_last()['message'] ?? ''); + } + + return new FileAttributes($path, null, null, null, $mimeType); + } + + public function lastModified(string $path): FileAttributes + { + $location = $this->prefixer->prefixPath($path); + error_clear_last(); + $lastModified = @filemtime($location); + + if ($lastModified === false) { + throw UnableToRetrieveMetadata::lastModified($path, error_get_last()['message'] ?? ''); + } + + return new FileAttributes($path, null, null, $lastModified); + } + + public function fileSize(string $path): FileAttributes + { + $location = $this->prefixer->prefixPath($path); + error_clear_last(); + + if (is_file($location) && ($fileSize = @filesize($location)) !== false) { + return new FileAttributes($path, $fileSize); + } + + throw UnableToRetrieveMetadata::fileSize($path, error_get_last()['message'] ?? ''); + } + + private function listDirectory(string $location): Generator + { + $iterator = new DirectoryIterator($location); + + foreach ($iterator as $item) { + if ($item->isDot()) { + continue; + } + + yield $item; + } + } + + private function setPermissions(string $location, int $visibility): void + { + error_clear_last(); + if ( ! @chmod($location, $visibility)) { + $extraMessage = error_get_last()['message'] ?? ''; + throw UnableToSetVisibility::atLocation($this->prefixer->stripPrefix($location), $extraMessage); + } + } +} diff --git a/msd/vendor/league/flysystem/src/MountManager.php b/msd/vendor/league/flysystem/src/MountManager.php new file mode 100644 index 0000000..0e34cf6 --- /dev/null +++ b/msd/vendor/league/flysystem/src/MountManager.php @@ -0,0 +1,334 @@ + + */ + private $filesystems = []; + + /** + * MountManager constructor. + * + * @param array $filesystems + */ + public function __construct(array $filesystems = []) + { + $this->mountFilesystems($filesystems); + } + + public function fileExists(string $location): bool + { + /** @var FilesystemOperator $filesystem */ + [$filesystem, $path] = $this->determineFilesystemAndPath($location); + + try { + return $filesystem->fileExists($path); + } catch (UnableToCheckFileExistence $exception) { + throw UnableToCheckFileExistence::forLocation($location, $exception); + } + } + + public function read(string $location): string + { + /** @var FilesystemOperator $filesystem */ + [$filesystem, $path] = $this->determineFilesystemAndPath($location); + + try { + return $filesystem->read($path); + } catch (UnableToReadFile $exception) { + throw UnableToReadFile::fromLocation($location, $exception->reason(), $exception); + } + } + + public function readStream(string $location) + { + /** @var FilesystemOperator $filesystem */ + [$filesystem, $path] = $this->determineFilesystemAndPath($location); + + try { + return $filesystem->readStream($path); + } catch (UnableToReadFile $exception) { + throw UnableToReadFile::fromLocation($location, $exception->reason(), $exception); + } + } + + public function listContents(string $location, bool $deep = self::LIST_SHALLOW): DirectoryListing + { + /** @var FilesystemOperator $filesystem */ + [$filesystem, $path, $mountIdentifier] = $this->determineFilesystemAndPath($location); + + return + $filesystem + ->listContents($path, $deep) + ->map( + function (StorageAttributes $attributes) use ($mountIdentifier) { + return $attributes->withPath(sprintf('%s://%s', $mountIdentifier, $attributes->path())); + } + ); + } + + public function lastModified(string $location): int + { + /** @var FilesystemOperator $filesystem */ + [$filesystem, $path] = $this->determineFilesystemAndPath($location); + + try { + return $filesystem->lastModified($path); + } catch (UnableToRetrieveMetadata $exception) { + throw UnableToRetrieveMetadata::lastModified($location, $exception->reason(), $exception); + } + } + + public function fileSize(string $location): int + { + /** @var FilesystemOperator $filesystem */ + [$filesystem, $path] = $this->determineFilesystemAndPath($location); + + try { + return $filesystem->fileSize($path); + } catch (UnableToRetrieveMetadata $exception) { + throw UnableToRetrieveMetadata::fileSize($location, $exception->reason(), $exception); + } + } + + public function mimeType(string $location): string + { + /** @var FilesystemOperator $filesystem */ + [$filesystem, $path] = $this->determineFilesystemAndPath($location); + + try { + return $filesystem->mimeType($path); + } catch (UnableToRetrieveMetadata $exception) { + throw UnableToRetrieveMetadata::mimeType($location, $exception->reason(), $exception); + } + } + + public function visibility(string $location): string + { + /** @var FilesystemOperator $filesystem */ + [$filesystem, $path] = $this->determineFilesystemAndPath($location); + + try { + return $filesystem->visibility($path); + } catch (UnableToRetrieveMetadata $exception) { + throw UnableToRetrieveMetadata::visibility($location, $exception->reason(), $exception); + } + } + + public function write(string $location, string $contents, array $config = []): void + { + /** @var FilesystemOperator $filesystem */ + [$filesystem, $path] = $this->determineFilesystemAndPath($location); + + try { + $filesystem->write($path, $contents, $config); + } catch (UnableToWriteFile $exception) { + throw UnableToWriteFile::atLocation($location, $exception->reason(), $exception); + } + } + + public function writeStream(string $location, $contents, array $config = []): void + { + /** @var FilesystemOperator $filesystem */ + [$filesystem, $path] = $this->determineFilesystemAndPath($location); + $filesystem->writeStream($path, $contents, $config); + } + + public function setVisibility(string $path, string $visibility): void + { + /** @var FilesystemOperator $filesystem */ + [$filesystem, $path] = $this->determineFilesystemAndPath($path); + $filesystem->setVisibility($path, $visibility); + } + + public function delete(string $location): void + { + /** @var FilesystemOperator $filesystem */ + [$filesystem, $path] = $this->determineFilesystemAndPath($location); + + try { + $filesystem->delete($path); + } catch (UnableToDeleteFile $exception) { + throw UnableToDeleteFile::atLocation($location, '', $exception); + } + } + + public function deleteDirectory(string $location): void + { + /** @var FilesystemOperator $filesystem */ + [$filesystem, $path] = $this->determineFilesystemAndPath($location); + + try { + $filesystem->deleteDirectory($path); + } catch (UnableToDeleteDirectory $exception) { + throw UnableToDeleteDirectory::atLocation($location, '', $exception); + } + } + + public function createDirectory(string $location, array $config = []): void + { + /** @var FilesystemOperator $filesystem */ + [$filesystem, $path] = $this->determineFilesystemAndPath($location); + + try { + $filesystem->createDirectory($path, $config); + } catch (UnableToCreateDirectory $exception) { + throw UnableToCreateDirectory::dueToFailure($location, $exception); + } + } + + public function move(string $source, string $destination, array $config = []): void + { + /** @var FilesystemOperator $sourceFilesystem */ + /* @var FilesystemOperator $destinationFilesystem */ + [$sourceFilesystem, $sourcePath] = $this->determineFilesystemAndPath($source); + [$destinationFilesystem, $destinationPath] = $this->determineFilesystemAndPath($destination); + + $sourceFilesystem === $destinationFilesystem ? $this->moveInTheSameFilesystem( + $sourceFilesystem, + $sourcePath, + $destinationPath, + $source, + $destination + ) : $this->moveAcrossFilesystems($source, $destination); + } + + public function copy(string $source, string $destination, array $config = []): void + { + /** @var FilesystemOperator $sourceFilesystem */ + /* @var FilesystemOperator $destinationFilesystem */ + [$sourceFilesystem, $sourcePath] = $this->determineFilesystemAndPath($source); + [$destinationFilesystem, $destinationPath] = $this->determineFilesystemAndPath($destination); + + $sourceFilesystem === $destinationFilesystem ? $this->copyInSameFilesystem( + $sourceFilesystem, + $sourcePath, + $destinationPath, + $source, + $destination + ) : $this->copyAcrossFilesystem( + $config['visibility'] ?? null, + $sourceFilesystem, + $sourcePath, + $destinationFilesystem, + $destinationPath, + $source, + $destination + ); + } + + private function mountFilesystems(array $filesystems): void + { + foreach ($filesystems as $key => $filesystem) { + $this->guardAgainstInvalidMount($key, $filesystem); + /* @var string $key */ + /* @var FilesystemOperator $filesystem */ + $this->mountFilesystem($key, $filesystem); + } + } + + /** + * @param mixed $key + * @param mixed $filesystem + */ + private function guardAgainstInvalidMount($key, $filesystem): void + { + if ( ! is_string($key)) { + throw UnableToMountFilesystem::becauseTheKeyIsNotValid($key); + } + + if ( ! $filesystem instanceof FilesystemOperator) { + throw UnableToMountFilesystem::becauseTheFilesystemWasNotValid($filesystem); + } + } + + private function mountFilesystem(string $key, FilesystemOperator $filesystem): void + { + $this->filesystems[$key] = $filesystem; + } + + /** + * @param string $path + * + * @return array{0:FilesystemOperator, 1:string} + */ + private function determineFilesystemAndPath(string $path): array + { + if (strpos($path, '://') < 1) { + throw UnableToResolveFilesystemMount::becauseTheSeparatorIsMissing($path); + } + + /** @var string $mountIdentifier */ + /** @var string $mountPath */ + [$mountIdentifier, $mountPath] = explode('://', $path, 2); + + if ( ! array_key_exists($mountIdentifier, $this->filesystems)) { + throw UnableToResolveFilesystemMount::becauseTheMountWasNotRegistered($mountIdentifier); + } + + return [$this->filesystems[$mountIdentifier], $mountPath, $mountIdentifier]; + } + + private function copyInSameFilesystem( + FilesystemOperator $sourceFilesystem, + string $sourcePath, + string $destinationPath, + string $source, + string $destination + ): void { + try { + $sourceFilesystem->copy($sourcePath, $destinationPath); + } catch (UnableToCopyFile $exception) { + throw UnableToCopyFile::fromLocationTo($source, $destination, $exception); + } + } + + private function copyAcrossFilesystem( + ?string $visibility, + FilesystemOperator $sourceFilesystem, + string $sourcePath, + FilesystemOperator $destinationFilesystem, + string $destinationPath, + string $source, + string $destination + ): void { + try { + $visibility = $visibility ?? $sourceFilesystem->visibility($sourcePath); + $stream = $sourceFilesystem->readStream($sourcePath); + $destinationFilesystem->writeStream($destinationPath, $stream, compact('visibility')); + } catch (UnableToRetrieveMetadata | UnableToReadFile | UnableToWriteFile $exception) { + throw UnableToCopyFile::fromLocationTo($source, $destination, $exception); + } + } + + private function moveInTheSameFilesystem( + FilesystemOperator $sourceFilesystem, + string $sourcePath, + string $destinationPath, + string $source, + string $destination + ): void { + try { + $sourceFilesystem->move($sourcePath, $destinationPath); + } catch (UnableToMoveFile $exception) { + throw UnableToMoveFile::fromLocationTo($source, $destination, $exception); + } + } + + private function moveAcrossFilesystems(string $source, string $destination): void + { + try { + $this->copy($source, $destination); + $this->delete($source); + } catch (UnableToCopyFile | UnableToDeleteFile $exception) { + throw UnableToMoveFile::fromLocationTo($source, $destination, $exception); + } + } +} diff --git a/msd/vendor/league/flysystem/src/PathNormalizer.php b/msd/vendor/league/flysystem/src/PathNormalizer.php new file mode 100644 index 0000000..19fdfee --- /dev/null +++ b/msd/vendor/league/flysystem/src/PathNormalizer.php @@ -0,0 +1,10 @@ +prefix = rtrim($prefix, '\\/'); + + if ($this->prefix !== '' || $prefix === $separator) { + $this->prefix .= $separator; + } + + $this->separator = $separator; + } + + public function prefixPath(string $path): string + { + return $this->prefix . ltrim($path, '\\/'); + } + + public function stripPrefix(string $path): string + { + /* @var string */ + return substr($path, strlen($this->prefix)); + } + + public function stripDirectoryPrefix(string $path): string + { + return rtrim($this->stripPrefix($path), '\\/'); + } + + public function prefixDirectoryPath(string $path): string + { + $prefixedPath = $this->prefixPath(rtrim($path, '\\/')); + + if ((substr($prefixedPath, -1) === $this->separator) || $prefixedPath === '') { + return $prefixedPath; + } + + return $prefixedPath . $this->separator; + } +} diff --git a/msd/vendor/league/flysystem/src/PathTraversalDetected.php b/msd/vendor/league/flysystem/src/PathTraversalDetected.php new file mode 100644 index 0000000..b95eb36 --- /dev/null +++ b/msd/vendor/league/flysystem/src/PathTraversalDetected.php @@ -0,0 +1,28 @@ +path; + } + + public static function forPath(string $path): PathTraversalDetected + { + $e = new PathTraversalDetected("Path traversal detected: {$path}"); + $e->path = $path; + + return $e; + } +} diff --git a/msd/vendor/league/flysystem/src/PortableVisibilityGuard.php b/msd/vendor/league/flysystem/src/PortableVisibilityGuard.php new file mode 100644 index 0000000..d622090 --- /dev/null +++ b/msd/vendor/league/flysystem/src/PortableVisibilityGuard.php @@ -0,0 +1,19 @@ +formatPropertyName((string) $offset); + + return isset($this->{$property}); + } + + /** + * @param mixed $offset + * + * @return mixed + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + $property = $this->formatPropertyName((string) $offset); + + return $this->{$property}; + } + + /** + * @param mixed $offset + * @param mixed $value + */ + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value): void + { + throw new RuntimeException('Properties can not be manipulated'); + } + + /** + * @param mixed $offset + */ + #[\ReturnTypeWillChange] + public function offsetUnset($offset): void + { + throw new RuntimeException('Properties can not be manipulated'); + } +} diff --git a/msd/vendor/league/flysystem/src/StorageAttributes.php b/msd/vendor/league/flysystem/src/StorageAttributes.php new file mode 100644 index 0000000..f6678b8 --- /dev/null +++ b/msd/vendor/league/flysystem/src/StorageAttributes.php @@ -0,0 +1,40 @@ +location; + } + + public static function atLocation(string $pathName): SymbolicLinkEncountered + { + $e = new static("Unsupported symbolic link encountered at location $pathName"); + $e->location = $pathName; + + return $e; + } +} diff --git a/msd/vendor/league/flysystem/src/UnableToCheckFileExistence.php b/msd/vendor/league/flysystem/src/UnableToCheckFileExistence.php new file mode 100644 index 0000000..a25bedc --- /dev/null +++ b/msd/vendor/league/flysystem/src/UnableToCheckFileExistence.php @@ -0,0 +1,21 @@ +source; + } + + public function destination(): string + { + return $this->destination; + } + + public static function fromLocationTo( + string $sourcePath, + string $destinationPath, + Throwable $previous = null + ): UnableToCopyFile { + $e = new static("Unable to copy file from $sourcePath to $destinationPath", 0 , $previous); + $e->source = $sourcePath; + $e->destination = $destinationPath; + + return $e; + } + + public function operation(): string + { + return FilesystemOperationFailed::OPERATION_COPY; + } +} diff --git a/msd/vendor/league/flysystem/src/UnableToCreateDirectory.php b/msd/vendor/league/flysystem/src/UnableToCreateDirectory.php new file mode 100644 index 0000000..7039dfc --- /dev/null +++ b/msd/vendor/league/flysystem/src/UnableToCreateDirectory.php @@ -0,0 +1,44 @@ +location = $dirname; + + return $e; + } + + public static function dueToFailure(string $dirname, Throwable $previous): UnableToCreateDirectory + { + $message = "Unable to create a directory at {$dirname}"; + $e = new static($message, 0, $previous); + $e->location = $dirname; + + return $e; + } + + public function operation(): string + { + return FilesystemOperationFailed::OPERATION_CREATE_DIRECTORY; + } + + public function location(): string + { + return $this->location; + } +} diff --git a/msd/vendor/league/flysystem/src/UnableToDeleteDirectory.php b/msd/vendor/league/flysystem/src/UnableToDeleteDirectory.php new file mode 100644 index 0000000..33c7d10 --- /dev/null +++ b/msd/vendor/league/flysystem/src/UnableToDeleteDirectory.php @@ -0,0 +1,48 @@ +location = $location; + $e->reason = $reason; + + return $e; + } + + public function operation(): string + { + return FilesystemOperationFailed::OPERATION_DELETE_DIRECTORY; + } + + public function reason(): string + { + return $this->reason; + } + + public function location(): string + { + return $this->location; + } +} diff --git a/msd/vendor/league/flysystem/src/UnableToDeleteFile.php b/msd/vendor/league/flysystem/src/UnableToDeleteFile.php new file mode 100644 index 0000000..9c33f36 --- /dev/null +++ b/msd/vendor/league/flysystem/src/UnableToDeleteFile.php @@ -0,0 +1,45 @@ +location = $location; + $e->reason = $reason; + + return $e; + } + + public function operation(): string + { + return FilesystemOperationFailed::OPERATION_DELETE; + } + + public function reason(): string + { + return $this->reason; + } + + public function location(): string + { + return $this->location; + } +} diff --git a/msd/vendor/league/flysystem/src/UnableToMountFilesystem.php b/msd/vendor/league/flysystem/src/UnableToMountFilesystem.php new file mode 100644 index 0000000..9d33315 --- /dev/null +++ b/msd/vendor/league/flysystem/src/UnableToMountFilesystem.php @@ -0,0 +1,32 @@ +source; + } + + public function destination(): string + { + return $this->destination; + } + + public static function fromLocationTo( + string $sourcePath, + string $destinationPath, + Throwable $previous = null + ): UnableToMoveFile { + $e = new static("Unable to move file from $sourcePath to $destinationPath", 0, $previous); + $e->source = $sourcePath; + $e->destination = $destinationPath; + + return $e; + } + + public function operation(): string + { + return FilesystemOperationFailed::OPERATION_MOVE; + } +} diff --git a/msd/vendor/league/flysystem/src/UnableToReadFile.php b/msd/vendor/league/flysystem/src/UnableToReadFile.php new file mode 100644 index 0000000..b1890f3 --- /dev/null +++ b/msd/vendor/league/flysystem/src/UnableToReadFile.php @@ -0,0 +1,45 @@ +location = $location; + $e->reason = $reason; + + return $e; + } + + public function operation(): string + { + return FilesystemOperationFailed::OPERATION_READ; + } + + public function reason(): string + { + return $this->reason; + } + + public function location(): string + { + return $this->location; + } +} diff --git a/msd/vendor/league/flysystem/src/UnableToResolveFilesystemMount.php b/msd/vendor/league/flysystem/src/UnableToResolveFilesystemMount.php new file mode 100644 index 0000000..db23c50 --- /dev/null +++ b/msd/vendor/league/flysystem/src/UnableToResolveFilesystemMount.php @@ -0,0 +1,20 @@ +reason = $reason; + $e->location = $location; + $e->metadataType = $type; + + return $e; + } + + public function reason(): string + { + return $this->reason; + } + + public function location(): string + { + return $this->location; + } + + public function metadataType(): string + { + return $this->metadataType; + } + + public function operation(): string + { + return FilesystemOperationFailed::OPERATION_RETRIEVE_METADATA; + } +} diff --git a/msd/vendor/league/flysystem/src/UnableToSetVisibility.php b/msd/vendor/league/flysystem/src/UnableToSetVisibility.php new file mode 100644 index 0000000..7e728fe --- /dev/null +++ b/msd/vendor/league/flysystem/src/UnableToSetVisibility.php @@ -0,0 +1,49 @@ +reason; + } + + public static function atLocation(string $filename, string $extraMessage = '', Throwable $previous = null): self + { + $message = "Unable to set visibility for file {$filename}. $extraMessage"; + $e = new static(rtrim($message), 0, $previous); + $e->reason = $extraMessage; + $e->location = $filename; + + return $e; + } + + public function operation(): string + { + return FilesystemOperationFailed::OPERATION_SET_VISIBILITY; + } + + public function location(): string + { + return $this->location; + } +} diff --git a/msd/vendor/league/flysystem/src/UnableToWriteFile.php b/msd/vendor/league/flysystem/src/UnableToWriteFile.php new file mode 100644 index 0000000..d442a1f --- /dev/null +++ b/msd/vendor/league/flysystem/src/UnableToWriteFile.php @@ -0,0 +1,45 @@ +location = $location; + $e->reason = $reason; + + return $e; + } + + public function operation(): string + { + return FilesystemOperationFailed::OPERATION_WRITE; + } + + public function reason(): string + { + return $this->reason; + } + + public function location(): string + { + return $this->location; + } +} diff --git a/msd/vendor/league/flysystem/src/UnixVisibility/PortableVisibilityConverter.php b/msd/vendor/league/flysystem/src/UnixVisibility/PortableVisibilityConverter.php new file mode 100644 index 0000000..b034fdd --- /dev/null +++ b/msd/vendor/league/flysystem/src/UnixVisibility/PortableVisibilityConverter.php @@ -0,0 +1,109 @@ +filePublic = $filePublic; + $this->filePrivate = $filePrivate; + $this->directoryPublic = $directoryPublic; + $this->directoryPrivate = $directoryPrivate; + $this->defaultForDirectories = $defaultForDirectories; + } + + public function forFile(string $visibility): int + { + PortableVisibilityGuard::guardAgainstInvalidInput($visibility); + + return $visibility === Visibility::PUBLIC + ? $this->filePublic + : $this->filePrivate; + } + + public function forDirectory(string $visibility): int + { + PortableVisibilityGuard::guardAgainstInvalidInput($visibility); + + return $visibility === Visibility::PUBLIC + ? $this->directoryPublic + : $this->directoryPrivate; + } + + public function inverseForFile(int $visibility): string + { + if ($visibility === $this->filePublic) { + return Visibility::PUBLIC; + } elseif ($visibility === $this->filePrivate) { + return Visibility::PRIVATE; + } + + return Visibility::PUBLIC; // default + } + + public function inverseForDirectory(int $visibility): string + { + if ($visibility === $this->directoryPublic) { + return Visibility::PUBLIC; + } elseif ($visibility === $this->directoryPrivate) { + return Visibility::PRIVATE; + } + + return Visibility::PUBLIC; // default + } + + public function defaultForDirectories(): int + { + return $this->defaultForDirectories === Visibility::PUBLIC ? $this->directoryPublic : $this->directoryPrivate; + } + + /** + * @param array $permissionMap + */ + public static function fromArray(array $permissionMap, string $defaultForDirectories = Visibility::PRIVATE): PortableVisibilityConverter + { + return new PortableVisibilityConverter( + $permissionMap['file']['public'] ?? 0644, + $permissionMap['file']['private'] ?? 0600, + $permissionMap['dir']['public'] ?? 0755, + $permissionMap['dir']['private'] ?? 0700, + $defaultForDirectories + ); + } +} diff --git a/msd/vendor/league/flysystem/src/UnixVisibility/VisibilityConverter.php b/msd/vendor/league/flysystem/src/UnixVisibility/VisibilityConverter.php new file mode 100644 index 0000000..532d115 --- /dev/null +++ b/msd/vendor/league/flysystem/src/UnixVisibility/VisibilityConverter.php @@ -0,0 +1,14 @@ +location; + } + + public static function atLocation(string $location): UnreadableFileEncountered + { + $e = new static("Unreadable file encountered at location {$location}."); + $e->location = $location; + + return $e; + } +} diff --git a/msd/vendor/league/flysystem/src/Visibility.php b/msd/vendor/league/flysystem/src/Visibility.php new file mode 100644 index 0000000..c927866 --- /dev/null +++ b/msd/vendor/league/flysystem/src/Visibility.php @@ -0,0 +1,11 @@ +rejectFunkyWhiteSpace($path); + + return $this->normalizeRelativePath($path); + } + + private function rejectFunkyWhiteSpace(string $path): void + { + if (preg_match('#\p{C}+#u', $path)) { + throw CorruptedPathDetected::forPath($path); + } + } + + private function normalizeRelativePath(string $path): string + { + $parts = []; + + foreach (explode('/', $path) as $part) { + switch ($part) { + case '': + case '.': + break; + + case '..': + if (empty($parts)) { + throw PathTraversalDetected::forPath($path); + } + array_pop($parts); + break; + + default: + $parts[] = $part; + break; + } + } + + return implode('/', $parts); + } +} diff --git a/msd/vendor/league/mime-type-detection/CHANGELOG.md b/msd/vendor/league/mime-type-detection/CHANGELOG.md new file mode 100644 index 0000000..2264f7a --- /dev/null +++ b/msd/vendor/league/mime-type-detection/CHANGELOG.md @@ -0,0 +1,31 @@ +# Changelog + +## 1.10.0 - 2022-04-11 + +### Fixed + +- Added Flysystem v1 inconclusive mime-types and made it configurable as a constructor parameter. + +## 1.9.0 - 2021-11-21 + +### Updated + +- Updated lookup + +## 1.8.0 - 2021-09-25 + +### Added + +- Added the decorator `OverridingExtensionToMimeTypeMap` which allows you to override values. + +## 1.7.0 - 2021-01-18 + +### Added + +- Added a `bufferSampleSize` parameter to the `FinfoMimeTypeDetector` class that allows you to send a reduced content sample which costs less memory. + +## 1.6.0 - 2021-01-18 + +### Changes + +- Updated generated mime-type map diff --git a/msd/vendor/league/mime-type-detection/LICENSE b/msd/vendor/league/mime-type-detection/LICENSE new file mode 100644 index 0000000..e9292db --- /dev/null +++ b/msd/vendor/league/mime-type-detection/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013-2022 Frank de Jonge + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/msd/vendor/league/mime-type-detection/composer.json b/msd/vendor/league/mime-type-detection/composer.json new file mode 100644 index 0000000..80ca1af --- /dev/null +++ b/msd/vendor/league/mime-type-detection/composer.json @@ -0,0 +1,34 @@ +{ + "name": "league/mime-type-detection", + "description": "Mime-type detection for Flysystem", + "license": "MIT", + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "scripts": { + "test": "vendor/bin/phpunit", + "phpstan": "vendor/bin/phpstan analyse -l 6 src" + }, + "require": { + "php": "^7.2 || ^8.0", + "ext-fileinfo": "*" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.8 || ^9.3", + "phpstan/phpstan": "^0.12.68", + "friendsofphp/php-cs-fixer": "^3.2" + }, + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "config": { + "platform": { + "php": "7.2.0" + } + } +} diff --git a/msd/vendor/league/mime-type-detection/src/EmptyExtensionToMimeTypeMap.php b/msd/vendor/league/mime-type-detection/src/EmptyExtensionToMimeTypeMap.php new file mode 100644 index 0000000..ce2e2b1 --- /dev/null +++ b/msd/vendor/league/mime-type-detection/src/EmptyExtensionToMimeTypeMap.php @@ -0,0 +1,13 @@ +extensions = $extensions ?: new GeneratedExtensionToMimeTypeMap(); + } + + public function detectMimeType(string $path, $contents): ?string + { + return $this->detectMimeTypeFromPath($path); + } + + public function detectMimeTypeFromPath(string $path): ?string + { + $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION)); + + return $this->extensions->lookupMimeType($extension); + } + + public function detectMimeTypeFromFile(string $path): ?string + { + return $this->detectMimeTypeFromPath($path); + } + + public function detectMimeTypeFromBuffer(string $contents): ?string + { + return null; + } +} diff --git a/msd/vendor/league/mime-type-detection/src/ExtensionToMimeTypeMap.php b/msd/vendor/league/mime-type-detection/src/ExtensionToMimeTypeMap.php new file mode 100644 index 0000000..a924b77 --- /dev/null +++ b/msd/vendor/league/mime-type-detection/src/ExtensionToMimeTypeMap.php @@ -0,0 +1,10 @@ + + */ + private $inconclusiveMimetypes; + + public function __construct( + string $magicFile = '', + ExtensionToMimeTypeMap $extensionMap = null, + ?int $bufferSampleSize = null, + array $inconclusiveMimetypes = self::INCONCLUSIVE_MIME_TYPES + ) { + $this->finfo = new finfo(FILEINFO_MIME_TYPE, $magicFile); + $this->extensionMap = $extensionMap ?: new GeneratedExtensionToMimeTypeMap(); + $this->bufferSampleSize = $bufferSampleSize; + $this->inconclusiveMimetypes = $inconclusiveMimetypes; + } + + public function detectMimeType(string $path, $contents): ?string + { + $mimeType = is_string($contents) + ? (@$this->finfo->buffer($this->takeSample($contents)) ?: null) + : null; + + if ($mimeType !== null && ! in_array($mimeType, $this->inconclusiveMimetypes)) { + return $mimeType; + } + + return $this->detectMimeTypeFromPath($path); + } + + public function detectMimeTypeFromPath(string $path): ?string + { + $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION)); + + return $this->extensionMap->lookupMimeType($extension); + } + + public function detectMimeTypeFromFile(string $path): ?string + { + return @$this->finfo->file($path) ?: null; + } + + public function detectMimeTypeFromBuffer(string $contents): ?string + { + return @$this->finfo->buffer($this->takeSample($contents)) ?: null; + } + + private function takeSample(string $contents): string + { + if ($this->bufferSampleSize === null) { + return $contents; + } + + return (string) substr($contents, 0, $this->bufferSampleSize); + } +} diff --git a/msd/vendor/league/mime-type-detection/src/GeneratedExtensionToMimeTypeMap.php b/msd/vendor/league/mime-type-detection/src/GeneratedExtensionToMimeTypeMap.php new file mode 100644 index 0000000..7691170 --- /dev/null +++ b/msd/vendor/league/mime-type-detection/src/GeneratedExtensionToMimeTypeMap.php @@ -0,0 +1,1227 @@ + 'application/vnd.1000minds.decision-model+xml', + '3dml' => 'text/vnd.in3d.3dml', + '3ds' => 'image/x-3ds', + '3g2' => 'video/3gpp2', + '3gp' => 'video/3gp', + '3gpp' => 'video/3gpp', + '3mf' => 'model/3mf', + '7z' => 'application/x-7z-compressed', + '7zip' => 'application/x-7z-compressed', + '123' => 'application/vnd.lotus-1-2-3', + 'aab' => 'application/x-authorware-bin', + 'aac' => 'audio/x-acc', + 'aam' => 'application/x-authorware-map', + 'aas' => 'application/x-authorware-seg', + 'abw' => 'application/x-abiword', + 'ac' => 'application/vnd.nokia.n-gage.ac+xml', + 'ac3' => 'audio/ac3', + 'acc' => 'application/vnd.americandynamics.acc', + 'ace' => 'application/x-ace-compressed', + 'acu' => 'application/vnd.acucobol', + 'acutc' => 'application/vnd.acucorp', + 'adp' => 'audio/adpcm', + 'aep' => 'application/vnd.audiograph', + 'afm' => 'application/x-font-type1', + 'afp' => 'application/vnd.ibm.modcap', + 'age' => 'application/vnd.age', + 'ahead' => 'application/vnd.ahead.space', + 'ai' => 'application/pdf', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'air' => 'application/vnd.adobe.air-application-installer-package+zip', + 'ait' => 'application/vnd.dvb.ait', + 'ami' => 'application/vnd.amiga.ami', + 'amr' => 'audio/amr', + 'apk' => 'application/vnd.android.package-archive', + 'apng' => 'image/apng', + 'appcache' => 'text/cache-manifest', + 'application' => 'application/x-ms-application', + 'apr' => 'application/vnd.lotus-approach', + 'arc' => 'application/x-freearc', + 'arj' => 'application/x-arj', + 'asc' => 'application/pgp-signature', + 'asf' => 'video/x-ms-asf', + 'asm' => 'text/x-asm', + 'aso' => 'application/vnd.accpac.simply.aso', + 'asx' => 'video/x-ms-asf', + 'atc' => 'application/vnd.acucorp', + 'atom' => 'application/atom+xml', + 'atomcat' => 'application/atomcat+xml', + 'atomdeleted' => 'application/atomdeleted+xml', + 'atomsvc' => 'application/atomsvc+xml', + 'atx' => 'application/vnd.antix.game-component', + 'au' => 'audio/x-au', + 'avci' => 'image/avci', + 'avcs' => 'image/avcs', + 'avi' => 'video/x-msvideo', + 'avif' => 'image/avif', + 'aw' => 'application/applixware', + 'azf' => 'application/vnd.airzip.filesecure.azf', + 'azs' => 'application/vnd.airzip.filesecure.azs', + 'azv' => 'image/vnd.airzip.accelerator.azv', + 'azw' => 'application/vnd.amazon.ebook', + 'b16' => 'image/vnd.pco.b16', + 'bat' => 'application/x-msdownload', + 'bcpio' => 'application/x-bcpio', + 'bdf' => 'application/x-font-bdf', + 'bdm' => 'application/vnd.syncml.dm+wbxml', + 'bdoc' => 'application/x-bdoc', + 'bed' => 'application/vnd.realvnc.bed', + 'bh2' => 'application/vnd.fujitsu.oasysprs', + 'bin' => 'application/octet-stream', + 'blb' => 'application/x-blorb', + 'blorb' => 'application/x-blorb', + 'bmi' => 'application/vnd.bmi', + 'bmml' => 'application/vnd.balsamiq.bmml+xml', + 'bmp' => 'image/bmp', + 'book' => 'application/vnd.framemaker', + 'box' => 'application/vnd.previewsystems.box', + 'boz' => 'application/x-bzip2', + 'bpk' => 'application/octet-stream', + 'bpmn' => 'application/octet-stream', + 'bsp' => 'model/vnd.valve.source.compiled-map', + 'btif' => 'image/prs.btif', + 'buffer' => 'application/octet-stream', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', + 'c' => 'text/x-c', + 'c4d' => 'application/vnd.clonk.c4group', + 'c4f' => 'application/vnd.clonk.c4group', + 'c4g' => 'application/vnd.clonk.c4group', + 'c4p' => 'application/vnd.clonk.c4group', + 'c4u' => 'application/vnd.clonk.c4group', + 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', + 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', + 'cab' => 'application/vnd.ms-cab-compressed', + 'caf' => 'audio/x-caf', + 'cap' => 'application/vnd.tcpdump.pcap', + 'car' => 'application/vnd.curl.car', + 'cat' => 'application/vnd.ms-pki.seccat', + 'cb7' => 'application/x-cbr', + 'cba' => 'application/x-cbr', + 'cbr' => 'application/x-cbr', + 'cbt' => 'application/x-cbr', + 'cbz' => 'application/x-cbr', + 'cc' => 'text/x-c', + 'cco' => 'application/x-cocoa', + 'cct' => 'application/x-director', + 'ccxml' => 'application/ccxml+xml', + 'cdbcmsg' => 'application/vnd.contact.cmsg', + 'cdf' => 'application/x-netcdf', + 'cdfx' => 'application/cdfx+xml', + 'cdkey' => 'application/vnd.mediastation.cdkey', + 'cdmia' => 'application/cdmi-capability', + 'cdmic' => 'application/cdmi-container', + 'cdmid' => 'application/cdmi-domain', + 'cdmio' => 'application/cdmi-object', + 'cdmiq' => 'application/cdmi-queue', + 'cdr' => 'application/cdr', + 'cdx' => 'chemical/x-cdx', + 'cdxml' => 'application/vnd.chemdraw+xml', + 'cdy' => 'application/vnd.cinderella', + 'cer' => 'application/pkix-cert', + 'cfs' => 'application/x-cfs-compressed', + 'cgm' => 'image/cgm', + 'chat' => 'application/x-chat', + 'chm' => 'application/vnd.ms-htmlhelp', + 'chrt' => 'application/vnd.kde.kchart', + 'cif' => 'chemical/x-cif', + 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', + 'cil' => 'application/vnd.ms-artgalry', + 'cjs' => 'application/node', + 'cla' => 'application/vnd.claymore', + 'class' => 'application/octet-stream', + 'clkk' => 'application/vnd.crick.clicker.keyboard', + 'clkp' => 'application/vnd.crick.clicker.palette', + 'clkt' => 'application/vnd.crick.clicker.template', + 'clkw' => 'application/vnd.crick.clicker.wordbank', + 'clkx' => 'application/vnd.crick.clicker', + 'clp' => 'application/x-msclip', + 'cmc' => 'application/vnd.cosmocaller', + 'cmdf' => 'chemical/x-cmdf', + 'cml' => 'chemical/x-cml', + 'cmp' => 'application/vnd.yellowriver-custom-menu', + 'cmx' => 'image/x-cmx', + 'cod' => 'application/vnd.rim.cod', + 'coffee' => 'text/coffeescript', + 'com' => 'application/x-msdownload', + 'conf' => 'text/plain', + 'cpio' => 'application/x-cpio', + 'cpl' => 'application/cpl+xml', + 'cpp' => 'text/x-c', + 'cpt' => 'application/mac-compactpro', + 'crd' => 'application/x-mscardfile', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'crx' => 'application/x-chrome-extension', + 'cryptonote' => 'application/vnd.rig.cryptonote', + 'csh' => 'application/x-csh', + 'csl' => 'application/vnd.citationstyles.style+xml', + 'csml' => 'chemical/x-csml', + 'csp' => 'application/vnd.commonspace', + 'csr' => 'application/octet-stream', + 'css' => 'text/css', + 'cst' => 'application/x-director', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'curl' => 'text/vnd.curl', + 'cww' => 'application/prs.cww', + 'cxt' => 'application/x-director', + 'cxx' => 'text/x-c', + 'dae' => 'model/vnd.collada+xml', + 'daf' => 'application/vnd.mobius.daf', + 'dart' => 'application/vnd.dart', + 'dataless' => 'application/vnd.fdsn.seed', + 'davmount' => 'application/davmount+xml', + 'dbf' => 'application/vnd.dbf', + 'dbk' => 'application/docbook+xml', + 'dcr' => 'application/x-director', + 'dcurl' => 'text/vnd.curl.dcurl', + 'dd2' => 'application/vnd.oma.dd2+xml', + 'ddd' => 'application/vnd.fujixerox.ddd', + 'ddf' => 'application/vnd.syncml.dmddf+xml', + 'dds' => 'image/vnd.ms-dds', + 'deb' => 'application/x-debian-package', + 'def' => 'text/plain', + 'deploy' => 'application/octet-stream', + 'der' => 'application/x-x509-ca-cert', + 'dfac' => 'application/vnd.dreamfactory', + 'dgc' => 'application/x-dgc-compressed', + 'dic' => 'text/x-c', + 'dir' => 'application/x-director', + 'dis' => 'application/vnd.mobius.dis', + 'disposition-notification' => 'message/disposition-notification', + 'dist' => 'application/octet-stream', + 'distz' => 'application/octet-stream', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/octet-stream', + 'dmg' => 'application/x-apple-diskimage', + 'dmn' => 'application/octet-stream', + 'dmp' => 'application/vnd.tcpdump.pcap', + 'dms' => 'application/octet-stream', + 'dna' => 'application/vnd.dna', + 'doc' => 'application/msword', + 'docm' => 'application/vnd.ms-word.template.macroEnabled.12', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dot' => 'application/msword', + 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'dp' => 'application/vnd.osgi.dp', + 'dpg' => 'application/vnd.dpgraph', + 'dra' => 'audio/vnd.dra', + 'drle' => 'image/dicom-rle', + 'dsc' => 'text/prs.lines.tag', + 'dssc' => 'application/dssc+der', + 'dtb' => 'application/x-dtbook+xml', + 'dtd' => 'application/xml-dtd', + 'dts' => 'audio/vnd.dts', + 'dtshd' => 'audio/vnd.dts.hd', + 'dump' => 'application/octet-stream', + 'dvb' => 'video/vnd.dvb.file', + 'dvi' => 'application/x-dvi', + 'dwd' => 'application/atsc-dwd+xml', + 'dwf' => 'model/vnd.dwf', + 'dwg' => 'image/vnd.dwg', + 'dxf' => 'image/vnd.dxf', + 'dxp' => 'application/vnd.spotfire.dxp', + 'dxr' => 'application/x-director', + 'ear' => 'application/java-archive', + 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', + 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', + 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', + 'ecma' => 'application/ecmascript', + 'edm' => 'application/vnd.novadigm.edm', + 'edx' => 'application/vnd.novadigm.edx', + 'efif' => 'application/vnd.picsel', + 'ei6' => 'application/vnd.pg.osasli', + 'elc' => 'application/octet-stream', + 'emf' => 'image/emf', + 'eml' => 'message/rfc822', + 'emma' => 'application/emma+xml', + 'emotionml' => 'application/emotionml+xml', + 'emz' => 'application/x-msmetafile', + 'eol' => 'audio/vnd.digital-winds', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'es' => 'application/ecmascript', + 'es3' => 'application/vnd.eszigno3+xml', + 'esa' => 'application/vnd.osgi.subsystem', + 'esf' => 'application/vnd.epson.esf', + 'et3' => 'application/vnd.eszigno3+xml', + 'etx' => 'text/x-setext', + 'eva' => 'application/x-eva', + 'evy' => 'application/x-envoy', + 'exe' => 'application/octet-stream', + 'exi' => 'application/exi', + 'exp' => 'application/express', + 'exr' => 'image/aces', + 'ext' => 'application/vnd.novadigm.ext', + 'ez' => 'application/andrew-inset', + 'ez2' => 'application/vnd.ezpix-album', + 'ez3' => 'application/vnd.ezpix-package', + 'f' => 'text/x-fortran', + 'f4v' => 'video/mp4', + 'f77' => 'text/x-fortran', + 'f90' => 'text/x-fortran', + 'fbs' => 'image/vnd.fastbidsheet', + 'fcdt' => 'application/vnd.adobe.formscentral.fcdt', + 'fcs' => 'application/vnd.isac.fcs', + 'fdf' => 'application/vnd.fdf', + 'fdt' => 'application/fdt+xml', + 'fe_launch' => 'application/vnd.denovo.fcselayout-link', + 'fg5' => 'application/vnd.fujitsu.oasysgp', + 'fgd' => 'application/x-director', + 'fh' => 'image/x-freehand', + 'fh4' => 'image/x-freehand', + 'fh5' => 'image/x-freehand', + 'fh7' => 'image/x-freehand', + 'fhc' => 'image/x-freehand', + 'fig' => 'application/x-xfig', + 'fits' => 'image/fits', + 'flac' => 'audio/x-flac', + 'fli' => 'video/x-fli', + 'flo' => 'application/vnd.micrografx.flo', + 'flv' => 'video/x-flv', + 'flw' => 'application/vnd.kde.kivio', + 'flx' => 'text/vnd.fmi.flexstor', + 'fly' => 'text/vnd.fly', + 'fm' => 'application/vnd.framemaker', + 'fnc' => 'application/vnd.frogans.fnc', + 'fo' => 'application/vnd.software602.filler.form+xml', + 'for' => 'text/x-fortran', + 'fpx' => 'image/vnd.fpx', + 'frame' => 'application/vnd.framemaker', + 'fsc' => 'application/vnd.fsc.weblaunch', + 'fst' => 'image/vnd.fst', + 'ftc' => 'application/vnd.fluxtime.clip', + 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', + 'fvt' => 'video/vnd.fvt', + 'fxp' => 'application/vnd.adobe.fxp', + 'fxpl' => 'application/vnd.adobe.fxp', + 'fzs' => 'application/vnd.fuzzysheet', + 'g2w' => 'application/vnd.geoplan', + 'g3' => 'image/g3fax', + 'g3w' => 'application/vnd.geospace', + 'gac' => 'application/vnd.groove-account', + 'gam' => 'application/x-tads', + 'gbr' => 'application/rpki-ghostbusters', + 'gca' => 'application/x-gca-compressed', + 'gdl' => 'model/vnd.gdl', + 'gdoc' => 'application/vnd.google-apps.document', + 'ged' => 'text/vnd.familysearch.gedcom', + 'geo' => 'application/vnd.dynageo', + 'geojson' => 'application/geo+json', + 'gex' => 'application/vnd.geometry-explorer', + 'ggb' => 'application/vnd.geogebra.file', + 'ggt' => 'application/vnd.geogebra.tool', + 'ghf' => 'application/vnd.groove-help', + 'gif' => 'image/gif', + 'gim' => 'application/vnd.groove-identity-message', + 'glb' => 'model/gltf-binary', + 'gltf' => 'model/gltf+json', + 'gml' => 'application/gml+xml', + 'gmx' => 'application/vnd.gmx', + 'gnumeric' => 'application/x-gnumeric', + 'gpg' => 'application/gpg-keys', + 'gph' => 'application/vnd.flographit', + 'gpx' => 'application/gpx+xml', + 'gqf' => 'application/vnd.grafeq', + 'gqs' => 'application/vnd.grafeq', + 'gram' => 'application/srgs', + 'gramps' => 'application/x-gramps-xml', + 'gre' => 'application/vnd.geometry-explorer', + 'grv' => 'application/vnd.groove-injector', + 'grxml' => 'application/srgs+xml', + 'gsf' => 'application/x-font-ghostscript', + 'gsheet' => 'application/vnd.google-apps.spreadsheet', + 'gslides' => 'application/vnd.google-apps.presentation', + 'gtar' => 'application/x-gtar', + 'gtm' => 'application/vnd.groove-tool-message', + 'gtw' => 'model/vnd.gtw', + 'gv' => 'text/vnd.graphviz', + 'gxf' => 'application/gxf', + 'gxt' => 'application/vnd.geonext', + 'gz' => 'application/gzip', + 'gzip' => 'application/gzip', + 'h' => 'text/x-c', + 'h261' => 'video/h261', + 'h263' => 'video/h263', + 'h264' => 'video/h264', + 'hal' => 'application/vnd.hal+xml', + 'hbci' => 'application/vnd.hbci', + 'hbs' => 'text/x-handlebars-template', + 'hdd' => 'application/x-virtualbox-hdd', + 'hdf' => 'application/x-hdf', + 'heic' => 'image/heic', + 'heics' => 'image/heic-sequence', + 'heif' => 'image/heif', + 'heifs' => 'image/heif-sequence', + 'hej2' => 'image/hej2k', + 'held' => 'application/atsc-held+xml', + 'hh' => 'text/x-c', + 'hjson' => 'application/hjson', + 'hlp' => 'application/winhlp', + 'hpgl' => 'application/vnd.hp-hpgl', + 'hpid' => 'application/vnd.hp-hpid', + 'hps' => 'application/vnd.hp-hps', + 'hqx' => 'application/mac-binhex40', + 'hsj2' => 'image/hsj2', + 'htc' => 'text/x-component', + 'htke' => 'application/vnd.kenameaapp', + 'htm' => 'text/html', + 'html' => 'text/html', + 'hvd' => 'application/vnd.yamaha.hv-dic', + 'hvp' => 'application/vnd.yamaha.hv-voice', + 'hvs' => 'application/vnd.yamaha.hv-script', + 'i2g' => 'application/vnd.intergeo', + 'icc' => 'application/vnd.iccprofile', + 'ice' => 'x-conference/x-cooltalk', + 'icm' => 'application/vnd.iccprofile', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ief' => 'image/ief', + 'ifb' => 'text/calendar', + 'ifm' => 'application/vnd.shana.informed.formdata', + 'iges' => 'model/iges', + 'igl' => 'application/vnd.igloader', + 'igm' => 'application/vnd.insors.igm', + 'igs' => 'model/iges', + 'igx' => 'application/vnd.micrografx.igx', + 'iif' => 'application/vnd.shana.informed.interchange', + 'img' => 'application/octet-stream', + 'imp' => 'application/vnd.accpac.simply.imp', + 'ims' => 'application/vnd.ms-ims', + 'in' => 'text/plain', + 'ini' => 'text/plain', + 'ink' => 'application/inkml+xml', + 'inkml' => 'application/inkml+xml', + 'install' => 'application/x-install-instructions', + 'iota' => 'application/vnd.astraea-software.iota', + 'ipfix' => 'application/ipfix', + 'ipk' => 'application/vnd.shana.informed.package', + 'irm' => 'application/vnd.ibm.rights-management', + 'irp' => 'application/vnd.irepository.package+xml', + 'iso' => 'application/x-iso9660-image', + 'itp' => 'application/vnd.shana.informed.formtemplate', + 'its' => 'application/its+xml', + 'ivp' => 'application/vnd.immervision-ivp', + 'ivu' => 'application/vnd.immervision-ivu', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'jade' => 'text/jade', + 'jam' => 'application/vnd.jam', + 'jar' => 'application/java-archive', + 'jardiff' => 'application/x-java-archive-diff', + 'java' => 'text/x-java-source', + 'jhc' => 'image/jphc', + 'jisp' => 'application/vnd.jisp', + 'jls' => 'image/jls', + 'jlt' => 'application/vnd.hp-jlyt', + 'jng' => 'image/x-jng', + 'jnlp' => 'application/x-java-jnlp-file', + 'joda' => 'application/vnd.joost.joda-archive', + 'jp2' => 'image/jp2', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpf' => 'image/jpx', + 'jpg' => 'image/jpeg', + 'jpg2' => 'image/jp2', + 'jpgm' => 'video/jpm', + 'jpgv' => 'video/jpeg', + 'jph' => 'image/jph', + 'jpm' => 'video/jpm', + 'jpx' => 'image/jpx', + 'js' => 'application/javascript', + 'json' => 'application/json', + 'json5' => 'application/json5', + 'jsonld' => 'application/ld+json', + 'jsonml' => 'application/jsonml+json', + 'jsx' => 'text/jsx', + 'jxr' => 'image/jxr', + 'jxra' => 'image/jxra', + 'jxrs' => 'image/jxrs', + 'jxs' => 'image/jxs', + 'jxsc' => 'image/jxsc', + 'jxsi' => 'image/jxsi', + 'jxss' => 'image/jxss', + 'kar' => 'audio/midi', + 'karbon' => 'application/vnd.kde.karbon', + 'kdb' => 'application/octet-stream', + 'kdbx' => 'application/x-keepass2', + 'key' => 'application/x-iwork-keynote-sffkey', + 'kfo' => 'application/vnd.kde.kformula', + 'kia' => 'application/vnd.kidspiration', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'kne' => 'application/vnd.kinar', + 'knp' => 'application/vnd.kinar', + 'kon' => 'application/vnd.kde.kontour', + 'kpr' => 'application/vnd.kde.kpresenter', + 'kpt' => 'application/vnd.kde.kpresenter', + 'kpxx' => 'application/vnd.ds-keypoint', + 'ksp' => 'application/vnd.kde.kspread', + 'ktr' => 'application/vnd.kahootz', + 'ktx' => 'image/ktx', + 'ktx2' => 'image/ktx2', + 'ktz' => 'application/vnd.kahootz', + 'kwd' => 'application/vnd.kde.kword', + 'kwt' => 'application/vnd.kde.kword', + 'lasxml' => 'application/vnd.las.las+xml', + 'latex' => 'application/x-latex', + 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', + 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', + 'les' => 'application/vnd.hhe.lesson-player', + 'less' => 'text/less', + 'lgr' => 'application/lgr+xml', + 'lha' => 'application/octet-stream', + 'link66' => 'application/vnd.route66.link66+xml', + 'list' => 'text/plain', + 'list3820' => 'application/vnd.ibm.modcap', + 'listafp' => 'application/vnd.ibm.modcap', + 'litcoffee' => 'text/coffeescript', + 'lnk' => 'application/x-ms-shortcut', + 'log' => 'text/plain', + 'lostxml' => 'application/lost+xml', + 'lrf' => 'application/octet-stream', + 'lrm' => 'application/vnd.ms-lrm', + 'ltf' => 'application/vnd.frogans.ltf', + 'lua' => 'text/x-lua', + 'luac' => 'application/x-lua-bytecode', + 'lvp' => 'audio/vnd.lucent.voice', + 'lwp' => 'application/vnd.lotus-wordpro', + 'lzh' => 'application/octet-stream', + 'm1v' => 'video/mpeg', + 'm2a' => 'audio/mpeg', + 'm2v' => 'video/mpeg', + 'm3a' => 'audio/mpeg', + 'm3u' => 'text/plain', + 'm3u8' => 'application/vnd.apple.mpegurl', + 'm4a' => 'audio/x-m4a', + 'm4p' => 'application/mp4', + 'm4s' => 'video/iso.segment', + 'm4u' => 'application/vnd.mpegurl', + 'm4v' => 'video/x-m4v', + 'm13' => 'application/x-msmediaview', + 'm14' => 'application/x-msmediaview', + 'm21' => 'application/mp21', + 'ma' => 'application/mathematica', + 'mads' => 'application/mads+xml', + 'maei' => 'application/mmt-aei+xml', + 'mag' => 'application/vnd.ecowin.chart', + 'maker' => 'application/vnd.framemaker', + 'man' => 'text/troff', + 'manifest' => 'text/cache-manifest', + 'map' => 'application/json', + 'mar' => 'application/octet-stream', + 'markdown' => 'text/markdown', + 'mathml' => 'application/mathml+xml', + 'mb' => 'application/mathematica', + 'mbk' => 'application/vnd.mobius.mbk', + 'mbox' => 'application/mbox', + 'mc1' => 'application/vnd.medcalcdata', + 'mcd' => 'application/vnd.mcd', + 'mcurl' => 'text/vnd.curl.mcurl', + 'md' => 'text/markdown', + 'mdb' => 'application/x-msaccess', + 'mdi' => 'image/vnd.ms-modi', + 'mdx' => 'text/mdx', + 'me' => 'text/troff', + 'mesh' => 'model/mesh', + 'meta4' => 'application/metalink4+xml', + 'metalink' => 'application/metalink+xml', + 'mets' => 'application/mets+xml', + 'mfm' => 'application/vnd.mfmp', + 'mft' => 'application/rpki-manifest', + 'mgp' => 'application/vnd.osgeo.mapguide.package', + 'mgz' => 'application/vnd.proteus.magazine', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mie' => 'application/x-mie', + 'mif' => 'application/vnd.mif', + 'mime' => 'message/rfc822', + 'mj2' => 'video/mj2', + 'mjp2' => 'video/mj2', + 'mjs' => 'application/javascript', + 'mk3d' => 'video/x-matroska', + 'mka' => 'audio/x-matroska', + 'mkd' => 'text/x-markdown', + 'mks' => 'video/x-matroska', + 'mkv' => 'video/x-matroska', + 'mlp' => 'application/vnd.dolby.mlp', + 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', + 'mmf' => 'application/vnd.smaf', + 'mml' => 'text/mathml', + 'mmr' => 'image/vnd.fujixerox.edmics-mmr', + 'mng' => 'video/x-mng', + 'mny' => 'application/x-msmoney', + 'mobi' => 'application/x-mobipocket-ebook', + 'mods' => 'application/mods+xml', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp2' => 'audio/mpeg', + 'mp2a' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4s' => 'application/mp4', + 'mp4v' => 'video/mp4', + 'mp21' => 'application/mp21', + 'mpc' => 'application/vnd.mophun.certificate', + 'mpd' => 'application/dash+xml', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpf' => 'application/media-policy-dataset+xml', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'mpga' => 'audio/mpeg', + 'mpkg' => 'application/vnd.apple.installer+xml', + 'mpm' => 'application/vnd.blueice.multipass', + 'mpn' => 'application/vnd.mophun.application', + 'mpp' => 'application/vnd.ms-project', + 'mpt' => 'application/vnd.ms-project', + 'mpy' => 'application/vnd.ibm.minipay', + 'mqy' => 'application/vnd.mobius.mqy', + 'mrc' => 'application/marc', + 'mrcx' => 'application/marcxml+xml', + 'ms' => 'text/troff', + 'mscml' => 'application/mediaservercontrol+xml', + 'mseed' => 'application/vnd.fdsn.mseed', + 'mseq' => 'application/vnd.mseq', + 'msf' => 'application/vnd.epson.msf', + 'msg' => 'application/vnd.ms-outlook', + 'msh' => 'model/mesh', + 'msi' => 'application/x-msdownload', + 'msl' => 'application/vnd.mobius.msl', + 'msm' => 'application/octet-stream', + 'msp' => 'application/octet-stream', + 'msty' => 'application/vnd.muvee.style', + 'mtl' => 'model/mtl', + 'mts' => 'model/vnd.mts', + 'mus' => 'application/vnd.musician', + 'musd' => 'application/mmt-usd+xml', + 'musicxml' => 'application/vnd.recordare.musicxml+xml', + 'mvb' => 'application/x-msmediaview', + 'mvt' => 'application/vnd.mapbox-vector-tile', + 'mwf' => 'application/vnd.mfer', + 'mxf' => 'application/mxf', + 'mxl' => 'application/vnd.recordare.musicxml', + 'mxmf' => 'audio/mobile-xmf', + 'mxml' => 'application/xv+xml', + 'mxs' => 'application/vnd.triscape.mxs', + 'mxu' => 'video/vnd.mpegurl', + 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', + 'n3' => 'text/n3', + 'nb' => 'application/mathematica', + 'nbp' => 'application/vnd.wolfram.player', + 'nc' => 'application/x-netcdf', + 'ncx' => 'application/x-dtbncx+xml', + 'nfo' => 'text/x-nfo', + 'ngdat' => 'application/vnd.nokia.n-gage.data', + 'nitf' => 'application/vnd.nitf', + 'nlu' => 'application/vnd.neurolanguage.nlu', + 'nml' => 'application/vnd.enliven', + 'nnd' => 'application/vnd.noblenet-directory', + 'nns' => 'application/vnd.noblenet-sealer', + 'nnw' => 'application/vnd.noblenet-web', + 'npx' => 'image/vnd.net-fpx', + 'nq' => 'application/n-quads', + 'nsc' => 'application/x-conference', + 'nsf' => 'application/vnd.lotus-notes', + 'nt' => 'application/n-triples', + 'ntf' => 'application/vnd.nitf', + 'numbers' => 'application/x-iwork-numbers-sffnumbers', + 'nzb' => 'application/x-nzb', + 'oa2' => 'application/vnd.fujitsu.oasys2', + 'oa3' => 'application/vnd.fujitsu.oasys3', + 'oas' => 'application/vnd.fujitsu.oasys', + 'obd' => 'application/x-msbinder', + 'obgx' => 'application/vnd.openblox.game+xml', + 'obj' => 'model/obj', + 'oda' => 'application/oda', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odft' => 'application/vnd.oasis.opendocument.formula-template', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'oga' => 'audio/ogg', + 'ogex' => 'model/vnd.opengex', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'omdoc' => 'application/omdoc+xml', + 'onepkg' => 'application/onenote', + 'onetmp' => 'application/onenote', + 'onetoc' => 'application/onenote', + 'onetoc2' => 'application/onenote', + 'opf' => 'application/oebps-package+xml', + 'opml' => 'text/x-opml', + 'oprc' => 'application/vnd.palm', + 'opus' => 'audio/ogg', + 'org' => 'text/x-org', + 'osf' => 'application/vnd.yamaha.openscoreformat', + 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', + 'osm' => 'application/vnd.openstreetmap.data+xml', + 'otc' => 'application/vnd.oasis.opendocument.chart-template', + 'otf' => 'font/otf', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'oti' => 'application/vnd.oasis.opendocument.image-template', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'ova' => 'application/x-virtualbox-ova', + 'ovf' => 'application/x-virtualbox-ovf', + 'owl' => 'application/rdf+xml', + 'oxps' => 'application/oxps', + 'oxt' => 'application/vnd.openofficeorg.extension', + 'p' => 'text/x-pascal', + 'p7a' => 'application/x-pkcs7-signature', + 'p7b' => 'application/x-pkcs7-certificates', + 'p7c' => 'application/pkcs7-mime', + 'p7m' => 'application/pkcs7-mime', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'p8' => 'application/pkcs8', + 'p10' => 'application/x-pkcs10', + 'p12' => 'application/x-pkcs12', + 'pac' => 'application/x-ns-proxy-autoconfig', + 'pages' => 'application/x-iwork-pages-sffpages', + 'pas' => 'text/x-pascal', + 'paw' => 'application/vnd.pawaafile', + 'pbd' => 'application/vnd.powerbuilder6', + 'pbm' => 'image/x-portable-bitmap', + 'pcap' => 'application/vnd.tcpdump.pcap', + 'pcf' => 'application/x-font-pcf', + 'pcl' => 'application/vnd.hp-pcl', + 'pclxl' => 'application/vnd.hp-pclxl', + 'pct' => 'image/x-pict', + 'pcurl' => 'application/vnd.curl.pcurl', + 'pcx' => 'image/x-pcx', + 'pdb' => 'application/x-pilot', + 'pde' => 'text/x-processing', + 'pdf' => 'application/pdf', + 'pem' => 'application/x-x509-user-cert', + 'pfa' => 'application/x-font-type1', + 'pfb' => 'application/x-font-type1', + 'pfm' => 'application/x-font-type1', + 'pfr' => 'application/font-tdpfr', + 'pfx' => 'application/x-pkcs12', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'pgp' => 'application/pgp', + 'phar' => 'application/octet-stream', + 'php' => 'application/x-httpd-php', + 'php3' => 'application/x-httpd-php', + 'php4' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'phtml' => 'application/x-httpd-php', + 'pic' => 'image/x-pict', + 'pkg' => 'application/octet-stream', + 'pki' => 'application/pkixcmp', + 'pkipath' => 'application/pkix-pkipath', + 'pkpass' => 'application/vnd.apple.pkpass', + 'pl' => 'application/x-perl', + 'plb' => 'application/vnd.3gpp.pic-bw-large', + 'plc' => 'application/vnd.mobius.plc', + 'plf' => 'application/vnd.pocketlearn', + 'pls' => 'application/pls+xml', + 'pm' => 'application/x-perl', + 'pml' => 'application/vnd.ctc-posml', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'portpkg' => 'application/vnd.macports.portpkg', + 'pot' => 'application/vnd.ms-powerpoint', + 'potm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppa' => 'application/vnd.ms-powerpoint', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', + 'ppd' => 'application/vnd.cups-ppd', + 'ppm' => 'image/x-portable-pixmap', + 'pps' => 'application/vnd.ms-powerpoint', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'ppt' => 'application/powerpoint', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'pqa' => 'application/vnd.palm', + 'prc' => 'model/prc', + 'pre' => 'application/vnd.lotus-freelance', + 'prf' => 'application/pics-rules', + 'provx' => 'application/provenance+xml', + 'ps' => 'application/postscript', + 'psb' => 'application/vnd.3gpp.pic-bw-small', + 'psd' => 'application/x-photoshop', + 'psf' => 'application/x-font-linux-psf', + 'pskcxml' => 'application/pskc+xml', + 'pti' => 'image/prs.pti', + 'ptid' => 'application/vnd.pvi.ptid1', + 'pub' => 'application/x-mspublisher', + 'pvb' => 'application/vnd.3gpp.pic-bw-var', + 'pwn' => 'application/vnd.3m.post-it-notes', + 'pya' => 'audio/vnd.ms-playready.media.pya', + 'pyv' => 'video/vnd.ms-playready.media.pyv', + 'qam' => 'application/vnd.epson.quickanime', + 'qbo' => 'application/vnd.intu.qbo', + 'qfx' => 'application/vnd.intu.qfx', + 'qps' => 'application/vnd.publishare-delta-tree', + 'qt' => 'video/quicktime', + 'qwd' => 'application/vnd.quark.quarkxpress', + 'qwt' => 'application/vnd.quark.quarkxpress', + 'qxb' => 'application/vnd.quark.quarkxpress', + 'qxd' => 'application/vnd.quark.quarkxpress', + 'qxl' => 'application/vnd.quark.quarkxpress', + 'qxt' => 'application/vnd.quark.quarkxpress', + 'ra' => 'audio/x-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'raml' => 'application/raml+yaml', + 'rapd' => 'application/route-apd+xml', + 'rar' => 'application/x-rar', + 'ras' => 'image/x-cmu-raster', + 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', + 'rdf' => 'application/rdf+xml', + 'rdz' => 'application/vnd.data-vision.rdz', + 'relo' => 'application/p2p-overlay+xml', + 'rep' => 'application/vnd.businessobjects', + 'res' => 'application/x-dtbresource+xml', + 'rgb' => 'image/x-rgb', + 'rif' => 'application/reginfo+xml', + 'rip' => 'audio/vnd.rip', + 'ris' => 'application/x-research-info-systems', + 'rl' => 'application/resource-lists+xml', + 'rlc' => 'image/vnd.fujixerox.edmics-rlc', + 'rld' => 'application/resource-lists-diff+xml', + 'rm' => 'audio/x-pn-realaudio', + 'rmi' => 'audio/midi', + 'rmp' => 'audio/x-pn-realaudio-plugin', + 'rms' => 'application/vnd.jcp.javame.midlet-rms', + 'rmvb' => 'application/vnd.rn-realmedia-vbr', + 'rnc' => 'application/relax-ng-compact-syntax', + 'rng' => 'application/xml', + 'roa' => 'application/rpki-roa', + 'roff' => 'text/troff', + 'rp9' => 'application/vnd.cloanto.rp9', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'rpss' => 'application/vnd.nokia.radio-presets', + 'rpst' => 'application/vnd.nokia.radio-preset', + 'rq' => 'application/sparql-query', + 'rs' => 'application/rls-services+xml', + 'rsa' => 'application/x-pkcs7', + 'rsat' => 'application/atsc-rsat+xml', + 'rsd' => 'application/rsd+xml', + 'rsheet' => 'application/urc-ressheet+xml', + 'rss' => 'application/rss+xml', + 'rtf' => 'text/rtf', + 'rtx' => 'text/richtext', + 'run' => 'application/x-makeself', + 'rusd' => 'application/route-usd+xml', + 'rv' => 'video/vnd.rn-realvideo', + 's' => 'text/x-asm', + 's3m' => 'audio/s3m', + 'saf' => 'application/vnd.yamaha.smaf-audio', + 'sass' => 'text/x-sass', + 'sbml' => 'application/sbml+xml', + 'sc' => 'application/vnd.ibm.secure-container', + 'scd' => 'application/x-msschedule', + 'scm' => 'application/vnd.lotus-screencam', + 'scq' => 'application/scvp-cv-request', + 'scs' => 'application/scvp-cv-response', + 'scss' => 'text/x-scss', + 'scurl' => 'text/vnd.curl.scurl', + 'sda' => 'application/vnd.stardivision.draw', + 'sdc' => 'application/vnd.stardivision.calc', + 'sdd' => 'application/vnd.stardivision.impress', + 'sdkd' => 'application/vnd.solent.sdkm+xml', + 'sdkm' => 'application/vnd.solent.sdkm+xml', + 'sdp' => 'application/sdp', + 'sdw' => 'application/vnd.stardivision.writer', + 'sea' => 'application/octet-stream', + 'see' => 'application/vnd.seemail', + 'seed' => 'application/vnd.fdsn.seed', + 'sema' => 'application/vnd.sema', + 'semd' => 'application/vnd.semd', + 'semf' => 'application/vnd.semf', + 'senmlx' => 'application/senml+xml', + 'sensmlx' => 'application/sensml+xml', + 'ser' => 'application/java-serialized-object', + 'setpay' => 'application/set-payment-initiation', + 'setreg' => 'application/set-registration-initiation', + 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', + 'sfs' => 'application/vnd.spotfire.sfs', + 'sfv' => 'text/x-sfv', + 'sgi' => 'image/sgi', + 'sgl' => 'application/vnd.stardivision.writer-global', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'shex' => 'text/shex', + 'shf' => 'application/shf+xml', + 'shtml' => 'text/html', + 'sid' => 'image/x-mrsid-image', + 'sieve' => 'application/sieve', + 'sig' => 'application/pgp-signature', + 'sil' => 'audio/silk', + 'silo' => 'model/mesh', + 'sis' => 'application/vnd.symbian.install', + 'sisx' => 'application/vnd.symbian.install', + 'sit' => 'application/x-stuffit', + 'sitx' => 'application/x-stuffitx', + 'siv' => 'application/sieve', + 'skd' => 'application/vnd.koan', + 'skm' => 'application/vnd.koan', + 'skp' => 'application/vnd.koan', + 'skt' => 'application/vnd.koan', + 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'slim' => 'text/slim', + 'slm' => 'text/slim', + 'sls' => 'application/route-s-tsid+xml', + 'slt' => 'application/vnd.epson.salt', + 'sm' => 'application/vnd.stepmania.stepchart', + 'smf' => 'application/vnd.stardivision.math', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'smv' => 'video/x-smv', + 'smzip' => 'application/vnd.stepmania.package', + 'snd' => 'audio/basic', + 'snf' => 'application/x-font-snf', + 'so' => 'application/octet-stream', + 'spc' => 'application/x-pkcs7-certificates', + 'spdx' => 'text/spdx', + 'spf' => 'application/vnd.yamaha.smaf-phrase', + 'spl' => 'application/x-futuresplash', + 'spot' => 'text/vnd.in3d.spot', + 'spp' => 'application/scvp-vp-response', + 'spq' => 'application/scvp-vp-request', + 'spx' => 'audio/ogg', + 'sql' => 'application/x-sql', + 'src' => 'application/x-wais-source', + 'srt' => 'application/x-subrip', + 'sru' => 'application/sru+xml', + 'srx' => 'application/sparql-results+xml', + 'ssdl' => 'application/ssdl+xml', + 'sse' => 'application/vnd.kodak-descriptor', + 'ssf' => 'application/vnd.epson.ssf', + 'ssml' => 'application/ssml+xml', + 'sst' => 'application/octet-stream', + 'st' => 'application/vnd.sailingtracker.track', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'std' => 'application/vnd.sun.xml.draw.template', + 'stf' => 'application/vnd.wt.stf', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'stk' => 'application/hyperstudio', + 'stl' => 'model/stl', + 'stpx' => 'model/step+xml', + 'stpxz' => 'model/step-xml+zip', + 'stpz' => 'model/step+zip', + 'str' => 'application/vnd.pg.format', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'styl' => 'text/stylus', + 'stylus' => 'text/stylus', + 'sub' => 'text/vnd.dvb.subtitle', + 'sus' => 'application/vnd.sus-calendar', + 'susp' => 'application/vnd.sus-calendar', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svc' => 'application/vnd.dvb.service', + 'svd' => 'application/vnd.svd', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'swa' => 'application/x-director', + 'swf' => 'application/x-shockwave-flash', + 'swi' => 'application/vnd.aristanetworks.swi', + 'swidtag' => 'application/swid+xml', + 'sxc' => 'application/vnd.sun.xml.calc', + 'sxd' => 'application/vnd.sun.xml.draw', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sxm' => 'application/vnd.sun.xml.math', + 'sxw' => 'application/vnd.sun.xml.writer', + 't' => 'text/troff', + 't3' => 'application/x-t3vm-image', + 't38' => 'image/t38', + 'taglet' => 'application/vnd.mynfc', + 'tao' => 'application/vnd.tao.intent-module-archive', + 'tap' => 'image/vnd.tencent.tap', + 'tar' => 'application/x-tar', + 'tcap' => 'application/vnd.3gpp2.tcap', + 'tcl' => 'application/x-tcl', + 'td' => 'application/urc-targetdesc+xml', + 'teacher' => 'application/vnd.smart.teacher', + 'tei' => 'application/tei+xml', + 'teicorpus' => 'application/tei+xml', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'text' => 'text/plain', + 'tfi' => 'application/thraud+xml', + 'tfm' => 'application/x-tex-tfm', + 'tfx' => 'image/tiff-fx', + 'tga' => 'image/x-tga', + 'tgz' => 'application/x-tar', + 'thmx' => 'application/vnd.ms-officetheme', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tk' => 'application/x-tcl', + 'tmo' => 'application/vnd.tmobile-livetv', + 'toml' => 'application/toml', + 'torrent' => 'application/x-bittorrent', + 'tpl' => 'application/vnd.groove-tool-template', + 'tpt' => 'application/vnd.trid.tpt', + 'tr' => 'text/troff', + 'tra' => 'application/vnd.trueapp', + 'trig' => 'application/trig', + 'trm' => 'application/x-msterminal', + 'ts' => 'video/mp2t', + 'tsd' => 'application/timestamped-data', + 'tsv' => 'text/tab-separated-values', + 'ttc' => 'font/collection', + 'ttf' => 'font/ttf', + 'ttl' => 'text/turtle', + 'ttml' => 'application/ttml+xml', + 'twd' => 'application/vnd.simtech-mindmapper', + 'twds' => 'application/vnd.simtech-mindmapper', + 'txd' => 'application/vnd.genomatix.tuxedo', + 'txf' => 'application/vnd.mobius.txf', + 'txt' => 'text/plain', + 'u3d' => 'model/u3d', + 'u8dsn' => 'message/global-delivery-status', + 'u8hdr' => 'message/global-headers', + 'u8mdn' => 'message/global-disposition-notification', + 'u8msg' => 'message/global', + 'u32' => 'application/x-authorware-bin', + 'ubj' => 'application/ubjson', + 'udeb' => 'application/x-debian-package', + 'ufd' => 'application/vnd.ufdl', + 'ufdl' => 'application/vnd.ufdl', + 'ulx' => 'application/x-glulx', + 'umj' => 'application/vnd.umajin', + 'unityweb' => 'application/vnd.unity', + 'uoml' => 'application/vnd.uoml+xml', + 'uri' => 'text/uri-list', + 'uris' => 'text/uri-list', + 'urls' => 'text/uri-list', + 'usdz' => 'model/vnd.usdz+zip', + 'ustar' => 'application/x-ustar', + 'utz' => 'application/vnd.uiq.theme', + 'uu' => 'text/x-uuencode', + 'uva' => 'audio/vnd.dece.audio', + 'uvd' => 'application/vnd.dece.data', + 'uvf' => 'application/vnd.dece.data', + 'uvg' => 'image/vnd.dece.graphic', + 'uvh' => 'video/vnd.dece.hd', + 'uvi' => 'image/vnd.dece.graphic', + 'uvm' => 'video/vnd.dece.mobile', + 'uvp' => 'video/vnd.dece.pd', + 'uvs' => 'video/vnd.dece.sd', + 'uvt' => 'application/vnd.dece.ttml+xml', + 'uvu' => 'video/vnd.uvvu.mp4', + 'uvv' => 'video/vnd.dece.video', + 'uvva' => 'audio/vnd.dece.audio', + 'uvvd' => 'application/vnd.dece.data', + 'uvvf' => 'application/vnd.dece.data', + 'uvvg' => 'image/vnd.dece.graphic', + 'uvvh' => 'video/vnd.dece.hd', + 'uvvi' => 'image/vnd.dece.graphic', + 'uvvm' => 'video/vnd.dece.mobile', + 'uvvp' => 'video/vnd.dece.pd', + 'uvvs' => 'video/vnd.dece.sd', + 'uvvt' => 'application/vnd.dece.ttml+xml', + 'uvvu' => 'video/vnd.uvvu.mp4', + 'uvvv' => 'video/vnd.dece.video', + 'uvvx' => 'application/vnd.dece.unspecified', + 'uvvz' => 'application/vnd.dece.zip', + 'uvx' => 'application/vnd.dece.unspecified', + 'uvz' => 'application/vnd.dece.zip', + 'vbox' => 'application/x-virtualbox-vbox', + 'vbox-extpack' => 'application/x-virtualbox-vbox-extpack', + 'vcard' => 'text/vcard', + 'vcd' => 'application/x-cdlink', + 'vcf' => 'text/x-vcard', + 'vcg' => 'application/vnd.groove-vcard', + 'vcs' => 'text/x-vcalendar', + 'vcx' => 'application/vnd.vcx', + 'vdi' => 'application/x-virtualbox-vdi', + 'vds' => 'model/vnd.sap.vds', + 'vhd' => 'application/x-virtualbox-vhd', + 'vis' => 'application/vnd.visionary', + 'viv' => 'video/vnd.vivo', + 'vlc' => 'application/videolan', + 'vmdk' => 'application/x-virtualbox-vmdk', + 'vob' => 'video/x-ms-vob', + 'vor' => 'application/vnd.stardivision.writer', + 'vox' => 'application/x-authorware-bin', + 'vrml' => 'model/vrml', + 'vsd' => 'application/vnd.visio', + 'vsf' => 'application/vnd.vsf', + 'vss' => 'application/vnd.visio', + 'vst' => 'application/vnd.visio', + 'vsw' => 'application/vnd.visio', + 'vtf' => 'image/vnd.valve.source.texture', + 'vtt' => 'text/vtt', + 'vtu' => 'model/vnd.vtu', + 'vxml' => 'application/voicexml+xml', + 'w3d' => 'application/x-director', + 'wad' => 'application/x-doom', + 'wadl' => 'application/vnd.sun.wadl+xml', + 'war' => 'application/java-archive', + 'wasm' => 'application/wasm', + 'wav' => 'audio/x-wav', + 'wax' => 'audio/x-ms-wax', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbs' => 'application/vnd.criticaltools.wbs+xml', + 'wbxml' => 'application/wbxml', + 'wcm' => 'application/vnd.ms-works', + 'wdb' => 'application/vnd.ms-works', + 'wdp' => 'image/vnd.ms-photo', + 'weba' => 'audio/webm', + 'webapp' => 'application/x-web-app-manifest+json', + 'webm' => 'video/webm', + 'webmanifest' => 'application/manifest+json', + 'webp' => 'image/webp', + 'wg' => 'application/vnd.pmi.widget', + 'wgt' => 'application/widget', + 'wif' => 'application/watcherinfo+xml', + 'wks' => 'application/vnd.ms-works', + 'wm' => 'video/x-ms-wm', + 'wma' => 'audio/x-ms-wma', + 'wmd' => 'application/x-ms-wmd', + 'wmf' => 'image/wmf', + 'wml' => 'text/vnd.wap.wml', + 'wmlc' => 'application/wmlc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wmz' => 'application/x-msmetafile', + 'woff' => 'font/woff', + 'woff2' => 'font/woff2', + 'word' => 'application/msword', + 'wpd' => 'application/vnd.wordperfect', + 'wpl' => 'application/vnd.ms-wpl', + 'wps' => 'application/vnd.ms-works', + 'wqd' => 'application/vnd.wqd', + 'wri' => 'application/x-mswrite', + 'wrl' => 'model/vrml', + 'wsc' => 'message/vnd.wfa.wsc', + 'wsdl' => 'application/wsdl+xml', + 'wspolicy' => 'application/wspolicy+xml', + 'wtb' => 'application/vnd.webturbo', + 'wvx' => 'video/x-ms-wvx', + 'x3d' => 'model/x3d+xml', + 'x3db' => 'model/x3d+fastinfoset', + 'x3dbz' => 'model/x3d+binary', + 'x3dv' => 'model/x3d-vrml', + 'x3dvz' => 'model/x3d+vrml', + 'x3dz' => 'model/x3d+xml', + 'x32' => 'application/x-authorware-bin', + 'x_b' => 'model/vnd.parasolid.transmit.binary', + 'x_t' => 'model/vnd.parasolid.transmit.text', + 'xaml' => 'application/xaml+xml', + 'xap' => 'application/x-silverlight-app', + 'xar' => 'application/vnd.xara', + 'xav' => 'application/xcap-att+xml', + 'xbap' => 'application/x-ms-xbap', + 'xbd' => 'application/vnd.fujixerox.docuworks.binder', + 'xbm' => 'image/x-xbitmap', + 'xca' => 'application/xcap-caps+xml', + 'xcs' => 'application/calendar+xml', + 'xdf' => 'application/xcap-diff+xml', + 'xdm' => 'application/vnd.syncml.dm+xml', + 'xdp' => 'application/vnd.adobe.xdp+xml', + 'xdssc' => 'application/dssc+xml', + 'xdw' => 'application/vnd.fujixerox.docuworks', + 'xel' => 'application/xcap-el+xml', + 'xenc' => 'application/xenc+xml', + 'xer' => 'application/patch-ops-error+xml', + 'xfdf' => 'application/vnd.adobe.xfdf', + 'xfdl' => 'application/vnd.xfdl', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xhvml' => 'application/xv+xml', + 'xif' => 'image/vnd.xiff', + 'xl' => 'application/excel', + 'xla' => 'application/vnd.ms-excel', + 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', + 'xlc' => 'application/vnd.ms-excel', + 'xlf' => 'application/xliff+xml', + 'xlm' => 'application/vnd.ms-excel', + 'xls' => 'application/vnd.ms-excel', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xlt' => 'application/vnd.ms-excel', + 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'xlw' => 'application/vnd.ms-excel', + 'xm' => 'audio/xm', + 'xml' => 'application/xml', + 'xns' => 'application/xcap-ns+xml', + 'xo' => 'application/vnd.olpc-sugar', + 'xop' => 'application/xop+xml', + 'xpi' => 'application/x-xpinstall', + 'xpl' => 'application/xproc+xml', + 'xpm' => 'image/x-xpixmap', + 'xpr' => 'application/vnd.is-xpr', + 'xps' => 'application/vnd.ms-xpsdocument', + 'xpw' => 'application/vnd.intercon.formnet', + 'xpx' => 'application/vnd.intercon.formnet', + 'xsd' => 'application/xml', + 'xsl' => 'application/xml', + 'xslt' => 'application/xslt+xml', + 'xsm' => 'application/vnd.syncml+xml', + 'xspf' => 'application/xspf+xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xvm' => 'application/xv+xml', + 'xvml' => 'application/xv+xml', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'xz' => 'application/x-xz', + 'yaml' => 'text/yaml', + 'yang' => 'application/yang', + 'yin' => 'application/yin+xml', + 'yml' => 'text/yaml', + 'ymp' => 'text/x-suse-ymp', + 'z' => 'application/x-compress', + 'z1' => 'application/x-zmachine', + 'z2' => 'application/x-zmachine', + 'z3' => 'application/x-zmachine', + 'z4' => 'application/x-zmachine', + 'z5' => 'application/x-zmachine', + 'z6' => 'application/x-zmachine', + 'z7' => 'application/x-zmachine', + 'z8' => 'application/x-zmachine', + 'zaz' => 'application/vnd.zzazz.deck+xml', + 'zip' => 'application/zip', + 'zir' => 'application/vnd.zul', + 'zirz' => 'application/vnd.zul', + 'zmm' => 'application/vnd.handheld-entertainment+xml', + 'zsh' => 'text/x-scriptzsh', + ]; + + public function lookupMimeType(string $extension): ?string + { + return self::MIME_TYPES_FOR_EXTENSIONS[$extension] ?? null; + } +} diff --git a/msd/vendor/league/mime-type-detection/src/MimeTypeDetector.php b/msd/vendor/league/mime-type-detection/src/MimeTypeDetector.php new file mode 100644 index 0000000..649c46f --- /dev/null +++ b/msd/vendor/league/mime-type-detection/src/MimeTypeDetector.php @@ -0,0 +1,19 @@ + $overrides + */ + public function __construct(ExtensionToMimeTypeMap $innerMap, array $overrides) + { + $this->innerMap = $innerMap; + $this->overrides = $overrides; + } + + public function lookupMimeType(string $extension): ?string + { + return $this->overrides[$extension] ?? $this->innerMap->lookupMimeType($extension); + } +} diff --git a/msd/vendor/monolog/monolog/CHANGELOG.md b/msd/vendor/monolog/monolog/CHANGELOG.md new file mode 100644 index 0000000..7f9db2b --- /dev/null +++ b/msd/vendor/monolog/monolog/CHANGELOG.md @@ -0,0 +1,608 @@ +### 2.8.0 (2022-07-24) + + * Deprecated `CubeHandler` and `PHPConsoleHandler` as both projects are abandoned and those should not be used anymore (#1734) + * Added RFC 5424 level (`7` to `0`) support to `Logger::log` and `Logger::addRecord` to increase interoperability (#1723) + * Added support for `__toString` for objects which are not json serializable in `JsonFormatter` (#1733) + * Added `GoogleCloudLoggingFormatter` (#1719) + * Added support for Predis 2.x (#1732) + * Added `AmqpHandler->setExtraAttributes` to allow configuring attributes when using an AMQPExchange (#1724) + * Fixed serialization/unserialization of handlers to make sure private properties are included (#1727) + * Fixed allowInlineLineBreaks in LineFormatter causing issues with windows paths containing `\n` or `\r` sequences (#1720) + * Fixed max normalization depth not being taken into account when formatting exceptions with a deep chain of previous exceptions (#1726) + * Fixed PHP 8.2 deprecation warnings (#1722) + * Fixed rare race condition or filesystem issue where StreamHandler is unable to create the directory the log should go into yet it exists already (#1678) + +### 2.7.0 (2022-06-09) + + * Added `$datetime` parameter to `Logger::addRecord` as low level API to allow logging into the past or future (#1682) + * Added `Logger::useLoggingLoopDetection` to allow disabling cyclic logging detection in concurrent frameworks (#1681) + * Fixed handling of fatal errors if callPrevious is disabled in ErrorHandler (#1670) + * Marked the reusable `Monolog\Test\TestCase` class as `@internal` to make sure PHPStorm does not show it above PHPUnit, you may still use it to test your own handlers/etc though (#1677) + * Fixed RotatingFileHandler issue when the date format contained slashes (#1671) + +### 2.6.0 (2022-05-10) + + * Deprecated `SwiftMailerHandler`, use `SymfonyMailerHandler` instead + * Added `SymfonyMailerHandler` (#1663) + * Added ElasticSearch 8.x support to the ElasticsearchHandler (#1662) + * Added a way to filter/modify stack traces in LineFormatter (#1665) + * Fixed UdpSocket not being able to reopen/reconnect after close() + * Fixed infinite loops if a Handler is triggering logging while handling log records + +### 2.5.0 (2022-04-08) + + * Added `callType` to IntrospectionProcessor (#1612) + * Fixed AsMonologProcessor syntax to be compatible with PHP 7.2 (#1651) + +### 2.4.0 (2022-03-14) + + * Added [`Monolog\LogRecord`](src/Monolog/LogRecord.php) interface that can be used to type-hint records like `array|\Monolog\LogRecord $record` to be forward compatible with the upcoming Monolog 3 changes + * Added `includeStacktraces` constructor params to LineFormatter & JsonFormatter (#1603) + * Added `persistent`, `timeout`, `writingTimeout`, `connectionTimeout`, `chunkSize` constructor params to SocketHandler and derivatives (#1600) + * Added `AsMonologProcessor` PHP attribute which can help autowiring / autoconfiguration of processors if frameworks / integrations decide to make use of it. This is useless when used purely with Monolog (#1637) + * Added support for keeping native BSON types as is in MongoDBFormatter (#1620) + * Added support for a `user_agent` key in WebProcessor, disabled by default but you can use it by configuring the $extraFields you want (#1613) + * Added support for username/userIcon in SlackWebhookHandler (#1617) + * Added extension points to BrowserConsoleHandler (#1593) + * Added record message/context/extra info to exceptions thrown when a StreamHandler cannot open its stream to avoid completely losing the data logged (#1630) + * Fixed error handler signature to accept a null $context which happens with internal PHP errors (#1614) + * Fixed a few setter methods not returning `self` (#1609) + * Fixed handling of records going over the max Telegram message length (#1616) + +### 2.3.5 (2021-10-01) + + * Fixed regression in StreamHandler since 2.3.3 on systems with the memory_limit set to >=20GB (#1592) + +### 2.3.4 (2021-09-15) + + * Fixed support for psr/log 3.x (#1589) + +### 2.3.3 (2021-09-14) + + * Fixed memory usage when using StreamHandler and calling stream_get_contents on the resource you passed to it (#1578, #1577) + * Fixed support for psr/log 2.x (#1587) + * Fixed some type annotations + +### 2.3.2 (2021-07-23) + + * Fixed compatibility with PHP 7.2 - 7.4 when experiencing PCRE errors (#1568) + +### 2.3.1 (2021-07-14) + + * Fixed Utils::getClass handling of anonymous classes not being fully compatible with PHP 8 (#1563) + * Fixed some `@inheritDoc` annotations having the wrong case + +### 2.3.0 (2021-07-05) + + * Added a ton of PHPStan type annotations as well as type aliases on Monolog\Logger for Record, Level and LevelName that you can import (#1557) + * Added ability to customize date format when using JsonFormatter (#1561) + * Fixed FilterHandler not calling reset on its internal handler when reset() is called on it (#1531) + * Fixed SyslogUdpHandler not setting the timezone correctly on DateTimeImmutable instances (#1540) + * Fixed StreamHandler thread safety - chunk size set to 2GB now to avoid interlacing when doing concurrent writes (#1553) + +### 2.2.0 (2020-12-14) + + * Added JSON_PARTIAL_OUTPUT_ON_ERROR to default json encoding flags, to avoid dropping entire context data or even records due to an invalid subset of it somewhere + * Added setDateFormat to NormalizerFormatter (and Line/Json formatters by extension) to allow changing this after object creation + * Added RedisPubSubHandler to log records to a Redis channel using PUBLISH + * Added support for Elastica 7, and deprecated the $type argument of ElasticaFormatter which is not in use anymore as of Elastica 7 + * Added support for millisecond write timeouts in SocketHandler, you can now pass floats to setWritingTimeout, e.g. 0.2 is 200ms + * Added support for unix sockets in SyslogUdpHandler (set $port to 0 to make the $host a unix socket) + * Added handleBatch support for TelegramBotHandler + * Added RFC5424e extended date format including milliseconds to SyslogUdpHandler + * Added support for configuring handlers with numeric level values in strings (coming from e.g. env vars) + * Fixed Wildfire/FirePHP/ChromePHP handling of unicode characters + * Fixed PHP 8 issues in SyslogUdpHandler + * Fixed internal type error when mbstring is missing + +### 2.1.1 (2020-07-23) + + * Fixed removing of json encoding options + * Fixed type hint of $level not accepting strings in SendGridHandler and OverflowHandler + * Fixed SwiftMailerHandler not accepting email templates with an empty subject + * Fixed array access on null in RavenHandler + * Fixed unique_id in WebProcessor not being disableable + +### 2.1.0 (2020-05-22) + + * Added `JSON_INVALID_UTF8_SUBSTITUTE` to default json flags, so that invalid UTF8 characters now get converted to [�](https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character) instead of being converted from ISO-8859-15 to UTF8 as it was before, which was hardly a comprehensive solution + * Added `$ignoreEmptyContextAndExtra` option to JsonFormatter to skip empty context/extra entirely from the output + * Added `$parseMode`, `$disableWebPagePreview` and `$disableNotification` options to TelegramBotHandler + * Added tentative support for PHP 8 + * NormalizerFormatter::addJsonEncodeOption and removeJsonEncodeOption are now public to allow modifying default json flags + * Fixed GitProcessor type error when there is no git repo present + * Fixed normalization of SoapFault objects containing deeply nested objects as "detail" + * Fixed support for relative paths in RotatingFileHandler + +### 2.0.2 (2019-12-20) + + * Fixed ElasticsearchHandler swallowing exceptions details when failing to index log records + * Fixed normalization of SoapFault objects containing non-strings as "detail" in LineFormatter + * Fixed formatting of resources in JsonFormatter + * Fixed RedisHandler failing to use MULTI properly when passed a proxied Redis instance (e.g. in Symfony with lazy services) + * Fixed FilterHandler triggering a notice when handleBatch was filtering all records passed to it + * Fixed Turkish locale messing up the conversion of level names to their constant values + +### 2.0.1 (2019-11-13) + + * Fixed normalization of Traversables to avoid traversing them as not all of them are rewindable + * Fixed setFormatter/getFormatter to forward to the nested handler in FilterHandler, FingersCrossedHandler, BufferHandler, OverflowHandler and SamplingHandler + * Fixed BrowserConsoleHandler formatting when using multiple styles + * Fixed normalization of exception codes to be always integers even for PDOException which have them as numeric strings + * Fixed normalization of SoapFault objects containing non-strings as "detail" + * Fixed json encoding across all handlers to always attempt recovery of non-UTF-8 strings instead of failing the whole encoding + * Fixed ChromePHPHandler to avoid sending more data than latest Chrome versions allow in headers (4KB down from 256KB). + * Fixed type error in BrowserConsoleHandler when the context array of log records was not associative. + +### 2.0.0 (2019-08-30) + + * BC Break: This is a major release, see [UPGRADE.md](UPGRADE.md) for details if you are coming from a 1.x release + * BC Break: Logger methods log/debug/info/notice/warning/error/critical/alert/emergency now have explicit void return types + * Added FallbackGroupHandler which works like the WhatFailureGroupHandler but stops dispatching log records as soon as one handler accepted it + * Fixed support for UTF-8 when cutting strings to avoid cutting a multibyte-character in half + * Fixed normalizers handling of exception backtraces to avoid serializing arguments in some cases + * Fixed date timezone handling in SyslogUdpHandler + +### 2.0.0-beta2 (2019-07-06) + + * BC Break: This is a major release, see [UPGRADE.md](UPGRADE.md) for details if you are coming from a 1.x release + * BC Break: PHP 7.2 is now the minimum required PHP version. + * BC Break: Removed SlackbotHandler, RavenHandler and HipChatHandler, see [UPGRADE.md](UPGRADE.md) for details + * Added OverflowHandler which will only flush log records to its nested handler when reaching a certain amount of logs (i.e. only pass through when things go really bad) + * Added TelegramBotHandler to log records to a [Telegram](https://core.telegram.org/bots/api) bot account + * Added support for JsonSerializable when normalizing exceptions + * Added support for RFC3164 (outdated BSD syslog protocol) to SyslogUdpHandler + * Added SoapFault details to formatted exceptions + * Fixed DeduplicationHandler silently failing to start when file could not be opened + * Fixed issue in GroupHandler and WhatFailureGroupHandler where setting multiple processors would duplicate records + * Fixed GelfFormatter losing some data when one attachment was too long + * Fixed issue in SignalHandler restarting syscalls functionality + * Improved performance of LogglyHandler when sending multiple logs in a single request + +### 2.0.0-beta1 (2018-12-08) + + * BC Break: This is a major release, see [UPGRADE.md](UPGRADE.md) for details if you are coming from a 1.x release + * BC Break: PHP 7.1 is now the minimum required PHP version. + * BC Break: Quite a few interface changes, only relevant if you implemented your own handlers/processors/formatters + * BC Break: Removed non-PSR-3 methods to add records, all the `add*` (e.g. `addWarning`) methods as well as `emerg`, `crit`, `err` and `warn` + * BC Break: The record timezone is now set per Logger instance and not statically anymore + * BC Break: There is no more default handler configured on empty Logger instances + * BC Break: ElasticSearchHandler renamed to ElasticaHandler + * BC Break: Various handler-specific breaks, see [UPGRADE.md](UPGRADE.md) for details + * Added scalar type hints and return hints in all the places it was possible. Switched strict_types on for more reliability. + * Added DateTimeImmutable support, all record datetime are now immutable, and will toString/json serialize with the correct date format, including microseconds (unless disabled) + * Added timezone and microseconds to the default date format + * Added SendGridHandler to use the SendGrid API to send emails + * Added LogmaticHandler to use the Logmatic.io API to store log records + * Added SqsHandler to send log records to an AWS SQS queue + * Added ElasticsearchHandler to send records via the official ES library. Elastica users should now use ElasticaHandler instead of ElasticSearchHandler + * Added NoopHandler which is similar to the NullHandle but does not prevent the bubbling of log records to handlers further down the configuration, useful for temporarily disabling a handler in configuration files + * Added ProcessHandler to write log output to the STDIN of a given process + * Added HostnameProcessor that adds the machine's hostname to log records + * Added a `$dateFormat` option to the PsrLogMessageProcessor which lets you format DateTime instances nicely + * Added support for the PHP 7.x `mongodb` extension in the MongoDBHandler + * Fixed many minor issues in various handlers, and probably added a few regressions too + +### 1.26.1 (2021-05-28) + + * Fixed PHP 8.1 deprecation warning + +### 1.26.0 (2020-12-14) + + * Added $dateFormat and $removeUsedContextFields arguments to PsrLogMessageProcessor (backport from 2.x) + +### 1.25.5 (2020-07-23) + + * Fixed array access on null in RavenHandler + * Fixed unique_id in WebProcessor not being disableable + +### 1.25.4 (2020-05-22) + + * Fixed GitProcessor type error when there is no git repo present + * Fixed normalization of SoapFault objects containing deeply nested objects as "detail" + * Fixed support for relative paths in RotatingFileHandler + +### 1.25.3 (2019-12-20) + + * Fixed formatting of resources in JsonFormatter + * Fixed RedisHandler failing to use MULTI properly when passed a proxied Redis instance (e.g. in Symfony with lazy services) + * Fixed FilterHandler triggering a notice when handleBatch was filtering all records passed to it + * Fixed Turkish locale messing up the conversion of level names to their constant values + +### 1.25.2 (2019-11-13) + + * Fixed normalization of Traversables to avoid traversing them as not all of them are rewindable + * Fixed setFormatter/getFormatter to forward to the nested handler in FilterHandler, FingersCrossedHandler, BufferHandler and SamplingHandler + * Fixed BrowserConsoleHandler formatting when using multiple styles + * Fixed normalization of exception codes to be always integers even for PDOException which have them as numeric strings + * Fixed normalization of SoapFault objects containing non-strings as "detail" + * Fixed json encoding across all handlers to always attempt recovery of non-UTF-8 strings instead of failing the whole encoding + +### 1.25.1 (2019-09-06) + + * Fixed forward-compatible interfaces to be compatible with Monolog 1.x too. + +### 1.25.0 (2019-09-06) + + * Deprecated SlackbotHandler, use SlackWebhookHandler or SlackHandler instead + * Deprecated RavenHandler, use sentry/sentry 2.x and their Sentry\Monolog\Handler instead + * Deprecated HipChatHandler, migrate to Slack and use SlackWebhookHandler or SlackHandler instead + * Added forward-compatible interfaces and traits FormattableHandlerInterface, FormattableHandlerTrait, ProcessableHandlerInterface, ProcessableHandlerTrait. If you use modern PHP and want to make code compatible with Monolog 1 and 2 this can help. You will have to require at least Monolog 1.25 though. + * Added support for RFC3164 (outdated BSD syslog protocol) to SyslogUdpHandler + * Fixed issue in GroupHandler and WhatFailureGroupHandler where setting multiple processors would duplicate records + * Fixed issue in SignalHandler restarting syscalls functionality + * Fixed normalizers handling of exception backtraces to avoid serializing arguments in some cases + * Fixed ZendMonitorHandler to work with the latest Zend Server versions + * Fixed ChromePHPHandler to avoid sending more data than latest Chrome versions allow in headers (4KB down from 256KB). + +### 1.24.0 (2018-11-05) + + * BC Notice: If you are extending any of the Monolog's Formatters' `normalize` method, make sure you add the new `$depth = 0` argument to your function signature to avoid strict PHP warnings. + * Added a `ResettableInterface` in order to reset/reset/clear/flush handlers and processors + * Added a `ProcessorInterface` as an optional way to label a class as being a processor (mostly useful for autowiring dependency containers) + * Added a way to log signals being received using Monolog\SignalHandler + * Added ability to customize error handling at the Logger level using Logger::setExceptionHandler + * Added InsightOpsHandler to migrate users of the LogEntriesHandler + * Added protection to NormalizerFormatter against circular and very deep structures, it now stops normalizing at a depth of 9 + * Added capture of stack traces to ErrorHandler when logging PHP errors + * Added RavenHandler support for a `contexts` context or extra key to forward that to Sentry's contexts + * Added forwarding of context info to FluentdFormatter + * Added SocketHandler::setChunkSize to override the default chunk size in case you must send large log lines to rsyslog for example + * Added ability to extend/override BrowserConsoleHandler + * Added SlackWebhookHandler::getWebhookUrl and SlackHandler::getToken to enable class extensibility + * Added SwiftMailerHandler::getSubjectFormatter to enable class extensibility + * Dropped official support for HHVM in test builds + * Fixed normalization of exception traces when call_user_func is used to avoid serializing objects and the data they contain + * Fixed naming of fields in Slack handler, all field names are now capitalized in all cases + * Fixed HipChatHandler bug where slack dropped messages randomly + * Fixed normalization of objects in Slack handlers + * Fixed support for PHP7's Throwable in NewRelicHandler + * Fixed race bug when StreamHandler sometimes incorrectly reported it failed to create a directory + * Fixed table row styling issues in HtmlFormatter + * Fixed RavenHandler dropping the message when logging exception + * Fixed WhatFailureGroupHandler skipping processors when using handleBatch + and implement it where possible + * Fixed display of anonymous class names + +### 1.23.0 (2017-06-19) + + * Improved SyslogUdpHandler's support for RFC5424 and added optional `$ident` argument + * Fixed GelfHandler truncation to be per field and not per message + * Fixed compatibility issue with PHP <5.3.6 + * Fixed support for headless Chrome in ChromePHPHandler + * Fixed support for latest Aws SDK in DynamoDbHandler + * Fixed support for SwiftMailer 6.0+ in SwiftMailerHandler + +### 1.22.1 (2017-03-13) + + * Fixed lots of minor issues in the new Slack integrations + * Fixed support for allowInlineLineBreaks in LineFormatter when formatting exception backtraces + +### 1.22.0 (2016-11-26) + + * Added SlackbotHandler and SlackWebhookHandler to set up Slack integration more easily + * Added MercurialProcessor to add mercurial revision and branch names to log records + * Added support for AWS SDK v3 in DynamoDbHandler + * Fixed fatal errors occurring when normalizing generators that have been fully consumed + * Fixed RollbarHandler to include a level (rollbar level), monolog_level (original name), channel and datetime (unix) + * Fixed RollbarHandler not flushing records automatically, calling close() explicitly is not necessary anymore + * Fixed SyslogUdpHandler to avoid sending empty frames + * Fixed a few PHP 7.0 and 7.1 compatibility issues + +### 1.21.0 (2016-07-29) + + * Break: Reverted the addition of $context when the ErrorHandler handles regular php errors from 1.20.0 as it was causing issues + * Added support for more formats in RotatingFileHandler::setFilenameFormat as long as they have Y, m and d in order + * Added ability to format the main line of text the SlackHandler sends by explicitly setting a formatter on the handler + * Added information about SoapFault instances in NormalizerFormatter + * Added $handleOnlyReportedErrors option on ErrorHandler::registerErrorHandler (default true) to allow logging of all errors no matter the error_reporting level + +### 1.20.0 (2016-07-02) + + * Added FingersCrossedHandler::activate() to manually trigger the handler regardless of the activation policy + * Added StreamHandler::getUrl to retrieve the stream's URL + * Added ability to override addRow/addTitle in HtmlFormatter + * Added the $context to context information when the ErrorHandler handles a regular php error + * Deprecated RotatingFileHandler::setFilenameFormat to only support 3 formats: Y, Y-m and Y-m-d + * Fixed WhatFailureGroupHandler to work with PHP7 throwables + * Fixed a few minor bugs + +### 1.19.0 (2016-04-12) + + * Break: StreamHandler will not close streams automatically that it does not own. If you pass in a stream (not a path/url), then it will not close it for you. You can retrieve those using getStream() if needed + * Added DeduplicationHandler to remove duplicate records from notifications across multiple requests, useful for email or other notifications on errors + * Added ability to use `%message%` and other LineFormatter replacements in the subject line of emails sent with NativeMailHandler and SwiftMailerHandler + * Fixed HipChatHandler handling of long messages + +### 1.18.2 (2016-04-02) + + * Fixed ElasticaFormatter to use more precise dates + * Fixed GelfMessageFormatter sending too long messages + +### 1.18.1 (2016-03-13) + + * Fixed SlackHandler bug where slack dropped messages randomly + * Fixed RedisHandler issue when using with the PHPRedis extension + * Fixed AmqpHandler content-type being incorrectly set when using with the AMQP extension + * Fixed BrowserConsoleHandler regression + +### 1.18.0 (2016-03-01) + + * Added optional reduction of timestamp precision via `Logger->useMicrosecondTimestamps(false)`, disabling it gets you a bit of performance boost but reduces the precision to the second instead of microsecond + * Added possibility to skip some extra stack frames in IntrospectionProcessor if you have some library wrapping Monolog that is always adding frames + * Added `Logger->withName` to clone a logger (keeping all handlers) with a new name + * Added FluentdFormatter for the Fluentd unix socket protocol + * Added HandlerWrapper base class to ease the creation of handler wrappers, just extend it and override as needed + * Added support for replacing context sub-keys using `%context.*%` in LineFormatter + * Added support for `payload` context value in RollbarHandler + * Added setRelease to RavenHandler to describe the application version, sent with every log + * Added support for `fingerprint` context value in RavenHandler + * Fixed JSON encoding errors that would gobble up the whole log record, we now handle those more gracefully by dropping chars as needed + * Fixed write timeouts in SocketHandler and derivatives, set to 10sec by default, lower it with `setWritingTimeout()` + * Fixed PHP7 compatibility with regard to Exception/Throwable handling in a few places + +### 1.17.2 (2015-10-14) + + * Fixed ErrorHandler compatibility with non-Monolog PSR-3 loggers + * Fixed SlackHandler handling to use slack functionalities better + * Fixed SwiftMailerHandler bug when sending multiple emails they all had the same id + * Fixed 5.3 compatibility regression + +### 1.17.1 (2015-08-31) + + * Fixed RollbarHandler triggering PHP notices + +### 1.17.0 (2015-08-30) + + * Added support for `checksum` and `release` context/extra values in RavenHandler + * Added better support for exceptions in RollbarHandler + * Added UidProcessor::getUid + * Added support for showing the resource type in NormalizedFormatter + * Fixed IntrospectionProcessor triggering PHP notices + +### 1.16.0 (2015-08-09) + + * Added IFTTTHandler to notify ifttt.com triggers + * Added Logger::setHandlers() to allow setting/replacing all handlers + * Added $capSize in RedisHandler to cap the log size + * Fixed StreamHandler creation of directory to only trigger when the first log write happens + * Fixed bug in the handling of curl failures + * Fixed duplicate logging of fatal errors when both error and fatal error handlers are registered in monolog's ErrorHandler + * Fixed missing fatal errors records with handlers that need to be closed to flush log records + * Fixed TagProcessor::addTags support for associative arrays + +### 1.15.0 (2015-07-12) + + * Added addTags and setTags methods to change a TagProcessor + * Added automatic creation of directories if they are missing for a StreamHandler to open a log file + * Added retry functionality to Loggly, Cube and Mandrill handlers so they retry up to 5 times in case of network failure + * Fixed process exit code being incorrectly reset to 0 if ErrorHandler::registerExceptionHandler was used + * Fixed HTML/JS escaping in BrowserConsoleHandler + * Fixed JSON encoding errors being silently suppressed (PHP 5.5+ only) + +### 1.14.0 (2015-06-19) + + * Added PHPConsoleHandler to send record to Chrome's PHP Console extension and library + * Added support for objects implementing __toString in the NormalizerFormatter + * Added support for HipChat's v2 API in HipChatHandler + * Added Logger::setTimezone() to initialize the timezone monolog should use in case date.timezone isn't correct for your app + * Added an option to send formatted message instead of the raw record on PushoverHandler via ->useFormattedMessage(true) + * Fixed curl errors being silently suppressed + +### 1.13.1 (2015-03-09) + + * Fixed regression in HipChat requiring a new token to be created + +### 1.13.0 (2015-03-05) + + * Added Registry::hasLogger to check for the presence of a logger instance + * Added context.user support to RavenHandler + * Added HipChat API v2 support in the HipChatHandler + * Added NativeMailerHandler::addParameter to pass params to the mail() process + * Added context data to SlackHandler when $includeContextAndExtra is true + * Added ability to customize the Swift_Message per-email in SwiftMailerHandler + * Fixed SwiftMailerHandler to lazily create message instances if a callback is provided + * Fixed serialization of INF and NaN values in Normalizer and LineFormatter + +### 1.12.0 (2014-12-29) + + * Break: HandlerInterface::isHandling now receives a partial record containing only a level key. This was always the intent and does not break any Monolog handler but is strictly speaking a BC break and you should check if you relied on any other field in your own handlers. + * Added PsrHandler to forward records to another PSR-3 logger + * Added SamplingHandler to wrap around a handler and include only every Nth record + * Added MongoDBFormatter to support better storage with MongoDBHandler (it must be enabled manually for now) + * Added exception codes in the output of most formatters + * Added LineFormatter::includeStacktraces to enable exception stack traces in logs (uses more than one line) + * Added $useShortAttachment to SlackHandler to minify attachment size and $includeExtra to append extra data + * Added $host to HipChatHandler for users of private instances + * Added $transactionName to NewRelicHandler and support for a transaction_name context value + * Fixed MandrillHandler to avoid outputting API call responses + * Fixed some non-standard behaviors in SyslogUdpHandler + +### 1.11.0 (2014-09-30) + + * Break: The NewRelicHandler extra and context data are now prefixed with extra_ and context_ to avoid clashes. Watch out if you have scripts reading those from the API and rely on names + * Added WhatFailureGroupHandler to suppress any exception coming from the wrapped handlers and avoid chain failures if a logging service fails + * Added MandrillHandler to send emails via the Mandrillapp.com API + * Added SlackHandler to log records to a Slack.com account + * Added FleepHookHandler to log records to a Fleep.io account + * Added LogglyHandler::addTag to allow adding tags to an existing handler + * Added $ignoreEmptyContextAndExtra to LineFormatter to avoid empty [] at the end + * Added $useLocking to StreamHandler and RotatingFileHandler to enable flock() while writing + * Added support for PhpAmqpLib in the AmqpHandler + * Added FingersCrossedHandler::clear and BufferHandler::clear to reset them between batches in long running jobs + * Added support for adding extra fields from $_SERVER in the WebProcessor + * Fixed support for non-string values in PrsLogMessageProcessor + * Fixed SwiftMailer messages being sent with the wrong date in long running scripts + * Fixed minor PHP 5.6 compatibility issues + * Fixed BufferHandler::close being called twice + +### 1.10.0 (2014-06-04) + + * Added Logger::getHandlers() and Logger::getProcessors() methods + * Added $passthruLevel argument to FingersCrossedHandler to let it always pass some records through even if the trigger level is not reached + * Added support for extra data in NewRelicHandler + * Added $expandNewlines flag to the ErrorLogHandler to create multiple log entries when a message has multiple lines + +### 1.9.1 (2014-04-24) + + * Fixed regression in RotatingFileHandler file permissions + * Fixed initialization of the BufferHandler to make sure it gets flushed after receiving records + * Fixed ChromePHPHandler and FirePHPHandler's activation strategies to be more conservative + +### 1.9.0 (2014-04-20) + + * Added LogEntriesHandler to send logs to a LogEntries account + * Added $filePermissions to tweak file mode on StreamHandler and RotatingFileHandler + * Added $useFormatting flag to MemoryProcessor to make it send raw data in bytes + * Added support for table formatting in FirePHPHandler via the table context key + * Added a TagProcessor to add tags to records, and support for tags in RavenHandler + * Added $appendNewline flag to the JsonFormatter to enable using it when logging to files + * Added sound support to the PushoverHandler + * Fixed multi-threading support in StreamHandler + * Fixed empty headers issue when ChromePHPHandler received no records + * Fixed default format of the ErrorLogHandler + +### 1.8.0 (2014-03-23) + + * Break: the LineFormatter now strips newlines by default because this was a bug, set $allowInlineLineBreaks to true if you need them + * Added BrowserConsoleHandler to send logs to any browser's console via console.log() injection in the output + * Added FilterHandler to filter records and only allow those of a given list of levels through to the wrapped handler + * Added FlowdockHandler to send logs to a Flowdock account + * Added RollbarHandler to send logs to a Rollbar account + * Added HtmlFormatter to send prettier log emails with colors for each log level + * Added GitProcessor to add the current branch/commit to extra record data + * Added a Monolog\Registry class to allow easier global access to pre-configured loggers + * Added support for the new official graylog2/gelf-php lib for GelfHandler, upgrade if you can by replacing the mlehner/gelf-php requirement + * Added support for HHVM + * Added support for Loggly batch uploads + * Added support for tweaking the content type and encoding in NativeMailerHandler + * Added $skipClassesPartials to tweak the ignored classes in the IntrospectionProcessor + * Fixed batch request support in GelfHandler + +### 1.7.0 (2013-11-14) + + * Added ElasticSearchHandler to send logs to an Elastic Search server + * Added DynamoDbHandler and ScalarFormatter to send logs to Amazon's Dynamo DB + * Added SyslogUdpHandler to send logs to a remote syslogd server + * Added LogglyHandler to send logs to a Loggly account + * Added $level to IntrospectionProcessor so it only adds backtraces when needed + * Added $version to LogstashFormatter to allow using the new v1 Logstash format + * Added $appName to NewRelicHandler + * Added configuration of Pushover notification retries/expiry + * Added $maxColumnWidth to NativeMailerHandler to change the 70 chars default + * Added chainability to most setters for all handlers + * Fixed RavenHandler batch processing so it takes the message from the record with highest priority + * Fixed HipChatHandler batch processing so it sends all messages at once + * Fixed issues with eAccelerator + * Fixed and improved many small things + +### 1.6.0 (2013-07-29) + + * Added HipChatHandler to send logs to a HipChat chat room + * Added ErrorLogHandler to send logs to PHP's error_log function + * Added NewRelicHandler to send logs to NewRelic's service + * Added Monolog\ErrorHandler helper class to register a Logger as exception/error/fatal handler + * Added ChannelLevelActivationStrategy for the FingersCrossedHandler to customize levels by channel + * Added stack traces output when normalizing exceptions (json output & co) + * Added Monolog\Logger::API constant (currently 1) + * Added support for ChromePHP's v4.0 extension + * Added support for message priorities in PushoverHandler, see $highPriorityLevel and $emergencyLevel + * Added support for sending messages to multiple users at once with the PushoverHandler + * Fixed RavenHandler's support for batch sending of messages (when behind a Buffer or FingersCrossedHandler) + * Fixed normalization of Traversables with very large data sets, only the first 1000 items are shown now + * Fixed issue in RotatingFileHandler when an open_basedir restriction is active + * Fixed minor issues in RavenHandler and bumped the API to Raven 0.5.0 + * Fixed SyslogHandler issue when many were used concurrently with different facilities + +### 1.5.0 (2013-04-23) + + * Added ProcessIdProcessor to inject the PID in log records + * Added UidProcessor to inject a unique identifier to all log records of one request/run + * Added support for previous exceptions in the LineFormatter exception serialization + * Added Monolog\Logger::getLevels() to get all available levels + * Fixed ChromePHPHandler so it avoids sending headers larger than Chrome can handle + +### 1.4.1 (2013-04-01) + + * Fixed exception formatting in the LineFormatter to be more minimalistic + * Fixed RavenHandler's handling of context/extra data, requires Raven client >0.1.0 + * Fixed log rotation in RotatingFileHandler to work with long running scripts spanning multiple days + * Fixed WebProcessor array access so it checks for data presence + * Fixed Buffer, Group and FingersCrossed handlers to make use of their processors + +### 1.4.0 (2013-02-13) + + * Added RedisHandler to log to Redis via the Predis library or the phpredis extension + * Added ZendMonitorHandler to log to the Zend Server monitor + * Added the possibility to pass arrays of handlers and processors directly in the Logger constructor + * Added `$useSSL` option to the PushoverHandler which is enabled by default + * Fixed ChromePHPHandler and FirePHPHandler issue when multiple instances are used simultaneously + * Fixed header injection capability in the NativeMailHandler + +### 1.3.1 (2013-01-11) + + * Fixed LogstashFormatter to be usable with stream handlers + * Fixed GelfMessageFormatter levels on Windows + +### 1.3.0 (2013-01-08) + + * Added PSR-3 compliance, the `Monolog\Logger` class is now an instance of `Psr\Log\LoggerInterface` + * Added PsrLogMessageProcessor that you can selectively enable for full PSR-3 compliance + * Added LogstashFormatter (combine with SocketHandler or StreamHandler to send logs to Logstash) + * Added PushoverHandler to send mobile notifications + * Added CouchDBHandler and DoctrineCouchDBHandler + * Added RavenHandler to send data to Sentry servers + * Added support for the new MongoClient class in MongoDBHandler + * Added microsecond precision to log records' timestamps + * Added `$flushOnOverflow` param to BufferHandler to flush by batches instead of losing + the oldest entries + * Fixed normalization of objects with cyclic references + +### 1.2.1 (2012-08-29) + + * Added new $logopts arg to SyslogHandler to provide custom openlog options + * Fixed fatal error in SyslogHandler + +### 1.2.0 (2012-08-18) + + * Added AmqpHandler (for use with AMQP servers) + * Added CubeHandler + * Added NativeMailerHandler::addHeader() to send custom headers in mails + * Added the possibility to specify more than one recipient in NativeMailerHandler + * Added the possibility to specify float timeouts in SocketHandler + * Added NOTICE and EMERGENCY levels to conform with RFC 5424 + * Fixed the log records to use the php default timezone instead of UTC + * Fixed BufferHandler not being flushed properly on PHP fatal errors + * Fixed normalization of exotic resource types + * Fixed the default format of the SyslogHandler to avoid duplicating datetimes in syslog + +### 1.1.0 (2012-04-23) + + * Added Monolog\Logger::isHandling() to check if a handler will + handle the given log level + * Added ChromePHPHandler + * Added MongoDBHandler + * Added GelfHandler (for use with Graylog2 servers) + * Added SocketHandler (for use with syslog-ng for example) + * Added NormalizerFormatter + * Added the possibility to change the activation strategy of the FingersCrossedHandler + * Added possibility to show microseconds in logs + * Added `server` and `referer` to WebProcessor output + +### 1.0.2 (2011-10-24) + + * Fixed bug in IE with large response headers and FirePHPHandler + +### 1.0.1 (2011-08-25) + + * Added MemoryPeakUsageProcessor and MemoryUsageProcessor + * Added Monolog\Logger::getName() to get a logger's channel name + +### 1.0.0 (2011-07-06) + + * Added IntrospectionProcessor to get info from where the logger was called + * Fixed WebProcessor in CLI + +### 1.0.0-RC1 (2011-07-01) + + * Initial release diff --git a/msd/vendor/monolog/monolog/LICENSE b/msd/vendor/monolog/monolog/LICENSE new file mode 100644 index 0000000..56c1aaf --- /dev/null +++ b/msd/vendor/monolog/monolog/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011-2020 Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/msd/vendor/monolog/monolog/README.md b/msd/vendor/monolog/monolog/README.md new file mode 100644 index 0000000..bfcae0c --- /dev/null +++ b/msd/vendor/monolog/monolog/README.md @@ -0,0 +1,112 @@ +# Monolog - Logging for PHP [![Continuous Integration](https://github.com/Seldaek/monolog/workflows/Continuous%20Integration/badge.svg?branch=main)](https://github.com/Seldaek/monolog/actions) + +[![Total Downloads](https://img.shields.io/packagist/dt/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) +[![Latest Stable Version](https://img.shields.io/packagist/v/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) + + +Monolog sends your logs to files, sockets, inboxes, databases and various +web services. See the complete list of handlers below. Special handlers +allow you to build advanced logging strategies. + +This library implements the [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) +interface that you can type-hint against in your own libraries to keep +a maximum of interoperability. You can also use it in your applications to +make sure you can always use another compatible logger at a later time. +As of 1.11.0 Monolog public APIs will also accept PSR-3 log levels. +Internally Monolog still uses its own level scheme since it predates PSR-3. + +## Installation + +Install the latest version with + +```bash +$ composer require monolog/monolog +``` + +## Basic Usage + +```php +pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING)); + +// add records to the log +$log->warning('Foo'); +$log->error('Bar'); +``` + +## Documentation + +- [Usage Instructions](doc/01-usage.md) +- [Handlers, Formatters and Processors](doc/02-handlers-formatters-processors.md) +- [Utility Classes](doc/03-utilities.md) +- [Extending Monolog](doc/04-extending.md) +- [Log Record Structure](doc/message-structure.md) + +## Support Monolog Financially + +Get supported Monolog and help fund the project with the [Tidelift Subscription](https://tidelift.com/subscription/pkg/packagist-monolog-monolog?utm_source=packagist-monolog-monolog&utm_medium=referral&utm_campaign=enterprise) or via [GitHub sponsorship](https://github.com/sponsors/Seldaek). + +Tidelift delivers commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. + +## Third Party Packages + +Third party handlers, formatters and processors are +[listed in the wiki](https://github.com/Seldaek/monolog/wiki/Third-Party-Packages). You +can also add your own there if you publish one. + +## About + +### Requirements + +- Monolog `^2.0` works with PHP 7.2 or above, use Monolog `^1.25` for PHP 5.3+ support. + +### Support + +Monolog 1.x support is somewhat limited at this point and only important fixes will be done. You should migrate to Monolog 2 where possible to benefit from all the latest features and fixes. + +### Submitting bugs and feature requests + +Bugs and feature request are tracked on [GitHub](https://github.com/Seldaek/monolog/issues) + +### Framework Integrations + +- Frameworks and libraries using [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) + can be used very easily with Monolog since it implements the interface. +- [Symfony](http://symfony.com) comes out of the box with Monolog. +- [Laravel](http://laravel.com/) comes out of the box with Monolog. +- [Lumen](http://lumen.laravel.com/) comes out of the box with Monolog. +- [PPI](https://github.com/ppi/framework) comes out of the box with Monolog. +- [CakePHP](http://cakephp.org/) is usable with Monolog via the [cakephp-monolog](https://github.com/jadb/cakephp-monolog) plugin. +- [Slim](http://www.slimframework.com/) is usable with Monolog via the [Slim-Monolog](https://github.com/Flynsarmy/Slim-Monolog) log writer. +- [XOOPS 2.6](http://xoops.org/) comes out of the box with Monolog. +- [Aura.Web_Project](https://github.com/auraphp/Aura.Web_Project) comes out of the box with Monolog. +- [Nette Framework](http://nette.org/en/) is usable with Monolog via the [contributte/monolog](https://github.com/contributte/monolog) or [orisai/nette-monolog](https://github.com/orisai/nette-monolog) extensions. +- [Proton Micro Framework](https://github.com/alexbilbie/Proton) comes out of the box with Monolog. +- [FuelPHP](http://fuelphp.com/) comes out of the box with Monolog. +- [Equip Framework](https://github.com/equip/framework) comes out of the box with Monolog. +- [Yii 2](http://www.yiiframework.com/) is usable with Monolog via the [yii2-monolog](https://github.com/merorafael/yii2-monolog) or [yii2-psr-log-target](https://github.com/samdark/yii2-psr-log-target) plugins. +- [Hawkbit Micro Framework](https://github.com/HawkBitPhp/hawkbit) comes out of the box with Monolog. +- [SilverStripe 4](https://www.silverstripe.org/) comes out of the box with Monolog. +- [Drupal](https://www.drupal.org/) is usable with Monolog via the [monolog](https://www.drupal.org/project/monolog) module. +- [Aimeos ecommerce framework](https://aimeos.org/) is usable with Monolog via the [ai-monolog](https://github.com/aimeos/ai-monolog) extension. +- [Magento](https://magento.com/) comes out of the box with Monolog. + +### Author + +Jordi Boggiano - -
      +See also the list of [contributors](https://github.com/Seldaek/monolog/contributors) who participated in this project. + +### License + +Monolog is licensed under the MIT License - see the [LICENSE](LICENSE) file for details + +### Acknowledgements + +This library is heavily inspired by Python's [Logbook](https://logbook.readthedocs.io/en/stable/) +library, although most concepts have been adjusted to fit to the PHP world. diff --git a/msd/vendor/monolog/monolog/UPGRADE.md b/msd/vendor/monolog/monolog/UPGRADE.md new file mode 100644 index 0000000..84e15e6 --- /dev/null +++ b/msd/vendor/monolog/monolog/UPGRADE.md @@ -0,0 +1,72 @@ +### 2.0.0 + +- `Monolog\Logger::API` can be used to distinguish between a Monolog `1` and `2` + install of Monolog when writing integration code. + +- Removed non-PSR-3 methods to add records, all the `add*` (e.g. `addWarning`) + methods as well as `emerg`, `crit`, `err` and `warn`. + +- DateTime are now formatted with a timezone and microseconds (unless disabled). + Various formatters and log output might be affected, which may mess with log parsing + in some cases. + +- The `datetime` in every record array is now a DateTimeImmutable, not that you + should have been modifying these anyway. + +- The timezone is now set per Logger instance and not statically, either + via ->setTimezone or passed in the constructor. Calls to Logger::setTimezone + should be converted. + +- `HandlerInterface` has been split off and two new interfaces now exist for + more granular controls: `ProcessableHandlerInterface` and + `FormattableHandlerInterface`. Handlers not extending `AbstractHandler` + should make sure to implement the relevant interfaces. + +- `HandlerInterface` now requires the `close` method to be implemented. This + only impacts you if you implement the interface yourself, but you can extend + the new `Monolog\Handler\Handler` base class too. + +- There is no more default handler configured on empty Logger instances, if + you were relying on that you will not get any output anymore, make sure to + configure the handler you need. + +#### LogglyFormatter + +- The records' `datetime` is not sent anymore. Only `timestamp` is sent to Loggly. + +#### AmqpHandler + +- Log levels are not shortened to 4 characters anymore. e.g. a warning record + will be sent using the `warning.channel` routing key instead of `warn.channel` + as in 1.x. +- The exchange name does not default to 'log' anymore, and it is completely ignored + now for the AMQP extension users. Only PHPAmqpLib uses it if provided. + +#### RotatingFileHandler + +- The file name format must now contain `{date}` and the date format must be set + to one of the predefined FILE_PER_* constants to avoid issues with file rotation. + See `setFilenameFormat`. + +#### LogstashFormatter + +- Removed Logstash V0 support +- Context/extra prefix has been removed in favor of letting users configure the exact key being sent +- Context/extra data are now sent as an object instead of single keys + +#### HipChatHandler + +- Removed deprecated HipChat handler, migrate to Slack and use SlackWebhookHandler or SlackHandler instead + +#### SlackbotHandler + +- Removed deprecated SlackbotHandler handler, use SlackWebhookHandler or SlackHandler instead + +#### RavenHandler + +- Removed deprecated RavenHandler handler, use sentry/sentry 2.x and their Sentry\Monolog\Handler instead + +#### ElasticSearchHandler + +- As support for the official Elasticsearch library was added, the former ElasticSearchHandler has been + renamed to ElasticaHandler and the new one added as ElasticsearchHandler. diff --git a/msd/vendor/monolog/monolog/composer.json b/msd/vendor/monolog/monolog/composer.json new file mode 100644 index 0000000..ab775ad --- /dev/null +++ b/msd/vendor/monolog/monolog/composer.json @@ -0,0 +1,81 @@ +{ + "name": "monolog/monolog", + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "keywords": ["log", "logging", "psr-3"], + "homepage": "https://github.com/Seldaek/monolog", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "require": { + "php": ">=7.2", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "ext-json": "*", + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "graylog2/gelf-php": "^1.4.2", + "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpspec/prophecy": "^1.15", + "phpstan/phpstan": "^0.12.91", + "phpunit/phpunit": "^8.5.14", + "predis/predis": "^1.1 || ^2.0", + "rollbar/rollbar": "^1.3 || ^2 || ^3", + "ruflin/elastica": "^7", + "swiftmailer/swiftmailer": "^5.3|^6.0", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-openssl": "Required to send log messages using SSL" + }, + "autoload": { + "psr-4": {"Monolog\\": "src/Monolog"} + }, + "autoload-dev": { + "psr-4": {"Monolog\\": "tests/Monolog"} + }, + "provide": { + "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" + }, + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "scripts": { + "test": "@php vendor/bin/phpunit", + "phpstan": "@php vendor/bin/phpstan analyse" + }, + "config": { + "lock": false, + "sort-packages": true, + "platform-check": false, + "allow-plugins": { + "composer/package-versions-deprecated": true + } + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Attribute/AsMonologProcessor.php b/msd/vendor/monolog/monolog/src/Monolog/Attribute/AsMonologProcessor.php new file mode 100644 index 0000000..facf7be --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Attribute/AsMonologProcessor.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Attribute; + +/** + * A reusable attribute to help configure a class or a method as a processor. + * + * Using it offers no guarantee: it needs to be leveraged by a Monolog third-party consumer. + * + * Using it with the Monolog library only has no effect at all: processors should still be turned into a callable if + * needed and manually pushed to the loggers and to the processable handlers. + */ +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +class AsMonologProcessor +{ + /** @var string|null */ + public $channel = null; + /** @var string|null */ + public $handler = null; + /** @var string|null */ + public $method = null; + + /** + * @param string|null $channel The logging channel the processor should be pushed to. + * @param string|null $handler The handler the processor should be pushed to. + * @param string|null $method The method that processes the records (if the attribute is used at the class level). + */ + public function __construct( + ?string $channel = null, + ?string $handler = null, + ?string $method = null + ) { + $this->channel = $channel; + $this->handler = $handler; + $this->method = $method; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/DateTimeImmutable.php b/msd/vendor/monolog/monolog/src/Monolog/DateTimeImmutable.php new file mode 100644 index 0000000..12bfff2 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/DateTimeImmutable.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use DateTimeZone; + +/** + * Overrides default json encoding of date time objects + * + * @author Menno Holtkamp + * @author Jordi Boggiano + */ +class DateTimeImmutable extends \DateTimeImmutable implements \JsonSerializable +{ + /** + * @var bool + */ + private $useMicroseconds; + + public function __construct(bool $useMicroseconds, ?DateTimeZone $timezone = null) + { + $this->useMicroseconds = $useMicroseconds; + + parent::__construct('now', $timezone); + } + + public function jsonSerialize(): string + { + if ($this->useMicroseconds) { + return $this->format('Y-m-d\TH:i:s.uP'); + } + + return $this->format('Y-m-d\TH:i:sP'); + } + + public function __toString(): string + { + return $this->jsonSerialize(); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/ErrorHandler.php b/msd/vendor/monolog/monolog/src/Monolog/ErrorHandler.php new file mode 100644 index 0000000..9a7a6f2 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/ErrorHandler.php @@ -0,0 +1,307 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; + +/** + * Monolog error handler + * + * A facility to enable logging of runtime errors, exceptions and fatal errors. + * + * Quick setup: ErrorHandler::register($logger); + * + * @author Jordi Boggiano + */ +class ErrorHandler +{ + /** @var LoggerInterface */ + private $logger; + + /** @var ?callable */ + private $previousExceptionHandler = null; + /** @var array an array of class name to LogLevel::* constant mapping */ + private $uncaughtExceptionLevelMap = []; + + /** @var callable|true|null */ + private $previousErrorHandler = null; + /** @var array an array of E_* constant to LogLevel::* constant mapping */ + private $errorLevelMap = []; + /** @var bool */ + private $handleOnlyReportedErrors = true; + + /** @var bool */ + private $hasFatalErrorHandler = false; + /** @var LogLevel::* */ + private $fatalLevel = LogLevel::ALERT; + /** @var ?string */ + private $reservedMemory = null; + /** @var ?array{type: int, message: string, file: string, line: int, trace: mixed} */ + private $lastFatalData = null; + /** @var int[] */ + private static $fatalErrors = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR]; + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * Registers a new ErrorHandler for a given Logger + * + * By default it will handle errors, exceptions and fatal errors + * + * @param LoggerInterface $logger + * @param array|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling + * @param array|false $exceptionLevelMap an array of class name to LogLevel::* constant mapping, or false to disable exception handling + * @param LogLevel::*|null|false $fatalLevel a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling + * @return ErrorHandler + */ + public static function register(LoggerInterface $logger, $errorLevelMap = [], $exceptionLevelMap = [], $fatalLevel = null): self + { + /** @phpstan-ignore-next-line */ + $handler = new static($logger); + if ($errorLevelMap !== false) { + $handler->registerErrorHandler($errorLevelMap); + } + if ($exceptionLevelMap !== false) { + $handler->registerExceptionHandler($exceptionLevelMap); + } + if ($fatalLevel !== false) { + $handler->registerFatalHandler($fatalLevel); + } + + return $handler; + } + + /** + * @param array $levelMap an array of class name to LogLevel::* constant mapping + * @return $this + */ + public function registerExceptionHandler(array $levelMap = [], bool $callPrevious = true): self + { + $prev = set_exception_handler(function (\Throwable $e): void { + $this->handleException($e); + }); + $this->uncaughtExceptionLevelMap = $levelMap; + foreach ($this->defaultExceptionLevelMap() as $class => $level) { + if (!isset($this->uncaughtExceptionLevelMap[$class])) { + $this->uncaughtExceptionLevelMap[$class] = $level; + } + } + if ($callPrevious && $prev) { + $this->previousExceptionHandler = $prev; + } + + return $this; + } + + /** + * @param array $levelMap an array of E_* constant to LogLevel::* constant mapping + * @return $this + */ + public function registerErrorHandler(array $levelMap = [], bool $callPrevious = true, int $errorTypes = -1, bool $handleOnlyReportedErrors = true): self + { + $prev = set_error_handler([$this, 'handleError'], $errorTypes); + $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap); + if ($callPrevious) { + $this->previousErrorHandler = $prev ?: true; + } else { + $this->previousErrorHandler = null; + } + + $this->handleOnlyReportedErrors = $handleOnlyReportedErrors; + + return $this; + } + + /** + * @param LogLevel::*|null $level a LogLevel::* constant, null to use the default LogLevel::ALERT + * @param int $reservedMemorySize Amount of KBs to reserve in memory so that it can be freed when handling fatal errors giving Monolog some room in memory to get its job done + */ + public function registerFatalHandler($level = null, int $reservedMemorySize = 20): self + { + register_shutdown_function([$this, 'handleFatalError']); + + $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize); + $this->fatalLevel = null === $level ? LogLevel::ALERT : $level; + $this->hasFatalErrorHandler = true; + + return $this; + } + + /** + * @return array + */ + protected function defaultExceptionLevelMap(): array + { + return [ + 'ParseError' => LogLevel::CRITICAL, + 'Throwable' => LogLevel::ERROR, + ]; + } + + /** + * @return array + */ + protected function defaultErrorLevelMap(): array + { + return [ + E_ERROR => LogLevel::CRITICAL, + E_WARNING => LogLevel::WARNING, + E_PARSE => LogLevel::ALERT, + E_NOTICE => LogLevel::NOTICE, + E_CORE_ERROR => LogLevel::CRITICAL, + E_CORE_WARNING => LogLevel::WARNING, + E_COMPILE_ERROR => LogLevel::ALERT, + E_COMPILE_WARNING => LogLevel::WARNING, + E_USER_ERROR => LogLevel::ERROR, + E_USER_WARNING => LogLevel::WARNING, + E_USER_NOTICE => LogLevel::NOTICE, + E_STRICT => LogLevel::NOTICE, + E_RECOVERABLE_ERROR => LogLevel::ERROR, + E_DEPRECATED => LogLevel::NOTICE, + E_USER_DEPRECATED => LogLevel::NOTICE, + ]; + } + + /** + * @phpstan-return never + */ + private function handleException(\Throwable $e): void + { + $level = LogLevel::ERROR; + foreach ($this->uncaughtExceptionLevelMap as $class => $candidate) { + if ($e instanceof $class) { + $level = $candidate; + break; + } + } + + $this->logger->log( + $level, + sprintf('Uncaught Exception %s: "%s" at %s line %s', Utils::getClass($e), $e->getMessage(), $e->getFile(), $e->getLine()), + ['exception' => $e] + ); + + if ($this->previousExceptionHandler) { + ($this->previousExceptionHandler)($e); + } + + if (!headers_sent() && !ini_get('display_errors')) { + http_response_code(500); + } + + exit(255); + } + + /** + * @private + * + * @param mixed[] $context + */ + public function handleError(int $code, string $message, string $file = '', int $line = 0, ?array $context = []): bool + { + if ($this->handleOnlyReportedErrors && !(error_reporting() & $code)) { + return false; + } + + // fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries + if (!$this->hasFatalErrorHandler || !in_array($code, self::$fatalErrors, true)) { + $level = $this->errorLevelMap[$code] ?? LogLevel::CRITICAL; + $this->logger->log($level, self::codeToString($code).': '.$message, ['code' => $code, 'message' => $message, 'file' => $file, 'line' => $line]); + } else { + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + array_shift($trace); // Exclude handleError from trace + $this->lastFatalData = ['type' => $code, 'message' => $message, 'file' => $file, 'line' => $line, 'trace' => $trace]; + } + + if ($this->previousErrorHandler === true) { + return false; + } elseif ($this->previousErrorHandler) { + return (bool) ($this->previousErrorHandler)($code, $message, $file, $line, $context); + } + + return true; + } + + /** + * @private + */ + public function handleFatalError(): void + { + $this->reservedMemory = ''; + + if (is_array($this->lastFatalData)) { + $lastError = $this->lastFatalData; + } else { + $lastError = error_get_last(); + } + + if ($lastError && in_array($lastError['type'], self::$fatalErrors, true)) { + $trace = $lastError['trace'] ?? null; + $this->logger->log( + $this->fatalLevel, + 'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'], + ['code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'], 'trace' => $trace] + ); + + if ($this->logger instanceof Logger) { + foreach ($this->logger->getHandlers() as $handler) { + $handler->close(); + } + } + } + } + + /** + * @param int $code + */ + private static function codeToString($code): string + { + switch ($code) { + case E_ERROR: + return 'E_ERROR'; + case E_WARNING: + return 'E_WARNING'; + case E_PARSE: + return 'E_PARSE'; + case E_NOTICE: + return 'E_NOTICE'; + case E_CORE_ERROR: + return 'E_CORE_ERROR'; + case E_CORE_WARNING: + return 'E_CORE_WARNING'; + case E_COMPILE_ERROR: + return 'E_COMPILE_ERROR'; + case E_COMPILE_WARNING: + return 'E_COMPILE_WARNING'; + case E_USER_ERROR: + return 'E_USER_ERROR'; + case E_USER_WARNING: + return 'E_USER_WARNING'; + case E_USER_NOTICE: + return 'E_USER_NOTICE'; + case E_STRICT: + return 'E_STRICT'; + case E_RECOVERABLE_ERROR: + return 'E_RECOVERABLE_ERROR'; + case E_DEPRECATED: + return 'E_DEPRECATED'; + case E_USER_DEPRECATED: + return 'E_USER_DEPRECATED'; + } + + return 'Unknown PHP error'; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php new file mode 100644 index 0000000..0a8f0e4 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Formats a log message according to the ChromePHP array format + * + * @author Christophe Coevoet + */ +class ChromePHPFormatter implements FormatterInterface +{ + /** + * Translates Monolog log levels to Wildfire levels. + * + * @var array + */ + private $logLevels = [ + Logger::DEBUG => 'log', + Logger::INFO => 'info', + Logger::NOTICE => 'info', + Logger::WARNING => 'warn', + Logger::ERROR => 'error', + Logger::CRITICAL => 'error', + Logger::ALERT => 'error', + Logger::EMERGENCY => 'error', + ]; + + /** + * {@inheritDoc} + */ + public function format(array $record) + { + // Retrieve the line and file if set and remove them from the formatted extra + $backtrace = 'unknown'; + if (isset($record['extra']['file'], $record['extra']['line'])) { + $backtrace = $record['extra']['file'].' : '.$record['extra']['line']; + unset($record['extra']['file'], $record['extra']['line']); + } + + $message = ['message' => $record['message']]; + if ($record['context']) { + $message['context'] = $record['context']; + } + if ($record['extra']) { + $message['extra'] = $record['extra']; + } + if (count($message) === 1) { + $message = reset($message); + } + + return [ + $record['channel'], + $message, + $backtrace, + $this->logLevels[$record['level']], + ]; + } + + /** + * {@inheritDoc} + */ + public function formatBatch(array $records) + { + $formatted = []; + + foreach ($records as $record) { + $formatted[] = $this->format($record); + } + + return $formatted; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php new file mode 100644 index 0000000..c5741e7 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Elastica\Document; + +/** + * Format a log message into an Elastica Document + * + * @author Jelle Vink + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class ElasticaFormatter extends NormalizerFormatter +{ + /** + * @var string Elastic search index name + */ + protected $index; + + /** + * @var ?string Elastic search document type + */ + protected $type; + + /** + * @param string $index Elastic Search index name + * @param ?string $type Elastic Search document type, deprecated as of Elastica 7 + */ + public function __construct(string $index, ?string $type) + { + // elasticsearch requires a ISO 8601 format date with optional millisecond precision. + parent::__construct('Y-m-d\TH:i:s.uP'); + + $this->index = $index; + $this->type = $type; + } + + /** + * {@inheritDoc} + */ + public function format(array $record) + { + $record = parent::format($record); + + return $this->getDocument($record); + } + + public function getIndex(): string + { + return $this->index; + } + + /** + * @deprecated since Elastica 7 type has no effect + */ + public function getType(): string + { + /** @phpstan-ignore-next-line */ + return $this->type; + } + + /** + * Convert a log message into an Elastica Document + * + * @phpstan-param Record $record + */ + protected function getDocument(array $record): Document + { + $document = new Document(); + $document->setData($record); + if (method_exists($document, 'setType')) { + /** @phpstan-ignore-next-line */ + $document->setType($this->type); + } + $document->setIndex($this->index); + + return $document; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/ElasticsearchFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/ElasticsearchFormatter.php new file mode 100644 index 0000000..e836c4b --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/ElasticsearchFormatter.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use DateTimeInterface; + +/** + * Format a log message into an Elasticsearch record + * + * @author Avtandil Kikabidze + */ +class ElasticsearchFormatter extends NormalizerFormatter +{ + /** + * @var string Elasticsearch index name + */ + protected $index; + + /** + * @var string Elasticsearch record type + */ + protected $type; + + /** + * @param string $index Elasticsearch index name + * @param string $type Elasticsearch record type + */ + public function __construct(string $index, string $type) + { + // Elasticsearch requires an ISO 8601 format date with optional millisecond precision. + parent::__construct(DateTimeInterface::ISO8601); + + $this->index = $index; + $this->type = $type; + } + + /** + * {@inheritDoc} + */ + public function format(array $record) + { + $record = parent::format($record); + + return $this->getDocument($record); + } + + /** + * Getter index + * + * @return string + */ + public function getIndex(): string + { + return $this->index; + } + + /** + * Getter type + * + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * Convert a log message into an Elasticsearch record + * + * @param mixed[] $record Log message + * @return mixed[] + */ + protected function getDocument(array $record): array + { + $record['_index'] = $this->index; + $record['_type'] = $this->type; + + return $record; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php new file mode 100644 index 0000000..076dff4 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * formats the record to be used in the FlowdockHandler + * + * @author Dominik Liebler + */ +class FlowdockFormatter implements FormatterInterface +{ + /** + * @var string + */ + private $source; + + /** + * @var string + */ + private $sourceEmail; + + public function __construct(string $source, string $sourceEmail) + { + $this->source = $source; + $this->sourceEmail = $sourceEmail; + } + + /** + * {@inheritDoc} + * + * @return mixed[] + */ + public function format(array $record): array + { + $tags = [ + '#logs', + '#' . strtolower($record['level_name']), + '#' . $record['channel'], + ]; + + foreach ($record['extra'] as $value) { + $tags[] = '#' . $value; + } + + $subject = sprintf( + 'in %s: %s - %s', + $this->source, + $record['level_name'], + $this->getShortMessage($record['message']) + ); + + $record['flowdock'] = [ + 'source' => $this->source, + 'from_address' => $this->sourceEmail, + 'subject' => $subject, + 'content' => $record['message'], + 'tags' => $tags, + 'project' => $this->source, + ]; + + return $record; + } + + /** + * {@inheritDoc} + * + * @return mixed[][] + */ + public function formatBatch(array $records): array + { + $formatted = []; + + foreach ($records as $record) { + $formatted[] = $this->format($record); + } + + return $formatted; + } + + public function getShortMessage(string $message): string + { + static $hasMbString; + + if (null === $hasMbString) { + $hasMbString = function_exists('mb_strlen'); + } + + $maxLength = 45; + + if ($hasMbString) { + if (mb_strlen($message, 'UTF-8') > $maxLength) { + $message = mb_substr($message, 0, $maxLength - 4, 'UTF-8') . ' ...'; + } + } else { + if (strlen($message) > $maxLength) { + $message = substr($message, 0, $maxLength - 4) . ' ...'; + } + } + + return $message; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php new file mode 100644 index 0000000..efe4fd1 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Utils; + +/** + * Class FluentdFormatter + * + * Serializes a log message to Fluentd unix socket protocol + * + * Fluentd config: + * + * + * type unix + * path /var/run/td-agent/td-agent.sock + * + * + * Monolog setup: + * + * $logger = new Monolog\Logger('fluent.tag'); + * $fluentHandler = new Monolog\Handler\SocketHandler('unix:///var/run/td-agent/td-agent.sock'); + * $fluentHandler->setFormatter(new Monolog\Formatter\FluentdFormatter()); + * $logger->pushHandler($fluentHandler); + * + * @author Andrius Putna + */ +class FluentdFormatter implements FormatterInterface +{ + /** + * @var bool $levelTag should message level be a part of the fluentd tag + */ + protected $levelTag = false; + + public function __construct(bool $levelTag = false) + { + if (!function_exists('json_encode')) { + throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s FluentdUnixFormatter'); + } + + $this->levelTag = $levelTag; + } + + public function isUsingLevelsInTag(): bool + { + return $this->levelTag; + } + + public function format(array $record): string + { + $tag = $record['channel']; + if ($this->levelTag) { + $tag .= '.' . strtolower($record['level_name']); + } + + $message = [ + 'message' => $record['message'], + 'context' => $record['context'], + 'extra' => $record['extra'], + ]; + + if (!$this->levelTag) { + $message['level'] = $record['level']; + $message['level_name'] = $record['level_name']; + } + + return Utils::jsonEncode([$tag, $record['datetime']->getTimestamp(), $message]); + } + + public function formatBatch(array $records): string + { + $message = ''; + foreach ($records as $record) { + $message .= $this->format($record); + } + + return $message; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php new file mode 100644 index 0000000..5eb5afc --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Interface for formatters + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +interface FormatterInterface +{ + /** + * Formats a log record. + * + * @param array $record A record to format + * @return mixed The formatted record + * + * @phpstan-param Record $record + */ + public function format(array $record); + + /** + * Formats a set of log records. + * + * @param array $records A set of records to format + * @return mixed The formatted set of records + * + * @phpstan-param Record[] $records + */ + public function formatBatch(array $records); +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php new file mode 100644 index 0000000..2cd584d --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; +use Gelf\Message; +use Monolog\Utils; + +/** + * Serializes a log message to GELF + * @see http://docs.graylog.org/en/latest/pages/gelf.html + * + * @author Matt Lehner + * + * @phpstan-import-type Level from \Monolog\Logger + */ +class GelfMessageFormatter extends NormalizerFormatter +{ + protected const DEFAULT_MAX_LENGTH = 32766; + + /** + * @var string the name of the system for the Gelf log message + */ + protected $systemName; + + /** + * @var string a prefix for 'extra' fields from the Monolog record (optional) + */ + protected $extraPrefix; + + /** + * @var string a prefix for 'context' fields from the Monolog record (optional) + */ + protected $contextPrefix; + + /** + * @var int max length per field + */ + protected $maxLength; + + /** + * Translates Monolog log levels to Graylog2 log priorities. + * + * @var array + * + * @phpstan-var array + */ + private $logLevels = [ + Logger::DEBUG => 7, + Logger::INFO => 6, + Logger::NOTICE => 5, + Logger::WARNING => 4, + Logger::ERROR => 3, + Logger::CRITICAL => 2, + Logger::ALERT => 1, + Logger::EMERGENCY => 0, + ]; + + public function __construct(?string $systemName = null, ?string $extraPrefix = null, string $contextPrefix = 'ctxt_', ?int $maxLength = null) + { + if (!class_exists(Message::class)) { + throw new \RuntimeException('Composer package graylog2/gelf-php is required to use Monolog\'s GelfMessageFormatter'); + } + + parent::__construct('U.u'); + + $this->systemName = (is_null($systemName) || $systemName === '') ? (string) gethostname() : $systemName; + + $this->extraPrefix = is_null($extraPrefix) ? '' : $extraPrefix; + $this->contextPrefix = $contextPrefix; + $this->maxLength = is_null($maxLength) ? self::DEFAULT_MAX_LENGTH : $maxLength; + } + + /** + * {@inheritDoc} + */ + public function format(array $record): Message + { + $context = $extra = []; + if (isset($record['context'])) { + /** @var mixed[] $context */ + $context = parent::normalize($record['context']); + } + if (isset($record['extra'])) { + /** @var mixed[] $extra */ + $extra = parent::normalize($record['extra']); + } + + if (!isset($record['datetime'], $record['message'], $record['level'])) { + throw new \InvalidArgumentException('The record should at least contain datetime, message and level keys, '.var_export($record, true).' given'); + } + + $message = new Message(); + $message + ->setTimestamp($record['datetime']) + ->setShortMessage((string) $record['message']) + ->setHost($this->systemName) + ->setLevel($this->logLevels[$record['level']]); + + // message length + system name length + 200 for padding / metadata + $len = 200 + strlen((string) $record['message']) + strlen($this->systemName); + + if ($len > $this->maxLength) { + $message->setShortMessage(Utils::substr($record['message'], 0, $this->maxLength)); + } + + if (isset($record['channel'])) { + $message->setFacility($record['channel']); + } + if (isset($extra['line'])) { + $message->setLine($extra['line']); + unset($extra['line']); + } + if (isset($extra['file'])) { + $message->setFile($extra['file']); + unset($extra['file']); + } + + foreach ($extra as $key => $val) { + $val = is_scalar($val) || null === $val ? $val : $this->toJson($val); + $len = strlen($this->extraPrefix . $key . $val); + if ($len > $this->maxLength) { + $message->setAdditional($this->extraPrefix . $key, Utils::substr((string) $val, 0, $this->maxLength)); + + continue; + } + $message->setAdditional($this->extraPrefix . $key, $val); + } + + foreach ($context as $key => $val) { + $val = is_scalar($val) || null === $val ? $val : $this->toJson($val); + $len = strlen($this->contextPrefix . $key . $val); + if ($len > $this->maxLength) { + $message->setAdditional($this->contextPrefix . $key, Utils::substr((string) $val, 0, $this->maxLength)); + + continue; + } + $message->setAdditional($this->contextPrefix . $key, $val); + } + + /** @phpstan-ignore-next-line */ + if (null === $message->getFile() && isset($context['exception']['file'])) { + if (preg_match("/^(.+):([0-9]+)$/", $context['exception']['file'], $matches)) { + $message->setFile($matches[1]); + $message->setLine($matches[2]); + } + } + + return $message; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php new file mode 100644 index 0000000..b6c0040 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use DateTimeInterface; +use Monolog\LogRecord; + +/** + * Encodes message information into JSON in a format compatible with Cloud logging. + * + * @see https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry + * + * @author Luís Cobucci + */ +final class GoogleCloudLoggingFormatter extends JsonFormatter +{ + /** {@inheritdoc} **/ + public function format(array $record): string + { + // Re-key level for GCP logging + $record['severity'] = $record['level_name']; + $record['timestamp'] = $record['datetime']->format(DateTimeInterface::RFC3339_EXTENDED); + + // Remove keys that are not used by GCP + unset($record['level'], $record['level_name'], $record['datetime']); + + return parent::format($record); + } +} + diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php new file mode 100644 index 0000000..9b006ca --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; +use Monolog\Utils; + +/** + * Formats incoming records into an HTML table + * + * This is especially useful for html email logging + * + * @author Tiago Brito + */ +class HtmlFormatter extends NormalizerFormatter +{ + /** + * Translates Monolog log levels to html color priorities. + * + * @var array + */ + protected $logLevels = [ + Logger::DEBUG => '#CCCCCC', + Logger::INFO => '#28A745', + Logger::NOTICE => '#17A2B8', + Logger::WARNING => '#FFC107', + Logger::ERROR => '#FD7E14', + Logger::CRITICAL => '#DC3545', + Logger::ALERT => '#821722', + Logger::EMERGENCY => '#000000', + ]; + + /** + * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct(?string $dateFormat = null) + { + parent::__construct($dateFormat); + } + + /** + * Creates an HTML table row + * + * @param string $th Row header content + * @param string $td Row standard cell content + * @param bool $escapeTd false if td content must not be html escaped + */ + protected function addRow(string $th, string $td = ' ', bool $escapeTd = true): string + { + $th = htmlspecialchars($th, ENT_NOQUOTES, 'UTF-8'); + if ($escapeTd) { + $td = '
      '.htmlspecialchars($td, ENT_NOQUOTES, 'UTF-8').'
      '; + } + + return "\n$th:\n".$td."\n"; + } + + /** + * Create a HTML h1 tag + * + * @param string $title Text to be in the h1 + * @param int $level Error level + * @return string + */ + protected function addTitle(string $title, int $level): string + { + $title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8'); + + return '

      '.$title.'

      '; + } + + /** + * Formats a log record. + * + * @return string The formatted record + */ + public function format(array $record): string + { + $output = $this->addTitle($record['level_name'], $record['level']); + $output .= ''; + + $output .= $this->addRow('Message', (string) $record['message']); + $output .= $this->addRow('Time', $this->formatDate($record['datetime'])); + $output .= $this->addRow('Channel', $record['channel']); + if ($record['context']) { + $embeddedTable = '
      '; + foreach ($record['context'] as $key => $value) { + $embeddedTable .= $this->addRow((string) $key, $this->convertToString($value)); + } + $embeddedTable .= '
      '; + $output .= $this->addRow('Context', $embeddedTable, false); + } + if ($record['extra']) { + $embeddedTable = ''; + foreach ($record['extra'] as $key => $value) { + $embeddedTable .= $this->addRow((string) $key, $this->convertToString($value)); + } + $embeddedTable .= '
      '; + $output .= $this->addRow('Extra', $embeddedTable, false); + } + + return $output.''; + } + + /** + * Formats a set of log records. + * + * @return string The formatted set of records + */ + public function formatBatch(array $records): string + { + $message = ''; + foreach ($records as $record) { + $message .= $this->format($record); + } + + return $message; + } + + /** + * @param mixed $data + */ + protected function convertToString($data): string + { + if (null === $data || is_scalar($data)) { + return (string) $data; + } + + $data = $this->normalize($data); + + return Utils::jsonEncode($data, JSON_PRETTY_PRINT | Utils::DEFAULT_JSON_FLAGS, true); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php new file mode 100644 index 0000000..db71818 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php @@ -0,0 +1,224 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Throwable; + +/** + * Encodes whatever record data is passed to it as json + * + * This can be useful to log to databases or remote APIs + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class JsonFormatter extends NormalizerFormatter +{ + public const BATCH_MODE_JSON = 1; + public const BATCH_MODE_NEWLINES = 2; + + /** @var self::BATCH_MODE_* */ + protected $batchMode; + /** @var bool */ + protected $appendNewline; + /** @var bool */ + protected $ignoreEmptyContextAndExtra; + /** @var bool */ + protected $includeStacktraces = false; + + /** + * @param self::BATCH_MODE_* $batchMode + */ + public function __construct(int $batchMode = self::BATCH_MODE_JSON, bool $appendNewline = true, bool $ignoreEmptyContextAndExtra = false, bool $includeStacktraces = false) + { + $this->batchMode = $batchMode; + $this->appendNewline = $appendNewline; + $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra; + $this->includeStacktraces = $includeStacktraces; + + parent::__construct(); + } + + /** + * The batch mode option configures the formatting style for + * multiple records. By default, multiple records will be + * formatted as a JSON-encoded array. However, for + * compatibility with some API endpoints, alternative styles + * are available. + */ + public function getBatchMode(): int + { + return $this->batchMode; + } + + /** + * True if newlines are appended to every formatted record + */ + public function isAppendingNewlines(): bool + { + return $this->appendNewline; + } + + /** + * {@inheritDoc} + */ + public function format(array $record): string + { + $normalized = $this->normalize($record); + + if (isset($normalized['context']) && $normalized['context'] === []) { + if ($this->ignoreEmptyContextAndExtra) { + unset($normalized['context']); + } else { + $normalized['context'] = new \stdClass; + } + } + if (isset($normalized['extra']) && $normalized['extra'] === []) { + if ($this->ignoreEmptyContextAndExtra) { + unset($normalized['extra']); + } else { + $normalized['extra'] = new \stdClass; + } + } + + return $this->toJson($normalized, true) . ($this->appendNewline ? "\n" : ''); + } + + /** + * {@inheritDoc} + */ + public function formatBatch(array $records): string + { + switch ($this->batchMode) { + case static::BATCH_MODE_NEWLINES: + return $this->formatBatchNewlines($records); + + case static::BATCH_MODE_JSON: + default: + return $this->formatBatchJson($records); + } + } + + /** + * @return self + */ + public function includeStacktraces(bool $include = true): self + { + $this->includeStacktraces = $include; + + return $this; + } + + /** + * Return a JSON-encoded array of records. + * + * @phpstan-param Record[] $records + */ + protected function formatBatchJson(array $records): string + { + return $this->toJson($this->normalize($records), true); + } + + /** + * Use new lines to separate records instead of a + * JSON-encoded array. + * + * @phpstan-param Record[] $records + */ + protected function formatBatchNewlines(array $records): string + { + $instance = $this; + + $oldNewline = $this->appendNewline; + $this->appendNewline = false; + array_walk($records, function (&$value, $key) use ($instance) { + $value = $instance->format($value); + }); + $this->appendNewline = $oldNewline; + + return implode("\n", $records); + } + + /** + * Normalizes given $data. + * + * @param mixed $data + * + * @return mixed + */ + protected function normalize($data, int $depth = 0) + { + if ($depth > $this->maxNormalizeDepth) { + return 'Over '.$this->maxNormalizeDepth.' levels deep, aborting normalization'; + } + + if (is_array($data)) { + $normalized = []; + + $count = 1; + foreach ($data as $key => $value) { + if ($count++ > $this->maxNormalizeItemCount) { + $normalized['...'] = 'Over '.$this->maxNormalizeItemCount.' items ('.count($data).' total), aborting normalization'; + break; + } + + $normalized[$key] = $this->normalize($value, $depth + 1); + } + + return $normalized; + } + + if (is_object($data)) { + if ($data instanceof \DateTimeInterface) { + return $this->formatDate($data); + } + + if ($data instanceof Throwable) { + return $this->normalizeException($data, $depth); + } + + // if the object has specific json serializability we want to make sure we skip the __toString treatment below + if ($data instanceof \JsonSerializable) { + return $data; + } + + if (method_exists($data, '__toString')) { + return $data->__toString(); + } + + return $data; + } + + if (is_resource($data)) { + return parent::normalize($data); + } + + return $data; + } + + /** + * Normalizes given exception with or without its own stack trace based on + * `includeStacktraces` property. + * + * {@inheritDoc} + */ + protected function normalizeException(Throwable $e, int $depth = 0): array + { + $data = parent::normalizeException($e, $depth); + if (!$this->includeStacktraces) { + unset($data['trace']); + } + + return $data; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php new file mode 100644 index 0000000..f692ed3 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php @@ -0,0 +1,246 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Utils; + +/** + * Formats incoming records into a one-line string + * + * This is especially useful for logging to files + * + * @author Jordi Boggiano + * @author Christophe Coevoet + */ +class LineFormatter extends NormalizerFormatter +{ + public const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"; + + /** @var string */ + protected $format; + /** @var bool */ + protected $allowInlineLineBreaks; + /** @var bool */ + protected $ignoreEmptyContextAndExtra; + /** @var bool */ + protected $includeStacktraces; + /** @var ?callable */ + protected $stacktracesParser; + + /** + * @param string|null $format The format of the message + * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format + * @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries + * @param bool $ignoreEmptyContextAndExtra + */ + public function __construct(?string $format = null, ?string $dateFormat = null, bool $allowInlineLineBreaks = false, bool $ignoreEmptyContextAndExtra = false, bool $includeStacktraces = false) + { + $this->format = $format === null ? static::SIMPLE_FORMAT : $format; + $this->allowInlineLineBreaks = $allowInlineLineBreaks; + $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra; + $this->includeStacktraces($includeStacktraces); + parent::__construct($dateFormat); + } + + public function includeStacktraces(bool $include = true, ?callable $parser = null): self + { + $this->includeStacktraces = $include; + if ($this->includeStacktraces) { + $this->allowInlineLineBreaks = true; + $this->stacktracesParser = $parser; + } + + return $this; + } + + public function allowInlineLineBreaks(bool $allow = true): self + { + $this->allowInlineLineBreaks = $allow; + + return $this; + } + + public function ignoreEmptyContextAndExtra(bool $ignore = true): self + { + $this->ignoreEmptyContextAndExtra = $ignore; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function format(array $record): string + { + $vars = parent::format($record); + + $output = $this->format; + + foreach ($vars['extra'] as $var => $val) { + if (false !== strpos($output, '%extra.'.$var.'%')) { + $output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output); + unset($vars['extra'][$var]); + } + } + + foreach ($vars['context'] as $var => $val) { + if (false !== strpos($output, '%context.'.$var.'%')) { + $output = str_replace('%context.'.$var.'%', $this->stringify($val), $output); + unset($vars['context'][$var]); + } + } + + if ($this->ignoreEmptyContextAndExtra) { + if (empty($vars['context'])) { + unset($vars['context']); + $output = str_replace('%context%', '', $output); + } + + if (empty($vars['extra'])) { + unset($vars['extra']); + $output = str_replace('%extra%', '', $output); + } + } + + foreach ($vars as $var => $val) { + if (false !== strpos($output, '%'.$var.'%')) { + $output = str_replace('%'.$var.'%', $this->stringify($val), $output); + } + } + + // remove leftover %extra.xxx% and %context.xxx% if any + if (false !== strpos($output, '%')) { + $output = preg_replace('/%(?:extra|context)\..+?%/', '', $output); + if (null === $output) { + $pcreErrorCode = preg_last_error(); + throw new \RuntimeException('Failed to run preg_replace: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode)); + } + } + + return $output; + } + + public function formatBatch(array $records): string + { + $message = ''; + foreach ($records as $record) { + $message .= $this->format($record); + } + + return $message; + } + + /** + * @param mixed $value + */ + public function stringify($value): string + { + return $this->replaceNewlines($this->convertToString($value)); + } + + protected function normalizeException(\Throwable $e, int $depth = 0): string + { + $str = $this->formatException($e); + + if ($previous = $e->getPrevious()) { + do { + $depth++; + if ($depth > $this->maxNormalizeDepth) { + $str .= '\n[previous exception] Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization'; + break; + } + + $str .= "\n[previous exception] " . $this->formatException($previous); + } while ($previous = $previous->getPrevious()); + } + + return $str; + } + + /** + * @param mixed $data + */ + protected function convertToString($data): string + { + if (null === $data || is_bool($data)) { + return var_export($data, true); + } + + if (is_scalar($data)) { + return (string) $data; + } + + return $this->toJson($data, true); + } + + protected function replaceNewlines(string $str): string + { + if ($this->allowInlineLineBreaks) { + if (0 === strpos($str, '{')) { + $str = preg_replace('/(?getCode(); + if ($e instanceof \SoapFault) { + if (isset($e->faultcode)) { + $str .= ' faultcode: ' . $e->faultcode; + } + + if (isset($e->faultactor)) { + $str .= ' faultactor: ' . $e->faultactor; + } + + if (isset($e->detail)) { + if (is_string($e->detail)) { + $str .= ' detail: ' . $e->detail; + } elseif (is_object($e->detail) || is_array($e->detail)) { + $str .= ' detail: ' . $this->toJson($e->detail, true); + } + } + } + $str .= '): ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine() . ')'; + + if ($this->includeStacktraces) { + $str .= $this->stacktracesParser($e); + } + + return $str; + } + + private function stacktracesParser(\Throwable $e): string + { + $trace = $e->getTraceAsString(); + + if ($this->stacktracesParser) { + $trace = $this->stacktracesParserCustom($trace); + } + + return "\n[stacktrace]\n" . $trace . "\n"; + } + + private function stacktracesParserCustom(string $trace): string + { + return implode("\n", array_filter(array_map($this->stacktracesParser, explode("\n", $trace)))); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php new file mode 100644 index 0000000..f478a16 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Encodes message information into JSON in a format compatible with Loggly. + * + * @author Adam Pancutt + */ +class LogglyFormatter extends JsonFormatter +{ + /** + * Overrides the default batch mode to new lines for compatibility with the + * Loggly bulk API. + */ + public function __construct(int $batchMode = self::BATCH_MODE_NEWLINES, bool $appendNewline = false) + { + parent::__construct($batchMode, $appendNewline); + } + + /** + * Appends the 'timestamp' parameter for indexing by Loggly. + * + * @see https://www.loggly.com/docs/automated-parsing/#json + * @see \Monolog\Formatter\JsonFormatter::format() + */ + public function format(array $record): string + { + if (isset($record["datetime"]) && ($record["datetime"] instanceof \DateTimeInterface)) { + $record["timestamp"] = $record["datetime"]->format("Y-m-d\TH:i:s.uO"); + unset($record["datetime"]); + } + + return parent::format($record); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/LogmaticFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/LogmaticFormatter.php new file mode 100644 index 0000000..1539650 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/LogmaticFormatter.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Encodes message information into JSON in a format compatible with Logmatic. + * + * @author Julien Breux + */ +class LogmaticFormatter extends JsonFormatter +{ + protected const MARKERS = ["sourcecode", "php"]; + + /** + * @var string + */ + protected $hostname = ''; + + /** + * @var string + */ + protected $appname = ''; + + public function setHostname(string $hostname): self + { + $this->hostname = $hostname; + + return $this; + } + + public function setAppname(string $appname): self + { + $this->appname = $appname; + + return $this; + } + + /** + * Appends the 'hostname' and 'appname' parameter for indexing by Logmatic. + * + * @see http://doc.logmatic.io/docs/basics-to-send-data + * @see \Monolog\Formatter\JsonFormatter::format() + */ + public function format(array $record): string + { + if (!empty($this->hostname)) { + $record["hostname"] = $this->hostname; + } + if (!empty($this->appname)) { + $record["appname"] = $this->appname; + } + + $record["@marker"] = static::MARKERS; + + return parent::format($record); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php new file mode 100644 index 0000000..359348b --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Serializes a log message to Logstash Event Format + * + * @see https://www.elastic.co/products/logstash + * @see https://github.com/elastic/logstash/blob/master/logstash-core/src/main/java/org/logstash/Event.java + * + * @author Tim Mower + */ +class LogstashFormatter extends NormalizerFormatter +{ + /** + * @var string the name of the system for the Logstash log message, used to fill the @source field + */ + protected $systemName; + + /** + * @var string an application name for the Logstash log message, used to fill the @type field + */ + protected $applicationName; + + /** + * @var string the key for 'extra' fields from the Monolog record + */ + protected $extraKey; + + /** + * @var string the key for 'context' fields from the Monolog record + */ + protected $contextKey; + + /** + * @param string $applicationName The application that sends the data, used as the "type" field of logstash + * @param string|null $systemName The system/machine name, used as the "source" field of logstash, defaults to the hostname of the machine + * @param string $extraKey The key for extra keys inside logstash "fields", defaults to extra + * @param string $contextKey The key for context keys inside logstash "fields", defaults to context + */ + public function __construct(string $applicationName, ?string $systemName = null, string $extraKey = 'extra', string $contextKey = 'context') + { + // logstash requires a ISO 8601 format date with optional millisecond precision. + parent::__construct('Y-m-d\TH:i:s.uP'); + + $this->systemName = $systemName === null ? (string) gethostname() : $systemName; + $this->applicationName = $applicationName; + $this->extraKey = $extraKey; + $this->contextKey = $contextKey; + } + + /** + * {@inheritDoc} + */ + public function format(array $record): string + { + $record = parent::format($record); + + if (empty($record['datetime'])) { + $record['datetime'] = gmdate('c'); + } + $message = [ + '@timestamp' => $record['datetime'], + '@version' => 1, + 'host' => $this->systemName, + ]; + if (isset($record['message'])) { + $message['message'] = $record['message']; + } + if (isset($record['channel'])) { + $message['type'] = $record['channel']; + $message['channel'] = $record['channel']; + } + if (isset($record['level_name'])) { + $message['level'] = $record['level_name']; + } + if (isset($record['level'])) { + $message['monolog_level'] = $record['level']; + } + if ($this->applicationName) { + $message['type'] = $this->applicationName; + } + if (!empty($record['extra'])) { + $message[$this->extraKey] = $record['extra']; + } + if (!empty($record['context'])) { + $message[$this->contextKey] = $record['context']; + } + + return $this->toJson($message) . "\n"; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php new file mode 100644 index 0000000..e0abc11 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use MongoDB\BSON\Type; +use MongoDB\BSON\UTCDateTime; +use Monolog\Utils; + +/** + * Formats a record for use with the MongoDBHandler. + * + * @author Florian Plattner + */ +class MongoDBFormatter implements FormatterInterface +{ + /** @var bool */ + private $exceptionTraceAsString; + /** @var int */ + private $maxNestingLevel; + /** @var bool */ + private $isLegacyMongoExt; + + /** + * @param int $maxNestingLevel 0 means infinite nesting, the $record itself is level 1, $record['context'] is 2 + * @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings + */ + public function __construct(int $maxNestingLevel = 3, bool $exceptionTraceAsString = true) + { + $this->maxNestingLevel = max($maxNestingLevel, 0); + $this->exceptionTraceAsString = $exceptionTraceAsString; + + $this->isLegacyMongoExt = extension_loaded('mongodb') && version_compare((string) phpversion('mongodb'), '1.1.9', '<='); + } + + /** + * {@inheritDoc} + * + * @return mixed[] + */ + public function format(array $record): array + { + /** @var mixed[] $res */ + $res = $this->formatArray($record); + + return $res; + } + + /** + * {@inheritDoc} + * + * @return array + */ + public function formatBatch(array $records): array + { + $formatted = []; + foreach ($records as $key => $record) { + $formatted[$key] = $this->format($record); + } + + return $formatted; + } + + /** + * @param mixed[] $array + * @return mixed[]|string Array except when max nesting level is reached then a string "[...]" + */ + protected function formatArray(array $array, int $nestingLevel = 0) + { + if ($this->maxNestingLevel > 0 && $nestingLevel > $this->maxNestingLevel) { + return '[...]'; + } + + foreach ($array as $name => $value) { + if ($value instanceof \DateTimeInterface) { + $array[$name] = $this->formatDate($value, $nestingLevel + 1); + } elseif ($value instanceof \Throwable) { + $array[$name] = $this->formatException($value, $nestingLevel + 1); + } elseif (is_array($value)) { + $array[$name] = $this->formatArray($value, $nestingLevel + 1); + } elseif (is_object($value) && !$value instanceof Type) { + $array[$name] = $this->formatObject($value, $nestingLevel + 1); + } + } + + return $array; + } + + /** + * @param mixed $value + * @return mixed[]|string + */ + protected function formatObject($value, int $nestingLevel) + { + $objectVars = get_object_vars($value); + $objectVars['class'] = Utils::getClass($value); + + return $this->formatArray($objectVars, $nestingLevel); + } + + /** + * @return mixed[]|string + */ + protected function formatException(\Throwable $exception, int $nestingLevel) + { + $formattedException = [ + 'class' => Utils::getClass($exception), + 'message' => $exception->getMessage(), + 'code' => (int) $exception->getCode(), + 'file' => $exception->getFile() . ':' . $exception->getLine(), + ]; + + if ($this->exceptionTraceAsString === true) { + $formattedException['trace'] = $exception->getTraceAsString(); + } else { + $formattedException['trace'] = $exception->getTrace(); + } + + return $this->formatArray($formattedException, $nestingLevel); + } + + protected function formatDate(\DateTimeInterface $value, int $nestingLevel): UTCDateTime + { + if ($this->isLegacyMongoExt) { + return $this->legacyGetMongoDbDateTime($value); + } + + return $this->getMongoDbDateTime($value); + } + + private function getMongoDbDateTime(\DateTimeInterface $value): UTCDateTime + { + return new UTCDateTime((int) floor(((float) $value->format('U.u')) * 1000)); + } + + /** + * This is needed to support MongoDB Driver v1.19 and below + * + * See https://github.com/mongodb/mongo-php-driver/issues/426 + * + * It can probably be removed in 2.1 or later once MongoDB's 1.2 is released and widely adopted + */ + private function legacyGetMongoDbDateTime(\DateTimeInterface $value): UTCDateTime + { + $milliseconds = floor(((float) $value->format('U.u')) * 1000); + + $milliseconds = (PHP_INT_SIZE == 8) //64-bit OS? + ? (int) $milliseconds + : (string) $milliseconds; + + // @phpstan-ignore-next-line + return new UTCDateTime($milliseconds); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php new file mode 100644 index 0000000..a38d300 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php @@ -0,0 +1,287 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\DateTimeImmutable; +use Monolog\Utils; +use Throwable; + +/** + * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets + * + * @author Jordi Boggiano + */ +class NormalizerFormatter implements FormatterInterface +{ + public const SIMPLE_DATE = "Y-m-d\TH:i:sP"; + + /** @var string */ + protected $dateFormat; + /** @var int */ + protected $maxNormalizeDepth = 9; + /** @var int */ + protected $maxNormalizeItemCount = 1000; + + /** @var int */ + private $jsonEncodeOptions = Utils::DEFAULT_JSON_FLAGS; + + /** + * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct(?string $dateFormat = null) + { + $this->dateFormat = null === $dateFormat ? static::SIMPLE_DATE : $dateFormat; + if (!function_exists('json_encode')) { + throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s NormalizerFormatter'); + } + } + + /** + * {@inheritDoc} + * + * @param mixed[] $record + */ + public function format(array $record) + { + return $this->normalize($record); + } + + /** + * {@inheritDoc} + */ + public function formatBatch(array $records) + { + foreach ($records as $key => $record) { + $records[$key] = $this->format($record); + } + + return $records; + } + + public function getDateFormat(): string + { + return $this->dateFormat; + } + + public function setDateFormat(string $dateFormat): self + { + $this->dateFormat = $dateFormat; + + return $this; + } + + /** + * The maximum number of normalization levels to go through + */ + public function getMaxNormalizeDepth(): int + { + return $this->maxNormalizeDepth; + } + + public function setMaxNormalizeDepth(int $maxNormalizeDepth): self + { + $this->maxNormalizeDepth = $maxNormalizeDepth; + + return $this; + } + + /** + * The maximum number of items to normalize per level + */ + public function getMaxNormalizeItemCount(): int + { + return $this->maxNormalizeItemCount; + } + + public function setMaxNormalizeItemCount(int $maxNormalizeItemCount): self + { + $this->maxNormalizeItemCount = $maxNormalizeItemCount; + + return $this; + } + + /** + * Enables `json_encode` pretty print. + */ + public function setJsonPrettyPrint(bool $enable): self + { + if ($enable) { + $this->jsonEncodeOptions |= JSON_PRETTY_PRINT; + } else { + $this->jsonEncodeOptions &= ~JSON_PRETTY_PRINT; + } + + return $this; + } + + /** + * @param mixed $data + * @return null|scalar|array + */ + protected function normalize($data, int $depth = 0) + { + if ($depth > $this->maxNormalizeDepth) { + return 'Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization'; + } + + if (null === $data || is_scalar($data)) { + if (is_float($data)) { + if (is_infinite($data)) { + return ($data > 0 ? '' : '-') . 'INF'; + } + if (is_nan($data)) { + return 'NaN'; + } + } + + return $data; + } + + if (is_array($data)) { + $normalized = []; + + $count = 1; + foreach ($data as $key => $value) { + if ($count++ > $this->maxNormalizeItemCount) { + $normalized['...'] = 'Over ' . $this->maxNormalizeItemCount . ' items ('.count($data).' total), aborting normalization'; + break; + } + + $normalized[$key] = $this->normalize($value, $depth + 1); + } + + return $normalized; + } + + if ($data instanceof \DateTimeInterface) { + return $this->formatDate($data); + } + + if (is_object($data)) { + if ($data instanceof Throwable) { + return $this->normalizeException($data, $depth); + } + + if ($data instanceof \JsonSerializable) { + /** @var null|scalar|array $value */ + $value = $data->jsonSerialize(); + } elseif (method_exists($data, '__toString')) { + /** @var string $value */ + $value = $data->__toString(); + } else { + // the rest is normalized by json encoding and decoding it + /** @var null|scalar|array $value */ + $value = json_decode($this->toJson($data, true), true); + } + + return [Utils::getClass($data) => $value]; + } + + if (is_resource($data)) { + return sprintf('[resource(%s)]', get_resource_type($data)); + } + + return '[unknown('.gettype($data).')]'; + } + + /** + * @return mixed[] + */ + protected function normalizeException(Throwable $e, int $depth = 0) + { + if ($depth > $this->maxNormalizeDepth) { + return ['Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization']; + } + + if ($e instanceof \JsonSerializable) { + return (array) $e->jsonSerialize(); + } + + $data = [ + 'class' => Utils::getClass($e), + 'message' => $e->getMessage(), + 'code' => (int) $e->getCode(), + 'file' => $e->getFile().':'.$e->getLine(), + ]; + + if ($e instanceof \SoapFault) { + if (isset($e->faultcode)) { + $data['faultcode'] = $e->faultcode; + } + + if (isset($e->faultactor)) { + $data['faultactor'] = $e->faultactor; + } + + if (isset($e->detail)) { + if (is_string($e->detail)) { + $data['detail'] = $e->detail; + } elseif (is_object($e->detail) || is_array($e->detail)) { + $data['detail'] = $this->toJson($e->detail, true); + } + } + } + + $trace = $e->getTrace(); + foreach ($trace as $frame) { + if (isset($frame['file'])) { + $data['trace'][] = $frame['file'].':'.$frame['line']; + } + } + + if ($previous = $e->getPrevious()) { + $data['previous'] = $this->normalizeException($previous, $depth + 1); + } + + return $data; + } + + /** + * Return the JSON representation of a value + * + * @param mixed $data + * @throws \RuntimeException if encoding fails and errors are not ignored + * @return string if encoding fails and ignoreErrors is true 'null' is returned + */ + protected function toJson($data, bool $ignoreErrors = false): string + { + return Utils::jsonEncode($data, $this->jsonEncodeOptions, $ignoreErrors); + } + + /** + * @return string + */ + protected function formatDate(\DateTimeInterface $date) + { + // in case the date format isn't custom then we defer to the custom DateTimeImmutable + // formatting logic, which will pick the right format based on whether useMicroseconds is on + if ($this->dateFormat === self::SIMPLE_DATE && $date instanceof DateTimeImmutable) { + return (string) $date; + } + + return $date->format($this->dateFormat); + } + + public function addJsonEncodeOption(int $option): self + { + $this->jsonEncodeOptions |= $option; + + return $this; + } + + public function removeJsonEncodeOption(int $option): self + { + $this->jsonEncodeOptions &= ~$option; + + return $this; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php new file mode 100644 index 0000000..db57840 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Formats data into an associative array of scalar values. + * Objects and arrays will be JSON encoded. + * + * @author Andrew Lawson + */ +class ScalarFormatter extends NormalizerFormatter +{ + /** + * {@inheritDoc} + * + * @phpstan-return array $record + */ + public function format(array $record): array + { + $result = []; + foreach ($record as $key => $value) { + $result[$key] = $this->normalizeValue($value); + } + + return $result; + } + + /** + * @param mixed $value + * @return scalar|null + */ + protected function normalizeValue($value) + { + $normalized = $this->normalize($value); + + if (is_array($normalized)) { + return $this->toJson($normalized, true); + } + + return $normalized; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php b/msd/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php new file mode 100644 index 0000000..2dbc76c --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Serializes a log message according to Wildfire's header requirements + * + * @author Eric Clemmons (@ericclemmons) + * @author Christophe Coevoet + * @author Kirill chEbba Chebunin + * + * @phpstan-import-type Level from \Monolog\Logger + */ +class WildfireFormatter extends NormalizerFormatter +{ + /** + * Translates Monolog log levels to Wildfire levels. + * + * @var array + */ + private $logLevels = [ + Logger::DEBUG => 'LOG', + Logger::INFO => 'INFO', + Logger::NOTICE => 'INFO', + Logger::WARNING => 'WARN', + Logger::ERROR => 'ERROR', + Logger::CRITICAL => 'ERROR', + Logger::ALERT => 'ERROR', + Logger::EMERGENCY => 'ERROR', + ]; + + /** + * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct(?string $dateFormat = null) + { + parent::__construct($dateFormat); + + // http headers do not like non-ISO-8559-1 characters + $this->removeJsonEncodeOption(JSON_UNESCAPED_UNICODE); + } + + /** + * {@inheritDoc} + * + * @return string + */ + public function format(array $record): string + { + // Retrieve the line and file if set and remove them from the formatted extra + $file = $line = ''; + if (isset($record['extra']['file'])) { + $file = $record['extra']['file']; + unset($record['extra']['file']); + } + if (isset($record['extra']['line'])) { + $line = $record['extra']['line']; + unset($record['extra']['line']); + } + + /** @var mixed[] $record */ + $record = $this->normalize($record); + $message = ['message' => $record['message']]; + $handleError = false; + if ($record['context']) { + $message['context'] = $record['context']; + $handleError = true; + } + if ($record['extra']) { + $message['extra'] = $record['extra']; + $handleError = true; + } + if (count($message) === 1) { + $message = reset($message); + } + + if (isset($record['context']['table'])) { + $type = 'TABLE'; + $label = $record['channel'] .': '. $record['message']; + $message = $record['context']['table']; + } else { + $type = $this->logLevels[$record['level']]; + $label = $record['channel']; + } + + // Create JSON object describing the appearance of the message in the console + $json = $this->toJson([ + [ + 'Type' => $type, + 'File' => $file, + 'Line' => $line, + 'Label' => $label, + ], + $message, + ], $handleError); + + // The message itself is a serialization of the above JSON object + it's length + return sprintf( + '%d|%s|', + strlen($json), + $json + ); + } + + /** + * {@inheritDoc} + * + * @phpstan-return never + */ + public function formatBatch(array $records) + { + throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter'); + } + + /** + * {@inheritDoc} + * + * @return null|scalar|array|object + */ + protected function normalize($data, int $depth = 0) + { + if (is_object($data) && !$data instanceof \DateTimeInterface) { + return $data; + } + + return parent::normalize($data, $depth); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php new file mode 100644 index 0000000..efde02c --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\ResettableInterface; +use Psr\Log\LogLevel; + +/** + * Base Handler class providing basic level/bubble support + * + * @author Jordi Boggiano + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +abstract class AbstractHandler extends Handler implements ResettableInterface +{ + /** + * @var int + * @phpstan-var Level + */ + protected $level = Logger::DEBUG; + /** @var bool */ + protected $bubble = true; + + /** + * @param int|string $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = Logger::DEBUG, bool $bubble = true) + { + $this->setLevel($level); + $this->bubble = $bubble; + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return $record['level'] >= $this->level; + } + + /** + * Sets minimum logging level at which this handler will be triggered. + * + * @param Level|LevelName|LogLevel::* $level Level or level name + * @return self + */ + public function setLevel($level): self + { + $this->level = Logger::toMonologLevel($level); + + return $this; + } + + /** + * Gets minimum logging level at which this handler will be triggered. + * + * @return int + * + * @phpstan-return Level + */ + public function getLevel(): int + { + return $this->level; + } + + /** + * Sets the bubbling behavior. + * + * @param bool $bubble true means that this handler allows bubbling. + * false means that bubbling is not permitted. + * @return self + */ + public function setBubble(bool $bubble): self + { + $this->bubble = $bubble; + + return $this; + } + + /** + * Gets the bubbling behavior. + * + * @return bool true means that this handler allows bubbling. + * false means that bubbling is not permitted. + */ + public function getBubble(): bool + { + return $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function reset() + { + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php new file mode 100644 index 0000000..2d1816a --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Base Handler class providing the Handler structure, including processors and formatters + * + * Classes extending it should (in most cases) only implement write($record) + * + * @author Jordi Boggiano + * @author Christophe Coevoet + * + * @phpstan-import-type LevelName from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-type FormattedRecord array{message: string, context: mixed[], level: Level, level_name: LevelName, channel: string, datetime: \DateTimeImmutable, extra: mixed[], formatted: mixed} + */ +abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + use FormattableHandlerTrait; + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if (!$this->isHandling($record)) { + return false; + } + + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + $record['formatted'] = $this->getFormatter()->format($record); + + $this->write($record); + + return false === $this->bubble; + } + + /** + * Writes the record down to the log of the implementing handler + * + * @phpstan-param FormattedRecord $record + */ + abstract protected function write(array $record): void; + + /** + * @return void + */ + public function reset() + { + parent::reset(); + + $this->resetProcessors(); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php new file mode 100644 index 0000000..044f17e --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; + +/** + * Common syslog functionality + * + * @phpstan-import-type Level from \Monolog\Logger + */ +abstract class AbstractSyslogHandler extends AbstractProcessingHandler +{ + /** @var int */ + protected $facility; + + /** + * Translates Monolog log levels to syslog log priorities. + * @var array + * @phpstan-var array + */ + protected $logLevels = [ + Logger::DEBUG => LOG_DEBUG, + Logger::INFO => LOG_INFO, + Logger::NOTICE => LOG_NOTICE, + Logger::WARNING => LOG_WARNING, + Logger::ERROR => LOG_ERR, + Logger::CRITICAL => LOG_CRIT, + Logger::ALERT => LOG_ALERT, + Logger::EMERGENCY => LOG_EMERG, + ]; + + /** + * List of valid log facility names. + * @var array + */ + protected $facilities = [ + 'auth' => LOG_AUTH, + 'authpriv' => LOG_AUTHPRIV, + 'cron' => LOG_CRON, + 'daemon' => LOG_DAEMON, + 'kern' => LOG_KERN, + 'lpr' => LOG_LPR, + 'mail' => LOG_MAIL, + 'news' => LOG_NEWS, + 'syslog' => LOG_SYSLOG, + 'user' => LOG_USER, + 'uucp' => LOG_UUCP, + ]; + + /** + * @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant + */ + public function __construct($facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->facilities['local0'] = LOG_LOCAL0; + $this->facilities['local1'] = LOG_LOCAL1; + $this->facilities['local2'] = LOG_LOCAL2; + $this->facilities['local3'] = LOG_LOCAL3; + $this->facilities['local4'] = LOG_LOCAL4; + $this->facilities['local5'] = LOG_LOCAL5; + $this->facilities['local6'] = LOG_LOCAL6; + $this->facilities['local7'] = LOG_LOCAL7; + } else { + $this->facilities['local0'] = 128; // LOG_LOCAL0 + $this->facilities['local1'] = 136; // LOG_LOCAL1 + $this->facilities['local2'] = 144; // LOG_LOCAL2 + $this->facilities['local3'] = 152; // LOG_LOCAL3 + $this->facilities['local4'] = 160; // LOG_LOCAL4 + $this->facilities['local5'] = 168; // LOG_LOCAL5 + $this->facilities['local6'] = 176; // LOG_LOCAL6 + $this->facilities['local7'] = 184; // LOG_LOCAL7 + } + + // convert textual description of facility to syslog constant + if (is_string($facility) && array_key_exists(strtolower($facility), $this->facilities)) { + $facility = $this->facilities[strtolower($facility)]; + } elseif (!in_array($facility, array_values($this->facilities), true)) { + throw new \UnexpectedValueException('Unknown facility value "'.$facility.'" given'); + } + + $this->facility = $facility; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter('%channel%.%level_name%: %message% %context% %extra%'); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php new file mode 100644 index 0000000..c8b55c1 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php @@ -0,0 +1,170 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\JsonFormatter; +use PhpAmqpLib\Message\AMQPMessage; +use PhpAmqpLib\Channel\AMQPChannel; +use AMQPExchange; + +/** + * @phpstan-import-type Record from \Monolog\Logger + */ +class AmqpHandler extends AbstractProcessingHandler +{ + /** + * @var AMQPExchange|AMQPChannel $exchange + */ + protected $exchange; + /** @var array */ + private $extraAttributes = []; + + /** + * @return array + */ + public function getExtraAttributes(): array + { + return $this->extraAttributes; + } + + /** + * Configure extra attributes to pass to the AMQPExchange (if you are using the amqp extension) + * + * @param array $extraAttributes One of content_type, content_encoding, + * message_id, user_id, app_id, delivery_mode, + * priority, timestamp, expiration, type + * or reply_to, headers. + * @return AmqpHandler + */ + public function setExtraAttributes(array $extraAttributes): self + { + $this->extraAttributes = $extraAttributes; + return $this; + } + + /** + * @var string + */ + protected $exchangeName; + + /** + * @param AMQPExchange|AMQPChannel $exchange AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use + * @param string|null $exchangeName Optional exchange name, for AMQPChannel (PhpAmqpLib) only + */ + public function __construct($exchange, ?string $exchangeName = null, $level = Logger::DEBUG, bool $bubble = true) + { + if ($exchange instanceof AMQPChannel) { + $this->exchangeName = (string) $exchangeName; + } elseif (!$exchange instanceof AMQPExchange) { + throw new \InvalidArgumentException('PhpAmqpLib\Channel\AMQPChannel or AMQPExchange instance required'); + } elseif ($exchangeName) { + @trigger_error('The $exchangeName parameter can only be passed when using PhpAmqpLib, if using an AMQPExchange instance configure it beforehand', E_USER_DEPRECATED); + } + $this->exchange = $exchange; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $data = $record["formatted"]; + $routingKey = $this->getRoutingKey($record); + + if ($this->exchange instanceof AMQPExchange) { + $attributes = [ + 'delivery_mode' => 2, + 'content_type' => 'application/json', + ]; + if ($this->extraAttributes) { + $attributes = array_merge($attributes, $this->extraAttributes); + } + $this->exchange->publish( + $data, + $routingKey, + 0, + $attributes + ); + } else { + $this->exchange->basic_publish( + $this->createAmqpMessage($data), + $this->exchangeName, + $routingKey + ); + } + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + if ($this->exchange instanceof AMQPExchange) { + parent::handleBatch($records); + + return; + } + + foreach ($records as $record) { + if (!$this->isHandling($record)) { + continue; + } + + /** @var Record $record */ + $record = $this->processRecord($record); + $data = $this->getFormatter()->format($record); + + $this->exchange->batch_basic_publish( + $this->createAmqpMessage($data), + $this->exchangeName, + $this->getRoutingKey($record) + ); + } + + $this->exchange->publish_batch(); + } + + /** + * Gets the routing key for the AMQP exchange + * + * @phpstan-param Record $record + */ + protected function getRoutingKey(array $record): string + { + $routingKey = sprintf('%s.%s', $record['level_name'], $record['channel']); + + return strtolower($routingKey); + } + + private function createAmqpMessage(string $data): AMQPMessage + { + return new AMQPMessage( + $data, + [ + 'delivery_mode' => 2, + 'content_type' => 'application/json', + ] + ); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php new file mode 100644 index 0000000..e3d305c --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php @@ -0,0 +1,293 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; +use Monolog\Utils; + +use function count; +use function headers_list; +use function stripos; +use function trigger_error; + +use const E_USER_DEPRECATED; + +/** + * Handler sending logs to browser's javascript console with no browser extension required + * + * @author Olivier Poitrey + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class BrowserConsoleHandler extends AbstractProcessingHandler +{ + /** @var bool */ + protected static $initialized = false; + /** @var FormattedRecord[] */ + protected static $records = []; + + protected const FORMAT_HTML = 'html'; + protected const FORMAT_JS = 'js'; + protected const FORMAT_UNKNOWN = 'unknown'; + + /** + * {@inheritDoc} + * + * Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format. + * + * Example of formatted string: + * + * You can do [[blue text]]{color: blue} or [[green background]]{background-color: green; color: white} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter('[[%channel%]]{macro: autolabel} [[%level_name%]]{font-weight: bold} %message%'); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + // Accumulate records + static::$records[] = $record; + + // Register shutdown handler if not already done + if (!static::$initialized) { + static::$initialized = true; + $this->registerShutdownFunction(); + } + } + + /** + * Convert records to javascript console commands and send it to the browser. + * This method is automatically called on PHP shutdown if output is HTML or Javascript. + */ + public static function send(): void + { + $format = static::getResponseFormat(); + if ($format === self::FORMAT_UNKNOWN) { + return; + } + + if (count(static::$records)) { + if ($format === self::FORMAT_HTML) { + static::writeOutput(''); + } elseif ($format === self::FORMAT_JS) { + static::writeOutput(static::generateScript()); + } + static::resetStatic(); + } + } + + public function close(): void + { + self::resetStatic(); + } + + public function reset() + { + parent::reset(); + + self::resetStatic(); + } + + /** + * Forget all logged records + */ + public static function resetStatic(): void + { + static::$records = []; + } + + /** + * Wrapper for register_shutdown_function to allow overriding + */ + protected function registerShutdownFunction(): void + { + if (PHP_SAPI !== 'cli') { + register_shutdown_function(['Monolog\Handler\BrowserConsoleHandler', 'send']); + } + } + + /** + * Wrapper for echo to allow overriding + */ + protected static function writeOutput(string $str): void + { + echo $str; + } + + /** + * Checks the format of the response + * + * If Content-Type is set to application/javascript or text/javascript -> js + * If Content-Type is set to text/html, or is unset -> html + * If Content-Type is anything else -> unknown + * + * @return string One of 'js', 'html' or 'unknown' + * @phpstan-return self::FORMAT_* + */ + protected static function getResponseFormat(): string + { + // Check content type + foreach (headers_list() as $header) { + if (stripos($header, 'content-type:') === 0) { + return static::getResponseFormatFromContentType($header); + } + } + + return self::FORMAT_HTML; + } + + /** + * @return string One of 'js', 'html' or 'unknown' + * @phpstan-return self::FORMAT_* + */ + protected static function getResponseFormatFromContentType(string $contentType): string + { + // This handler only works with HTML and javascript outputs + // text/javascript is obsolete in favour of application/javascript, but still used + if (stripos($contentType, 'application/javascript') !== false || stripos($contentType, 'text/javascript') !== false) { + return self::FORMAT_JS; + } + + if (stripos($contentType, 'text/html') !== false) { + return self::FORMAT_HTML; + } + + return self::FORMAT_UNKNOWN; + } + + private static function generateScript(): string + { + $script = []; + foreach (static::$records as $record) { + $context = static::dump('Context', $record['context']); + $extra = static::dump('Extra', $record['extra']); + + if (empty($context) && empty($extra)) { + $script[] = static::call_array('log', static::handleStyles($record['formatted'])); + } else { + $script = array_merge( + $script, + [static::call_array('groupCollapsed', static::handleStyles($record['formatted']))], + $context, + $extra, + [static::call('groupEnd')] + ); + } + } + + return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);"; + } + + /** + * @return string[] + */ + private static function handleStyles(string $formatted): array + { + $args = []; + $format = '%c' . $formatted; + preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); + + foreach (array_reverse($matches) as $match) { + $args[] = '"font-weight: normal"'; + $args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0])); + + $pos = $match[0][1]; + $format = Utils::substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . Utils::substr($format, $pos + strlen($match[0][0])); + } + + $args[] = static::quote('font-weight: normal'); + $args[] = static::quote($format); + + return array_reverse($args); + } + + private static function handleCustomStyles(string $style, string $string): string + { + static $colors = ['blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey']; + static $labels = []; + + $style = preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function (array $m) use ($string, &$colors, &$labels) { + if (trim($m[1]) === 'autolabel') { + // Format the string as a label with consistent auto assigned background color + if (!isset($labels[$string])) { + $labels[$string] = $colors[count($labels) % count($colors)]; + } + $color = $labels[$string]; + + return "background-color: $color; color: white; border-radius: 3px; padding: 0 2px 0 2px"; + } + + return $m[1]; + }, $style); + + if (null === $style) { + $pcreErrorCode = preg_last_error(); + throw new \RuntimeException('Failed to run preg_replace_callback: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode)); + } + + return $style; + } + + /** + * @param mixed[] $dict + * @return mixed[] + */ + private static function dump(string $title, array $dict): array + { + $script = []; + $dict = array_filter($dict); + if (empty($dict)) { + return $script; + } + $script[] = static::call('log', static::quote('%c%s'), static::quote('font-weight: bold'), static::quote($title)); + foreach ($dict as $key => $value) { + $value = json_encode($value); + if (empty($value)) { + $value = static::quote(''); + } + $script[] = static::call('log', static::quote('%s: %o'), static::quote((string) $key), $value); + } + + return $script; + } + + private static function quote(string $arg): string + { + return '"' . addcslashes($arg, "\"\n\\") . '"'; + } + + /** + * @param mixed $args + */ + private static function call(...$args): string + { + $method = array_shift($args); + if (!is_string($method)) { + throw new \UnexpectedValueException('Expected the first arg to be a string, got: '.var_export($method, true)); + } + + return static::call_array($method, $args); + } + + /** + * @param mixed[] $args + */ + private static function call_array(string $method, array $args): string + { + return 'c.' . $method . '(' . implode(', ', $args) . ');'; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php new file mode 100644 index 0000000..7d35125 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php @@ -0,0 +1,167 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\ResettableInterface; +use Monolog\Formatter\FormatterInterface; + +/** + * Buffers all records until closing the handler and then pass them as batch. + * + * This is useful for a MailHandler to send only one mail per request instead of + * sending one per log message. + * + * @author Christophe Coevoet + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class BufferHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + + /** @var HandlerInterface */ + protected $handler; + /** @var int */ + protected $bufferSize = 0; + /** @var int */ + protected $bufferLimit; + /** @var bool */ + protected $flushOnOverflow; + /** @var Record[] */ + protected $buffer = []; + /** @var bool */ + protected $initialized = false; + + /** + * @param HandlerInterface $handler Handler. + * @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded + */ + public function __construct(HandlerInterface $handler, int $bufferLimit = 0, $level = Logger::DEBUG, bool $bubble = true, bool $flushOnOverflow = false) + { + parent::__construct($level, $bubble); + $this->handler = $handler; + $this->bufferLimit = $bufferLimit; + $this->flushOnOverflow = $flushOnOverflow; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($record['level'] < $this->level) { + return false; + } + + if (!$this->initialized) { + // __destructor() doesn't get called on Fatal errors + register_shutdown_function([$this, 'close']); + $this->initialized = true; + } + + if ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) { + if ($this->flushOnOverflow) { + $this->flush(); + } else { + array_shift($this->buffer); + $this->bufferSize--; + } + } + + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + $this->buffer[] = $record; + $this->bufferSize++; + + return false === $this->bubble; + } + + public function flush(): void + { + if ($this->bufferSize === 0) { + return; + } + + $this->handler->handleBatch($this->buffer); + $this->clear(); + } + + public function __destruct() + { + // suppress the parent behavior since we already have register_shutdown_function() + // to call close(), and the reference contained there will prevent this from being + // GC'd until the end of the request + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + $this->flush(); + + $this->handler->close(); + } + + /** + * Clears the buffer without flushing any messages down to the wrapped handler. + */ + public function clear(): void + { + $this->bufferSize = 0; + $this->buffer = []; + } + + public function reset() + { + $this->flush(); + + parent::reset(); + + $this->resetProcessors(); + + if ($this->handler instanceof ResettableInterface) { + $this->handler->reset(); + } + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + $this->handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + return $this->handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.'); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php new file mode 100644 index 0000000..82e7e4d --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php @@ -0,0 +1,196 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\ChromePHPFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/) + * + * This also works out of the box with Firefox 43+ + * + * @author Christophe Coevoet + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class ChromePHPHandler extends AbstractProcessingHandler +{ + use WebRequestRecognizerTrait; + + /** + * Version of the extension + */ + protected const VERSION = '4.0'; + + /** + * Header name + */ + protected const HEADER_NAME = 'X-ChromeLogger-Data'; + + /** + * Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+) + */ + protected const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}'; + + /** @var bool */ + protected static $initialized = false; + + /** + * Tracks whether we sent too much data + * + * Chrome limits the headers to 4KB, so when we sent 3KB we stop sending + * + * @var bool + */ + protected static $overflowed = false; + + /** @var mixed[] */ + protected static $json = [ + 'version' => self::VERSION, + 'columns' => ['label', 'log', 'backtrace', 'type'], + 'rows' => [], + ]; + + /** @var bool */ + protected static $sendHeaders = true; + + public function __construct($level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + if (!function_exists('json_encode')) { + throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s ChromePHPHandler'); + } + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + if (!$this->isWebRequest()) { + return; + } + + $messages = []; + + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + /** @var Record $message */ + $message = $this->processRecord($record); + $messages[] = $message; + } + + if (!empty($messages)) { + $messages = $this->getFormatter()->formatBatch($messages); + self::$json['rows'] = array_merge(self::$json['rows'], $messages); + $this->send(); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new ChromePHPFormatter(); + } + + /** + * Creates & sends header for a record + * + * @see sendHeader() + * @see send() + */ + protected function write(array $record): void + { + if (!$this->isWebRequest()) { + return; + } + + self::$json['rows'][] = $record['formatted']; + + $this->send(); + } + + /** + * Sends the log header + * + * @see sendHeader() + */ + protected function send(): void + { + if (self::$overflowed || !self::$sendHeaders) { + return; + } + + if (!self::$initialized) { + self::$initialized = true; + + self::$sendHeaders = $this->headersAccepted(); + if (!self::$sendHeaders) { + return; + } + + self::$json['request_uri'] = $_SERVER['REQUEST_URI'] ?? ''; + } + + $json = Utils::jsonEncode(self::$json, Utils::DEFAULT_JSON_FLAGS & ~JSON_UNESCAPED_UNICODE, true); + $data = base64_encode($json); + if (strlen($data) > 3 * 1024) { + self::$overflowed = true; + + $record = [ + 'message' => 'Incomplete logs, chrome header size limit reached', + 'context' => [], + 'level' => Logger::WARNING, + 'level_name' => Logger::getLevelName(Logger::WARNING), + 'channel' => 'monolog', + 'datetime' => new \DateTimeImmutable(), + 'extra' => [], + ]; + self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record); + $json = Utils::jsonEncode(self::$json, Utils::DEFAULT_JSON_FLAGS & ~JSON_UNESCAPED_UNICODE, true); + $data = base64_encode($json); + } + + if (trim($data) !== '') { + $this->sendHeader(static::HEADER_NAME, $data); + } + } + + /** + * Send header string to the client + */ + protected function sendHeader(string $header, string $content): void + { + if (!headers_sent() && self::$sendHeaders) { + header(sprintf('%s: %s', $header, $content)); + } + } + + /** + * Verifies if the headers are accepted by the current user agent + */ + protected function headersAccepted(): bool + { + if (empty($_SERVER['HTTP_USER_AGENT'])) { + return false; + } + + return preg_match(static::USER_AGENT_REGEX, $_SERVER['HTTP_USER_AGENT']) === 1; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php new file mode 100644 index 0000000..94311ea --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\JsonFormatter; +use Monolog\Logger; + +/** + * CouchDB handler + * + * @author Markus Bachmann + */ +class CouchDBHandler extends AbstractProcessingHandler +{ + /** @var mixed[] */ + private $options; + + /** + * @param mixed[] $options + */ + public function __construct(array $options = [], $level = Logger::DEBUG, bool $bubble = true) + { + $this->options = array_merge([ + 'host' => 'localhost', + 'port' => 5984, + 'dbname' => 'logger', + 'username' => null, + 'password' => null, + ], $options); + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $basicAuth = null; + if ($this->options['username']) { + $basicAuth = sprintf('%s:%s@', $this->options['username'], $this->options['password']); + } + + $url = 'http://'.$basicAuth.$this->options['host'].':'.$this->options['port'].'/'.$this->options['dbname']; + $context = stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'content' => $record['formatted'], + 'ignore_errors' => true, + 'max_redirects' => 0, + 'header' => 'Content-type: application/json', + ], + ]); + + if (false === @file_get_contents($url, false, $context)) { + throw new \RuntimeException(sprintf('Could not connect to %s', $url)); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php new file mode 100644 index 0000000..1721d2b --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php @@ -0,0 +1,167 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; + +/** + * Logs to Cube. + * + * @link https://github.com/square/cube/wiki + * @author Wan Chen + * @deprecated Since 2.8.0 and 3.2.0, Cube appears abandoned and thus we will drop this handler in Monolog 4 + */ +class CubeHandler extends AbstractProcessingHandler +{ + /** @var resource|\Socket|null */ + private $udpConnection = null; + /** @var resource|\CurlHandle|null */ + private $httpConnection = null; + /** @var string */ + private $scheme; + /** @var string */ + private $host; + /** @var int */ + private $port; + /** @var string[] */ + private $acceptedSchemes = ['http', 'udp']; + + /** + * Create a Cube handler + * + * @throws \UnexpectedValueException when given url is not a valid url. + * A valid url must consist of three parts : protocol://host:port + * Only valid protocols used by Cube are http and udp + */ + public function __construct(string $url, $level = Logger::DEBUG, bool $bubble = true) + { + $urlInfo = parse_url($url); + + if ($urlInfo === false || !isset($urlInfo['scheme'], $urlInfo['host'], $urlInfo['port'])) { + throw new \UnexpectedValueException('URL "'.$url.'" is not valid'); + } + + if (!in_array($urlInfo['scheme'], $this->acceptedSchemes)) { + throw new \UnexpectedValueException( + 'Invalid protocol (' . $urlInfo['scheme'] . ').' + . ' Valid options are ' . implode(', ', $this->acceptedSchemes) + ); + } + + $this->scheme = $urlInfo['scheme']; + $this->host = $urlInfo['host']; + $this->port = (int) $urlInfo['port']; + + parent::__construct($level, $bubble); + } + + /** + * Establish a connection to an UDP socket + * + * @throws \LogicException when unable to connect to the socket + * @throws MissingExtensionException when there is no socket extension + */ + protected function connectUdp(): void + { + if (!extension_loaded('sockets')) { + throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler'); + } + + $udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0); + if (false === $udpConnection) { + throw new \LogicException('Unable to create a socket'); + } + + $this->udpConnection = $udpConnection; + if (!socket_connect($this->udpConnection, $this->host, $this->port)) { + throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port); + } + } + + /** + * Establish a connection to an http server + * + * @throws \LogicException when unable to connect to the socket + * @throws MissingExtensionException when no curl extension + */ + protected function connectHttp(): void + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is required to use http URLs with the CubeHandler'); + } + + $httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put'); + if (false === $httpConnection) { + throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port); + } + + $this->httpConnection = $httpConnection; + curl_setopt($this->httpConnection, CURLOPT_CUSTOMREQUEST, "POST"); + curl_setopt($this->httpConnection, CURLOPT_RETURNTRANSFER, true); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $date = $record['datetime']; + + $data = ['time' => $date->format('Y-m-d\TH:i:s.uO')]; + unset($record['datetime']); + + if (isset($record['context']['type'])) { + $data['type'] = $record['context']['type']; + unset($record['context']['type']); + } else { + $data['type'] = $record['channel']; + } + + $data['data'] = $record['context']; + $data['data']['level'] = $record['level']; + + if ($this->scheme === 'http') { + $this->writeHttp(Utils::jsonEncode($data)); + } else { + $this->writeUdp(Utils::jsonEncode($data)); + } + } + + private function writeUdp(string $data): void + { + if (!$this->udpConnection) { + $this->connectUdp(); + } + + socket_send($this->udpConnection, $data, strlen($data), 0); + } + + private function writeHttp(string $data): void + { + if (!$this->httpConnection) { + $this->connectHttp(); + } + + if (null === $this->httpConnection) { + throw new \LogicException('No connection could be established'); + } + + curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']'); + curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, [ + 'Content-Type: application/json', + 'Content-Length: ' . strlen('['.$data.']'), + ]); + + Curl\Util::execute($this->httpConnection, 5, false); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php new file mode 100644 index 0000000..4f6aa29 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\Curl; + +use CurlHandle; + +/** + * This class is marked as internal and it is not under the BC promise of the package. + * + * @internal + */ +final class Util +{ + /** @var array */ + private static $retriableErrorCodes = [ + CURLE_COULDNT_RESOLVE_HOST, + CURLE_COULDNT_CONNECT, + CURLE_HTTP_NOT_FOUND, + CURLE_READ_ERROR, + CURLE_OPERATION_TIMEOUTED, + CURLE_HTTP_POST_ERROR, + CURLE_SSL_CONNECT_ERROR, + ]; + + /** + * Executes a CURL request with optional retries and exception on failure + * + * @param resource|CurlHandle $ch curl handler + * @param int $retries + * @param bool $closeAfterDone + * @return bool|string @see curl_exec + */ + public static function execute($ch, int $retries = 5, bool $closeAfterDone = true) + { + while ($retries--) { + $curlResponse = curl_exec($ch); + if ($curlResponse === false) { + $curlErrno = curl_errno($ch); + + if (false === in_array($curlErrno, self::$retriableErrorCodes, true) || !$retries) { + $curlError = curl_error($ch); + + if ($closeAfterDone) { + curl_close($ch); + } + + throw new \RuntimeException(sprintf('Curl error (code %d): %s', $curlErrno, $curlError)); + } + + continue; + } + + if ($closeAfterDone) { + curl_close($ch); + } + + return $curlResponse; + } + + return false; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php new file mode 100644 index 0000000..76b86c9 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Simple handler wrapper that deduplicates log records across multiple requests + * + * It also includes the BufferHandler functionality and will buffer + * all messages until the end of the request or flush() is called. + * + * This works by storing all log records' messages above $deduplicationLevel + * to the file specified by $deduplicationStore. When further logs come in at the end of the + * request (or when flush() is called), all those above $deduplicationLevel are checked + * against the existing stored logs. If they match and the timestamps in the stored log is + * not older than $time seconds, the new log record is discarded. If no log record is new, the + * whole data set is discarded. + * + * This is mainly useful in combination with Mail handlers or things like Slack or HipChat handlers + * that send messages to people, to avoid spamming with the same message over and over in case of + * a major component failure like a database server being down which makes all requests fail in the + * same way. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +class DeduplicationHandler extends BufferHandler +{ + /** + * @var string + */ + protected $deduplicationStore; + + /** + * @var Level + */ + protected $deduplicationLevel; + + /** + * @var int + */ + protected $time; + + /** + * @var bool + */ + private $gc = false; + + /** + * @param HandlerInterface $handler Handler. + * @param string $deduplicationStore The file/path where the deduplication log should be kept + * @param string|int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes + * @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * + * @phpstan-param Level|LevelName|LogLevel::* $deduplicationLevel + */ + public function __construct(HandlerInterface $handler, ?string $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, int $time = 60, bool $bubble = true) + { + parent::__construct($handler, 0, Logger::DEBUG, $bubble, false); + + $this->deduplicationStore = $deduplicationStore === null ? sys_get_temp_dir() . '/monolog-dedup-' . substr(md5(__FILE__), 0, 20) .'.log' : $deduplicationStore; + $this->deduplicationLevel = Logger::toMonologLevel($deduplicationLevel); + $this->time = $time; + } + + public function flush(): void + { + if ($this->bufferSize === 0) { + return; + } + + $passthru = null; + + foreach ($this->buffer as $record) { + if ($record['level'] >= $this->deduplicationLevel) { + $passthru = $passthru || !$this->isDuplicate($record); + if ($passthru) { + $this->appendRecord($record); + } + } + } + + // default of null is valid as well as if no record matches duplicationLevel we just pass through + if ($passthru === true || $passthru === null) { + $this->handler->handleBatch($this->buffer); + } + + $this->clear(); + + if ($this->gc) { + $this->collectLogs(); + } + } + + /** + * @phpstan-param Record $record + */ + private function isDuplicate(array $record): bool + { + if (!file_exists($this->deduplicationStore)) { + return false; + } + + $store = file($this->deduplicationStore, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if (!is_array($store)) { + return false; + } + + $yesterday = time() - 86400; + $timestampValidity = $record['datetime']->getTimestamp() - $this->time; + $expectedMessage = preg_replace('{[\r\n].*}', '', $record['message']); + + for ($i = count($store) - 1; $i >= 0; $i--) { + list($timestamp, $level, $message) = explode(':', $store[$i], 3); + + if ($level === $record['level_name'] && $message === $expectedMessage && $timestamp > $timestampValidity) { + return true; + } + + if ($timestamp < $yesterday) { + $this->gc = true; + } + } + + return false; + } + + private function collectLogs(): void + { + if (!file_exists($this->deduplicationStore)) { + return; + } + + $handle = fopen($this->deduplicationStore, 'rw+'); + + if (!$handle) { + throw new \RuntimeException('Failed to open file for reading and writing: ' . $this->deduplicationStore); + } + + flock($handle, LOCK_EX); + $validLogs = []; + + $timestampValidity = time() - $this->time; + + while (!feof($handle)) { + $log = fgets($handle); + if ($log && substr($log, 0, 10) >= $timestampValidity) { + $validLogs[] = $log; + } + } + + ftruncate($handle, 0); + rewind($handle); + foreach ($validLogs as $log) { + fwrite($handle, $log); + } + + flock($handle, LOCK_UN); + fclose($handle); + + $this->gc = false; + } + + /** + * @phpstan-param Record $record + */ + private function appendRecord(array $record): void + { + file_put_contents($this->deduplicationStore, $record['datetime']->getTimestamp() . ':' . $record['level_name'] . ':' . preg_replace('{[\r\n].*}', '', $record['message']) . "\n", FILE_APPEND); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php new file mode 100644 index 0000000..9d21be8 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Formatter\FormatterInterface; +use Doctrine\CouchDB\CouchDBClient; + +/** + * CouchDB handler for Doctrine CouchDB ODM + * + * @author Markus Bachmann + */ +class DoctrineCouchDBHandler extends AbstractProcessingHandler +{ + /** @var CouchDBClient */ + private $client; + + public function __construct(CouchDBClient $client, $level = Logger::DEBUG, bool $bubble = true) + { + $this->client = $client; + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->client->postDocument($record['formatted']); + } + + protected function getDefaultFormatter(): FormatterInterface + { + return new NormalizerFormatter; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php new file mode 100644 index 0000000..36cb7d3 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Aws\Sdk; +use Aws\DynamoDb\DynamoDbClient; +use Monolog\Formatter\FormatterInterface; +use Aws\DynamoDb\Marshaler; +use Monolog\Formatter\ScalarFormatter; +use Monolog\Logger; + +/** + * Amazon DynamoDB handler (http://aws.amazon.com/dynamodb/) + * + * @link https://github.com/aws/aws-sdk-php/ + * @author Andrew Lawson + */ +class DynamoDbHandler extends AbstractProcessingHandler +{ + public const DATE_FORMAT = 'Y-m-d\TH:i:s.uO'; + + /** + * @var DynamoDbClient + */ + protected $client; + + /** + * @var string + */ + protected $table; + + /** + * @var int + */ + protected $version; + + /** + * @var Marshaler + */ + protected $marshaler; + + public function __construct(DynamoDbClient $client, string $table, $level = Logger::DEBUG, bool $bubble = true) + { + /** @phpstan-ignore-next-line */ + if (defined('Aws\Sdk::VERSION') && version_compare(Sdk::VERSION, '3.0', '>=')) { + $this->version = 3; + $this->marshaler = new Marshaler; + } else { + $this->version = 2; + } + + $this->client = $client; + $this->table = $table; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $filtered = $this->filterEmptyFields($record['formatted']); + if ($this->version === 3) { + $formatted = $this->marshaler->marshalItem($filtered); + } else { + /** @phpstan-ignore-next-line */ + $formatted = $this->client->formatAttributes($filtered); + } + + $this->client->putItem([ + 'TableName' => $this->table, + 'Item' => $formatted, + ]); + } + + /** + * @param mixed[] $record + * @return mixed[] + */ + protected function filterEmptyFields(array $record): array + { + return array_filter($record, function ($value) { + return !empty($value) || false === $value || 0 === $value; + }); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new ScalarFormatter(self::DATE_FORMAT); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php new file mode 100644 index 0000000..9afafc8 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Elastica\Document; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\ElasticaFormatter; +use Monolog\Logger; +use Elastica\Client; +use Elastica\Exception\ExceptionInterface; + +/** + * Elastic Search handler + * + * Usage example: + * + * $client = new \Elastica\Client(); + * $options = array( + * 'index' => 'elastic_index_name', + * 'type' => 'elastic_doc_type', Types have been removed in Elastica 7 + * ); + * $handler = new ElasticaHandler($client, $options); + * $log = new Logger('application'); + * $log->pushHandler($handler); + * + * @author Jelle Vink + */ +class ElasticaHandler extends AbstractProcessingHandler +{ + /** + * @var Client + */ + protected $client; + + /** + * @var mixed[] Handler config options + */ + protected $options = []; + + /** + * @param Client $client Elastica Client object + * @param mixed[] $options Handler configuration + */ + public function __construct(Client $client, array $options = [], $level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + $this->client = $client; + $this->options = array_merge( + [ + 'index' => 'monolog', // Elastic index name + 'type' => 'record', // Elastic document type + 'ignore_error' => false, // Suppress Elastica exceptions + ], + $options + ); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->bulkSend([$record['formatted']]); + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($formatter instanceof ElasticaFormatter) { + return parent::setFormatter($formatter); + } + + throw new \InvalidArgumentException('ElasticaHandler is only compatible with ElasticaFormatter'); + } + + /** + * @return mixed[] + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new ElasticaFormatter($this->options['index'], $this->options['type']); + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $documents = $this->getFormatter()->formatBatch($records); + $this->bulkSend($documents); + } + + /** + * Use Elasticsearch bulk API to send list of documents + * + * @param Document[] $documents + * + * @throws \RuntimeException + */ + protected function bulkSend(array $documents): void + { + try { + $this->client->addDocuments($documents); + } catch (ExceptionInterface $e) { + if (!$this->options['ignore_error']) { + throw new \RuntimeException("Error sending messages to Elasticsearch", 0, $e); + } + } + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php new file mode 100644 index 0000000..2a9ebdb --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php @@ -0,0 +1,218 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Elastic\Elasticsearch\Response\Elasticsearch; +use Throwable; +use RuntimeException; +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\ElasticsearchFormatter; +use InvalidArgumentException; +use Elasticsearch\Common\Exceptions\RuntimeException as ElasticsearchRuntimeException; +use Elasticsearch\Client; +use Elastic\Elasticsearch\Exception\InvalidArgumentException as ElasticInvalidArgumentException; +use Elastic\Elasticsearch\Client as Client8; + +/** + * Elasticsearch handler + * + * @link https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/index.html + * + * Simple usage example: + * + * $client = \Elasticsearch\ClientBuilder::create() + * ->setHosts($hosts) + * ->build(); + * + * $options = array( + * 'index' => 'elastic_index_name', + * 'type' => 'elastic_doc_type', + * ); + * $handler = new ElasticsearchHandler($client, $options); + * $log = new Logger('application'); + * $log->pushHandler($handler); + * + * @author Avtandil Kikabidze + */ +class ElasticsearchHandler extends AbstractProcessingHandler +{ + /** + * @var Client|Client8 + */ + protected $client; + + /** + * @var mixed[] Handler config options + */ + protected $options = []; + + /** + * @var bool + */ + private $needsType; + + /** + * @param Client|Client8 $client Elasticsearch Client object + * @param mixed[] $options Handler configuration + */ + public function __construct($client, array $options = [], $level = Logger::DEBUG, bool $bubble = true) + { + if (!$client instanceof Client && !$client instanceof Client8) { + throw new \TypeError('Elasticsearch\Client or Elastic\Elasticsearch\Client instance required'); + } + + parent::__construct($level, $bubble); + $this->client = $client; + $this->options = array_merge( + [ + 'index' => 'monolog', // Elastic index name + 'type' => '_doc', // Elastic document type + 'ignore_error' => false, // Suppress Elasticsearch exceptions + ], + $options + ); + + if ($client instanceof Client8 || $client::VERSION[0] === '7') { + $this->needsType = false; + // force the type to _doc for ES8/ES7 + $this->options['type'] = '_doc'; + } else { + $this->needsType = true; + } + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->bulkSend([$record['formatted']]); + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($formatter instanceof ElasticsearchFormatter) { + return parent::setFormatter($formatter); + } + + throw new InvalidArgumentException('ElasticsearchHandler is only compatible with ElasticsearchFormatter'); + } + + /** + * Getter options + * + * @return mixed[] + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new ElasticsearchFormatter($this->options['index'], $this->options['type']); + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $documents = $this->getFormatter()->formatBatch($records); + $this->bulkSend($documents); + } + + /** + * Use Elasticsearch bulk API to send list of documents + * + * @param array[] $records Records + _index/_type keys + * @throws \RuntimeException + */ + protected function bulkSend(array $records): void + { + try { + $params = [ + 'body' => [], + ]; + + foreach ($records as $record) { + $params['body'][] = [ + 'index' => $this->needsType ? [ + '_index' => $record['_index'], + '_type' => $record['_type'], + ] : [ + '_index' => $record['_index'], + ], + ]; + unset($record['_index'], $record['_type']); + + $params['body'][] = $record; + } + + /** @var Elasticsearch */ + $responses = $this->client->bulk($params); + + if ($responses['errors'] === true) { + throw $this->createExceptionFromResponses($responses); + } + } catch (Throwable $e) { + if (! $this->options['ignore_error']) { + throw new RuntimeException('Error sending messages to Elasticsearch', 0, $e); + } + } + } + + /** + * Creates elasticsearch exception from responses array + * + * Only the first error is converted into an exception. + * + * @param mixed[]|Elasticsearch $responses returned by $this->client->bulk() + */ + protected function createExceptionFromResponses($responses): Throwable + { + foreach ($responses['items'] ?? [] as $item) { + if (isset($item['index']['error'])) { + return $this->createExceptionFromError($item['index']['error']); + } + } + + if (class_exists(ElasticInvalidArgumentException::class)) { + return new ElasticInvalidArgumentException('Elasticsearch failed to index one or more records.'); + } + + return new ElasticsearchRuntimeException('Elasticsearch failed to index one or more records.'); + } + + /** + * Creates elasticsearch exception from error array + * + * @param mixed[] $error + */ + protected function createExceptionFromError(array $error): Throwable + { + $previous = isset($error['caused_by']) ? $this->createExceptionFromError($error['caused_by']) : null; + + if (class_exists(ElasticInvalidArgumentException::class)) { + return new ElasticInvalidArgumentException($error['type'] . ': ' . $error['reason'], 0, $previous); + } + + return new ElasticsearchRuntimeException($error['type'] . ': ' . $error['reason'], 0, $previous); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php new file mode 100644 index 0000000..6d18c86 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Stores to PHP error_log() handler. + * + * @author Elan Ruusamäe + */ +class ErrorLogHandler extends AbstractProcessingHandler +{ + public const OPERATING_SYSTEM = 0; + public const SAPI = 4; + + /** @var int */ + protected $messageType; + /** @var bool */ + protected $expandNewlines; + + /** + * @param int $messageType Says where the error should go. + * @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries + */ + public function __construct(int $messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, bool $bubble = true, bool $expandNewlines = false) + { + parent::__construct($level, $bubble); + + if (false === in_array($messageType, self::getAvailableTypes(), true)) { + $message = sprintf('The given message type "%s" is not supported', print_r($messageType, true)); + + throw new \InvalidArgumentException($message); + } + + $this->messageType = $messageType; + $this->expandNewlines = $expandNewlines; + } + + /** + * @return int[] With all available types + */ + public static function getAvailableTypes(): array + { + return [ + self::OPERATING_SYSTEM, + self::SAPI, + ]; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter('[%datetime%] %channel%.%level_name%: %message% %context% %extra%'); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!$this->expandNewlines) { + error_log((string) $record['formatted'], $this->messageType); + + return; + } + + $lines = preg_split('{[\r\n]+}', (string) $record['formatted']); + if ($lines === false) { + $pcreErrorCode = preg_last_error(); + throw new \RuntimeException('Failed to preg_split formatted string: ' . $pcreErrorCode . ' / '. Utils::pcreLastErrorMessage($pcreErrorCode)); + } + foreach ($lines as $line) { + error_log($line, $this->messageType); + } + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php new file mode 100644 index 0000000..5920b45 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Throwable; + +/** + * Forwards records to at most one handler + * + * If a handler fails, the exception is suppressed and the record is forwarded to the next handler. + * + * As soon as one handler handles a record successfully, the handling stops there. + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class FallbackGroupHandler extends GroupHandler +{ + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + foreach ($this->handlers as $handler) { + try { + $handler->handle($record); + break; + } catch (Throwable $e) { + // What throwable? + } + } + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + if ($this->processors) { + $processed = []; + foreach ($records as $record) { + $processed[] = $this->processRecord($record); + } + /** @var Record[] $records */ + $records = $processed; + } + + foreach ($this->handlers as $handler) { + try { + $handler->handleBatch($records); + break; + } catch (Throwable $e) { + // What throwable? + } + } + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php new file mode 100644 index 0000000..2b5949c --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php @@ -0,0 +1,212 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\ResettableInterface; +use Monolog\Formatter\FormatterInterface; +use Psr\Log\LogLevel; + +/** + * Simple handler wrapper that filters records based on a list of levels + * + * It can be configured with an exact list of levels to allow, or a min/max level. + * + * @author Hennadiy Verkh + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class FilterHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + + /** + * Handler or factory callable($record, $this) + * + * @var callable|HandlerInterface + * @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface + */ + protected $handler; + + /** + * Minimum level for logs that are passed to handler + * + * @var int[] + * @phpstan-var array + */ + protected $acceptedLevels; + + /** + * Whether the messages that are handled can bubble up the stack or not + * + * @var bool + */ + protected $bubble; + + /** + * @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler + * + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler). + * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided + * @param int|string $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * + * @phpstan-param Level|LevelName|LogLevel::*|array $minLevelOrList + * @phpstan-param Level|LevelName|LogLevel::* $maxLevel + */ + public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, bool $bubble = true) + { + $this->handler = $handler; + $this->bubble = $bubble; + $this->setAcceptedLevels($minLevelOrList, $maxLevel); + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + /** + * @phpstan-return array + */ + public function getAcceptedLevels(): array + { + return array_flip($this->acceptedLevels); + } + + /** + * @param int|string|array $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided + * @param int|string $maxLevel Maximum level or level name to accept, only used if $minLevelOrList is not an array + * + * @phpstan-param Level|LevelName|LogLevel::*|array $minLevelOrList + * @phpstan-param Level|LevelName|LogLevel::* $maxLevel + */ + public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY): self + { + if (is_array($minLevelOrList)) { + $acceptedLevels = array_map('Monolog\Logger::toMonologLevel', $minLevelOrList); + } else { + $minLevelOrList = Logger::toMonologLevel($minLevelOrList); + $maxLevel = Logger::toMonologLevel($maxLevel); + $acceptedLevels = array_values(array_filter(Logger::getLevels(), function ($level) use ($minLevelOrList, $maxLevel) { + return $level >= $minLevelOrList && $level <= $maxLevel; + })); + } + $this->acceptedLevels = array_flip($acceptedLevels); + + return $this; + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return isset($this->acceptedLevels[$record['level']]); + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if (!$this->isHandling($record)) { + return false; + } + + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + $this->getHandler($record)->handle($record); + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $filtered = []; + foreach ($records as $record) { + if ($this->isHandling($record)) { + $filtered[] = $record; + } + } + + if (count($filtered) > 0) { + $this->getHandler($filtered[count($filtered) - 1])->handleBatch($filtered); + } + } + + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @return HandlerInterface + * + * @phpstan-param Record $record + */ + public function getHandler(array $record = null) + { + if (!$this->handler instanceof HandlerInterface) { + $this->handler = ($this->handler)($record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + return $this->handler; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + $handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + return $handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } + + public function reset() + { + $this->resetProcessors(); + + if ($this->getHandler() instanceof ResettableInterface) { + $this->getHandler()->reset(); + } + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php new file mode 100644 index 0000000..e93884d --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +/** + * Interface for activation strategies for the FingersCrossedHandler. + * + * @author Johannes M. Schmitt + * + * @phpstan-import-type Record from \Monolog\Logger + */ +interface ActivationStrategyInterface +{ + /** + * Returns whether the given record activates the handler. + * + * @phpstan-param Record $record + */ + public function isHandlerActivated(array $record): bool; +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php new file mode 100644 index 0000000..86ee8b9 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Channel and Error level based monolog activation strategy. Allows to trigger activation + * based on level per channel. e.g. trigger activation on level 'ERROR' by default, except + * for records of the 'sql' channel; those should trigger activation on level 'WARN'. + * + * Example: + * + * + * $activationStrategy = new ChannelLevelActivationStrategy( + * Logger::CRITICAL, + * array( + * 'request' => Logger::ALERT, + * 'sensitive' => Logger::ERROR, + * ) + * ); + * $handler = new FingersCrossedHandler(new StreamHandler('php://stderr'), $activationStrategy); + * + * + * @author Mike Meessen + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class ChannelLevelActivationStrategy implements ActivationStrategyInterface +{ + /** + * @var Level + */ + private $defaultActionLevel; + + /** + * @var array + */ + private $channelToActionLevel; + + /** + * @param int|string $defaultActionLevel The default action level to be used if the record's category doesn't match any + * @param array $channelToActionLevel An array that maps channel names to action levels. + * + * @phpstan-param array $channelToActionLevel + * @phpstan-param Level|LevelName|LogLevel::* $defaultActionLevel + */ + public function __construct($defaultActionLevel, array $channelToActionLevel = []) + { + $this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel); + $this->channelToActionLevel = array_map('Monolog\Logger::toMonologLevel', $channelToActionLevel); + } + + /** + * @phpstan-param Record $record + */ + public function isHandlerActivated(array $record): bool + { + if (isset($this->channelToActionLevel[$record['channel']])) { + return $record['level'] >= $this->channelToActionLevel[$record['channel']]; + } + + return $record['level'] >= $this->defaultActionLevel; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php new file mode 100644 index 0000000..fd82761 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Error level based activation strategy. + * + * @author Johannes M. Schmitt + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class ErrorLevelActivationStrategy implements ActivationStrategyInterface +{ + /** + * @var Level + */ + private $actionLevel; + + /** + * @param int|string $actionLevel Level or name or value + * + * @phpstan-param Level|LevelName|LogLevel::* $actionLevel + */ + public function __construct($actionLevel) + { + $this->actionLevel = Logger::toMonologLevel($actionLevel); + } + + public function isHandlerActivated(array $record): bool + { + return $record['level'] >= $this->actionLevel; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php new file mode 100644 index 0000000..2e3da84 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php @@ -0,0 +1,252 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; +use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; +use Monolog\Logger; +use Monolog\ResettableInterface; +use Monolog\Formatter\FormatterInterface; +use Psr\Log\LogLevel; + +/** + * Buffers all records until a certain level is reached + * + * The advantage of this approach is that you don't get any clutter in your log files. + * Only requests which actually trigger an error (or whatever your actionLevel is) will be + * in the logs, but they will contain all records, not only those above the level threshold. + * + * You can then have a passthruLevel as well which means that at the end of the request, + * even if it did not get activated, it will still send through log records of e.g. at least a + * warning level. + * + * You can find the various activation strategies in the + * Monolog\Handler\FingersCrossed\ namespace. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class FingersCrossedHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + + /** + * @var callable|HandlerInterface + * @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface + */ + protected $handler; + /** @var ActivationStrategyInterface */ + protected $activationStrategy; + /** @var bool */ + protected $buffering = true; + /** @var int */ + protected $bufferSize; + /** @var Record[] */ + protected $buffer = []; + /** @var bool */ + protected $stopBuffering; + /** + * @var ?int + * @phpstan-var ?Level + */ + protected $passthruLevel; + /** @var bool */ + protected $bubble; + + /** + * @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler + * + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler). + * @param int|string|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated + * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true) + * @param int|string $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $passthruLevel + * @phpstan-param Level|LevelName|LogLevel::*|ActivationStrategyInterface $activationStrategy + */ + public function __construct($handler, $activationStrategy = null, int $bufferSize = 0, bool $bubble = true, bool $stopBuffering = true, $passthruLevel = null) + { + if (null === $activationStrategy) { + $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING); + } + + // convert simple int activationStrategy to an object + if (!$activationStrategy instanceof ActivationStrategyInterface) { + $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy); + } + + $this->handler = $handler; + $this->activationStrategy = $activationStrategy; + $this->bufferSize = $bufferSize; + $this->bubble = $bubble; + $this->stopBuffering = $stopBuffering; + + if ($passthruLevel !== null) { + $this->passthruLevel = Logger::toMonologLevel($passthruLevel); + } + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return true; + } + + /** + * Manually activate this logger regardless of the activation strategy + */ + public function activate(): void + { + if ($this->stopBuffering) { + $this->buffering = false; + } + + $this->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer); + $this->buffer = []; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + if ($this->buffering) { + $this->buffer[] = $record; + if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { + array_shift($this->buffer); + } + if ($this->activationStrategy->isHandlerActivated($record)) { + $this->activate(); + } + } else { + $this->getHandler($record)->handle($record); + } + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + $this->flushBuffer(); + + $this->getHandler()->close(); + } + + public function reset() + { + $this->flushBuffer(); + + $this->resetProcessors(); + + if ($this->getHandler() instanceof ResettableInterface) { + $this->getHandler()->reset(); + } + } + + /** + * Clears the buffer without flushing any messages down to the wrapped handler. + * + * It also resets the handler to its initial buffering state. + */ + public function clear(): void + { + $this->buffer = []; + $this->reset(); + } + + /** + * Resets the state of the handler. Stops forwarding records to the wrapped handler. + */ + private function flushBuffer(): void + { + if (null !== $this->passthruLevel) { + $level = $this->passthruLevel; + $this->buffer = array_filter($this->buffer, function ($record) use ($level) { + return $record['level'] >= $level; + }); + if (count($this->buffer) > 0) { + $this->getHandler(end($this->buffer))->handleBatch($this->buffer); + } + } + + $this->buffer = []; + $this->buffering = true; + } + + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @return HandlerInterface + * + * @phpstan-param Record $record + */ + public function getHandler(array $record = null) + { + if (!$this->handler instanceof HandlerInterface) { + $this->handler = ($this->handler)($record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + return $this->handler; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + $handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + return $handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php new file mode 100644 index 0000000..fe202f7 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\WildfireFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol. + * + * @author Eric Clemmons (@ericclemmons) + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class FirePHPHandler extends AbstractProcessingHandler +{ + use WebRequestRecognizerTrait; + + /** + * WildFire JSON header message format + */ + protected const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2'; + + /** + * FirePHP structure for parsing messages & their presentation + */ + protected const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'; + + /** + * Must reference a "known" plugin, otherwise headers won't display in FirePHP + */ + protected const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3'; + + /** + * Header prefix for Wildfire to recognize & parse headers + */ + protected const HEADER_PREFIX = 'X-Wf'; + + /** + * Whether or not Wildfire vendor-specific headers have been generated & sent yet + * @var bool + */ + protected static $initialized = false; + + /** + * Shared static message index between potentially multiple handlers + * @var int + */ + protected static $messageIndex = 1; + + /** @var bool */ + protected static $sendHeaders = true; + + /** + * Base header creation function used by init headers & record headers + * + * @param array $meta Wildfire Plugin, Protocol & Structure Indexes + * @param string $message Log message + * + * @return array Complete header string ready for the client as key and message as value + * + * @phpstan-return non-empty-array + */ + protected function createHeader(array $meta, string $message): array + { + $header = sprintf('%s-%s', static::HEADER_PREFIX, join('-', $meta)); + + return [$header => $message]; + } + + /** + * Creates message header from record + * + * @return array + * + * @phpstan-return non-empty-array + * + * @see createHeader() + * + * @phpstan-param FormattedRecord $record + */ + protected function createRecordHeader(array $record): array + { + // Wildfire is extensible to support multiple protocols & plugins in a single request, + // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake. + return $this->createHeader( + [1, 1, 1, self::$messageIndex++], + $record['formatted'] + ); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new WildfireFormatter(); + } + + /** + * Wildfire initialization headers to enable message parsing + * + * @see createHeader() + * @see sendHeader() + * + * @return array + */ + protected function getInitHeaders(): array + { + // Initial payload consists of required headers for Wildfire + return array_merge( + $this->createHeader(['Protocol', 1], static::PROTOCOL_URI), + $this->createHeader([1, 'Structure', 1], static::STRUCTURE_URI), + $this->createHeader([1, 'Plugin', 1], static::PLUGIN_URI) + ); + } + + /** + * Send header string to the client + */ + protected function sendHeader(string $header, string $content): void + { + if (!headers_sent() && self::$sendHeaders) { + header(sprintf('%s: %s', $header, $content)); + } + } + + /** + * Creates & sends header for a record, ensuring init headers have been sent prior + * + * @see sendHeader() + * @see sendInitHeaders() + */ + protected function write(array $record): void + { + if (!self::$sendHeaders || !$this->isWebRequest()) { + return; + } + + // WildFire-specific headers must be sent prior to any messages + if (!self::$initialized) { + self::$initialized = true; + + self::$sendHeaders = $this->headersAccepted(); + if (!self::$sendHeaders) { + return; + } + + foreach ($this->getInitHeaders() as $header => $content) { + $this->sendHeader($header, $content); + } + } + + $header = $this->createRecordHeader($record); + if (trim(current($header)) !== '') { + $this->sendHeader(key($header), current($header)); + } + } + + /** + * Verifies if the headers are accepted by the current user agent + */ + protected function headersAccepted(): bool + { + if (!empty($_SERVER['HTTP_USER_AGENT']) && preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) { + return true; + } + + return isset($_SERVER['HTTP_X_FIREPHP_VERSION']); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php new file mode 100644 index 0000000..0846dda --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; +use Monolog\Logger; + +/** + * Sends logs to Fleep.io using Webhook integrations + * + * You'll need a Fleep.io account to use this handler. + * + * @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation + * @author Ando Roots + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class FleepHookHandler extends SocketHandler +{ + protected const FLEEP_HOST = 'fleep.io'; + + protected const FLEEP_HOOK_URI = '/hook/'; + + /** + * @var string Webhook token (specifies the conversation where logs are sent) + */ + protected $token; + + /** + * Construct a new Fleep.io Handler. + * + * For instructions on how to create a new web hook in your conversations + * see https://fleep.io/integrations/webhooks/ + * + * @param string $token Webhook token + * @throws MissingExtensionException + */ + public function __construct( + string $token, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FleepHookHandler'); + } + + $this->token = $token; + + $connectionString = 'ssl://' . static::FLEEP_HOST . ':443'; + parent::__construct( + $connectionString, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + } + + /** + * Returns the default formatter to use with this handler + * + * Overloaded to remove empty context and extra arrays from the end of the log message. + * + * @return LineFormatter + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter(null, null, true, true); + } + + /** + * Handles a log record + */ + public function write(array $record): void + { + parent::write($record); + $this->closeSocket(); + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the header of the API Call + */ + private function buildHeader(string $content): string + { + $header = "POST " . static::FLEEP_HOOK_URI . $this->token . " HTTP/1.1\r\n"; + $header .= "Host: " . static::FLEEP_HOST . "\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + /** + * Builds the body of API call + * + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record): string + { + $dataArray = [ + 'message' => $record['formatted'], + ]; + + return http_build_query($dataArray); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php new file mode 100644 index 0000000..3a4de29 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\FlowdockFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Sends notifications through the Flowdock push API + * + * This must be configured with a FlowdockFormatter instance via setFormatter() + * + * Notes: + * API token - Flowdock API token + * + * @author Dominik Liebler + * @see https://www.flowdock.com/api/push + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class FlowdockHandler extends SocketHandler +{ + /** + * @var string + */ + protected $apiToken; + + /** + * @throws MissingExtensionException if OpenSSL is missing + */ + public function __construct( + string $apiToken, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FlowdockHandler'); + } + + parent::__construct( + 'ssl://api.flowdock.com:443', + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + $this->apiToken = $apiToken; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if (!$formatter instanceof FlowdockFormatter) { + throw new \InvalidArgumentException('The FlowdockHandler requires an instance of Monolog\Formatter\FlowdockFormatter to function correctly'); + } + + return parent::setFormatter($formatter); + } + + /** + * Gets the default formatter. + */ + protected function getDefaultFormatter(): FormatterInterface + { + throw new \InvalidArgumentException('The FlowdockHandler must be configured (via setFormatter) with an instance of Monolog\Formatter\FlowdockFormatter to function correctly'); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + parent::write($record); + + $this->closeSocket(); + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the body of API call + * + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record): string + { + return Utils::jsonEncode($record['formatted']['flowdock']); + } + + /** + * Builds the header of the API Call + */ + private function buildHeader(string $content): string + { + $header = "POST /v1/messages/team_inbox/" . $this->apiToken . " HTTP/1.1\r\n"; + $header .= "Host: api.flowdock.com\r\n"; + $header .= "Content-Type: application/json\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php new file mode 100644 index 0000000..ee7b4ef --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; + +/** + * Interface to describe loggers that have a formatter + * + * @author Jordi Boggiano + */ +interface FormattableHandlerInterface +{ + /** + * Sets the formatter. + * + * @param FormatterInterface $formatter + * @return HandlerInterface self + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface; + + /** + * Gets the formatter. + * + * @return FormatterInterface + */ + public function getFormatter(): FormatterInterface; +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php new file mode 100644 index 0000000..9628870 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; + +/** + * Helper trait for implementing FormattableInterface + * + * @author Jordi Boggiano + */ +trait FormattableHandlerTrait +{ + /** + * @var ?FormatterInterface + */ + protected $formatter; + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $this->formatter = $formatter; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + if (!$this->formatter) { + $this->formatter = $this->getDefaultFormatter(); + } + + return $this->formatter; + } + + /** + * Gets the default formatter. + * + * Overwrite this if the LineFormatter is not a good default for your handler. + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter(); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php new file mode 100644 index 0000000..fe95dfe --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Gelf\PublisherInterface; +use Monolog\Logger; +use Monolog\Formatter\GelfMessageFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Handler to send messages to a Graylog2 (http://www.graylog2.org) server + * + * @author Matt Lehner + * @author Benjamin Zikarsky + */ +class GelfHandler extends AbstractProcessingHandler +{ + /** + * @var PublisherInterface the publisher object that sends the message to the server + */ + protected $publisher; + + /** + * @param PublisherInterface $publisher a gelf publisher object + */ + public function __construct(PublisherInterface $publisher, $level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + + $this->publisher = $publisher; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->publisher->publish($record['formatted']); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new GelfMessageFormatter(); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php new file mode 100644 index 0000000..4c2cbbe --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\ResettableInterface; + +/** + * Forwards records to multiple handlers + * + * @author Lenar Lõhmus + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class GroupHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface +{ + use ProcessableHandlerTrait; + + /** @var HandlerInterface[] */ + protected $handlers; + /** @var bool */ + protected $bubble; + + /** + * @param HandlerInterface[] $handlers Array of Handlers. + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(array $handlers, bool $bubble = true) + { + foreach ($handlers as $handler) { + if (!$handler instanceof HandlerInterface) { + throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.'); + } + } + + $this->handlers = $handlers; + $this->bubble = $bubble; + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + foreach ($this->handlers as $handler) { + $handler->handle($record); + } + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + if ($this->processors) { + $processed = []; + foreach ($records as $record) { + $processed[] = $this->processRecord($record); + } + /** @var Record[] $records */ + $records = $processed; + } + + foreach ($this->handlers as $handler) { + $handler->handleBatch($records); + } + } + + public function reset() + { + $this->resetProcessors(); + + foreach ($this->handlers as $handler) { + if ($handler instanceof ResettableInterface) { + $handler->reset(); + } + } + } + + public function close(): void + { + parent::close(); + + foreach ($this->handlers as $handler) { + $handler->close(); + } + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + foreach ($this->handlers as $handler) { + if ($handler instanceof FormattableHandlerInterface) { + $handler->setFormatter($formatter); + } + } + + return $this; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/Handler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/Handler.php new file mode 100644 index 0000000..ad11f0e --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/Handler.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Base Handler class providing basic close() support as well as handleBatch + * + * @author Jordi Boggiano + */ +abstract class Handler implements HandlerInterface +{ + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + foreach ($records as $record) { + $this->handle($record); + } + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + } + + public function __destruct() + { + try { + $this->close(); + } catch (\Throwable $e) { + // do nothing + } + } + + public function __sleep() + { + $this->close(); + + $reflClass = new \ReflectionClass($this); + + $keys = []; + foreach ($reflClass->getProperties() as $reflProp) { + if (!$reflProp->isStatic()) { + $keys[] = $reflProp->getName(); + } + } + + return $keys; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php new file mode 100644 index 0000000..f91ccd9 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Interface that all Monolog Handlers must implement + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +interface HandlerInterface +{ + /** + * Checks whether the given record will be handled by this handler. + * + * This is mostly done for performance reasons, to avoid calling processors for nothing. + * + * Handlers should still check the record levels within handle(), returning false in isHandling() + * is no guarantee that handle() will not be called, and isHandling() might not be called + * for a given record. + * + * @param array $record Partial log record containing only a level key + * + * @return bool + * + * @phpstan-param array{level: Level} $record + */ + public function isHandling(array $record): bool; + + /** + * Handles a record. + * + * All records may be passed to this method, and the handler should discard + * those that it does not want to handle. + * + * The return value of this function controls the bubbling process of the handler stack. + * Unless the bubbling is interrupted (by returning true), the Logger class will keep on + * calling further handlers in the stack with a given log record. + * + * @param array $record The record to handle + * @return bool true means that this handler handled the record, and that bubbling is not permitted. + * false means the record was either not processed or that this handler allows bubbling. + * + * @phpstan-param Record $record + */ + public function handle(array $record): bool; + + /** + * Handles a set of records at once. + * + * @param array $records The records to handle (an array of record arrays) + * + * @phpstan-param Record[] $records + */ + public function handleBatch(array $records): void; + + /** + * Closes the handler. + * + * Ends a log cycle and frees all resources used by the handler. + * + * Closing a Handler means flushing all buffers and freeing any open resources/handles. + * + * Implementations have to be idempotent (i.e. it should be possible to call close several times without breakage) + * and ideally handlers should be able to reopen themselves on handle() after they have been closed. + * + * This is useful at the end of a request and will be called automatically when the object + * is destroyed if you extend Monolog\Handler\Handler. + * + * If you are thinking of calling this method yourself, most likely you should be + * calling ResettableInterface::reset instead. Have a look. + */ + public function close(): void; +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php new file mode 100644 index 0000000..a6626f9 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\ResettableInterface; +use Monolog\Formatter\FormatterInterface; + +/** + * This simple wrapper class can be used to extend handlers functionality. + * + * Example: A custom filtering that can be applied to any handler. + * + * Inherit from this class and override handle() like this: + * + * public function handle(array $record) + * { + * if ($record meets certain conditions) { + * return false; + * } + * return $this->handler->handle($record); + * } + * + * @author Alexey Karapetov + */ +class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, FormattableHandlerInterface, ResettableInterface +{ + /** + * @var HandlerInterface + */ + protected $handler; + + public function __construct(HandlerInterface $handler) + { + $this->handler = $handler; + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return $this->handler->isHandling($record); + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + return $this->handler->handle($record); + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $this->handler->handleBatch($records); + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + $this->handler->close(); + } + + /** + * {@inheritDoc} + */ + public function pushProcessor(callable $callback): HandlerInterface + { + if ($this->handler instanceof ProcessableHandlerInterface) { + $this->handler->pushProcessor($callback); + + return $this; + } + + throw new \LogicException('The wrapped handler does not implement ' . ProcessableHandlerInterface::class); + } + + /** + * {@inheritDoc} + */ + public function popProcessor(): callable + { + if ($this->handler instanceof ProcessableHandlerInterface) { + return $this->handler->popProcessor(); + } + + throw new \LogicException('The wrapped handler does not implement ' . ProcessableHandlerInterface::class); + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + $this->handler->setFormatter($formatter); + + return $this; + } + + throw new \LogicException('The wrapped handler does not implement ' . FormattableHandlerInterface::class); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + return $this->handler->getFormatter(); + } + + throw new \LogicException('The wrapped handler does not implement ' . FormattableHandlerInterface::class); + } + + public function reset() + { + if ($this->handler instanceof ResettableInterface) { + $this->handler->reset(); + } + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php new file mode 100644 index 0000000..ff901d0 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; + +/** + * IFTTTHandler uses cURL to trigger IFTTT Maker actions + * + * Register a secret key and trigger/event name at https://ifttt.com/maker + * + * value1 will be the channel from monolog's Logger constructor, + * value2 will be the level name (ERROR, WARNING, ..) + * value3 will be the log record's message + * + * @author Nehal Patel + */ +class IFTTTHandler extends AbstractProcessingHandler +{ + /** @var string */ + private $eventName; + /** @var string */ + private $secretKey; + + /** + * @param string $eventName The name of the IFTTT Maker event that should be triggered + * @param string $secretKey A valid IFTTT secret key + */ + public function __construct(string $eventName, string $secretKey, $level = Logger::ERROR, bool $bubble = true) + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the IFTTTHandler'); + } + + $this->eventName = $eventName; + $this->secretKey = $secretKey; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + public function write(array $record): void + { + $postData = [ + "value1" => $record["channel"], + "value2" => $record["level_name"], + "value3" => $record["message"], + ]; + $postString = Utils::jsonEncode($postData); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "https://maker.ifttt.com/trigger/" . $this->eventName . "/with/key/" . $this->secretKey); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $postString); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + "Content-Type: application/json", + ]); + + Curl\Util::execute($ch); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php new file mode 100644 index 0000000..9ec2c49 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Inspired on LogEntriesHandler. + * + * @author Robert Kaufmann III + * @author Gabriel Machado + */ +class InsightOpsHandler extends SocketHandler +{ + /** + * @var string + */ + protected $logToken; + + /** + * @param string $token Log token supplied by InsightOps + * @param string $region Region where InsightOps account is hosted. Could be 'us' or 'eu'. + * @param bool $useSSL Whether or not SSL encryption should be used + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct( + string $token, + string $region = 'us', + bool $useSSL = true, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if ($useSSL && !extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for InsightOpsHandler'); + } + + $endpoint = $useSSL + ? 'ssl://' . $region . '.data.logs.insight.rapid7.com:443' + : $region . '.data.logs.insight.rapid7.com:80'; + + parent::__construct( + $endpoint, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + $this->logToken = $token; + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + return $this->logToken . ' ' . $record['formatted']; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php new file mode 100644 index 0000000..b2e7961 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * @author Robert Kaufmann III + */ +class LogEntriesHandler extends SocketHandler +{ + /** + * @var string + */ + protected $logToken; + + /** + * @param string $token Log token supplied by LogEntries + * @param bool $useSSL Whether or not SSL encryption should be used. + * @param string $host Custom hostname to send the data to if needed + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct( + string $token, + bool $useSSL = true, + $level = Logger::DEBUG, + bool $bubble = true, + string $host = 'data.logentries.com', + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if ($useSSL && !extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler'); + } + + $endpoint = $useSSL ? 'ssl://' . $host . ':443' : $host . ':80'; + parent::__construct( + $endpoint, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + $this->logToken = $token; + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + return $this->logToken . ' ' . $record['formatted']; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php new file mode 100644 index 0000000..dc55991 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LogglyFormatter; +use function array_key_exists; +use CurlHandle; + +/** + * Sends errors to Loggly. + * + * @author Przemek Sobstel + * @author Adam Pancutt + * @author Gregory Barchard + */ +class LogglyHandler extends AbstractProcessingHandler +{ + protected const HOST = 'logs-01.loggly.com'; + protected const ENDPOINT_SINGLE = 'inputs'; + protected const ENDPOINT_BATCH = 'bulk'; + + /** + * Caches the curl handlers for every given endpoint. + * + * @var resource[]|CurlHandle[] + */ + protected $curlHandlers = []; + + /** @var string */ + protected $token; + + /** @var string[] */ + protected $tag = []; + + /** + * @param string $token API token supplied by Loggly + * + * @throws MissingExtensionException If the curl extension is missing + */ + public function __construct(string $token, $level = Logger::DEBUG, bool $bubble = true) + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the LogglyHandler'); + } + + $this->token = $token; + + parent::__construct($level, $bubble); + } + + /** + * Loads and returns the shared curl handler for the given endpoint. + * + * @param string $endpoint + * + * @return resource|CurlHandle + */ + protected function getCurlHandler(string $endpoint) + { + if (!array_key_exists($endpoint, $this->curlHandlers)) { + $this->curlHandlers[$endpoint] = $this->loadCurlHandle($endpoint); + } + + return $this->curlHandlers[$endpoint]; + } + + /** + * Starts a fresh curl session for the given endpoint and returns its handler. + * + * @param string $endpoint + * + * @return resource|CurlHandle + */ + private function loadCurlHandle(string $endpoint) + { + $url = sprintf("https://%s/%s/%s/", static::HOST, $endpoint, $this->token); + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + return $ch; + } + + /** + * @param string[]|string $tag + */ + public function setTag($tag): self + { + $tag = !empty($tag) ? $tag : []; + $this->tag = is_array($tag) ? $tag : [$tag]; + + return $this; + } + + /** + * @param string[]|string $tag + */ + public function addTag($tag): self + { + if (!empty($tag)) { + $tag = is_array($tag) ? $tag : [$tag]; + $this->tag = array_unique(array_merge($this->tag, $tag)); + } + + return $this; + } + + protected function write(array $record): void + { + $this->send($record["formatted"], static::ENDPOINT_SINGLE); + } + + public function handleBatch(array $records): void + { + $level = $this->level; + + $records = array_filter($records, function ($record) use ($level) { + return ($record['level'] >= $level); + }); + + if ($records) { + $this->send($this->getFormatter()->formatBatch($records), static::ENDPOINT_BATCH); + } + } + + protected function send(string $data, string $endpoint): void + { + $ch = $this->getCurlHandler($endpoint); + + $headers = ['Content-Type: application/json']; + + if (!empty($this->tag)) { + $headers[] = 'X-LOGGLY-TAG: '.implode(',', $this->tag); + } + + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + + Curl\Util::execute($ch, 5, false); + } + + protected function getDefaultFormatter(): FormatterInterface + { + return new LogglyFormatter(); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php new file mode 100644 index 0000000..7331407 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LogmaticFormatter; + +/** + * @author Julien Breux + */ +class LogmaticHandler extends SocketHandler +{ + /** + * @var string + */ + private $logToken; + + /** + * @var string + */ + private $hostname; + + /** + * @var string + */ + private $appname; + + /** + * @param string $token Log token supplied by Logmatic. + * @param string $hostname Host name supplied by Logmatic. + * @param string $appname Application name supplied by Logmatic. + * @param bool $useSSL Whether or not SSL encryption should be used. + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct( + string $token, + string $hostname = '', + string $appname = '', + bool $useSSL = true, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if ($useSSL && !extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use SSL encrypted connection for LogmaticHandler'); + } + + $endpoint = $useSSL ? 'ssl://api.logmatic.io:10515' : 'api.logmatic.io:10514'; + $endpoint .= '/v1/'; + + parent::__construct( + $endpoint, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + + $this->logToken = $token; + $this->hostname = $hostname; + $this->appname = $appname; + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + return $this->logToken . ' ' . $record['formatted']; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + $formatter = new LogmaticFormatter(); + + if (!empty($this->hostname)) { + $formatter->setHostname($this->hostname); + } + if (!empty($this->appname)) { + $formatter->setAppname($this->appname); + } + + return $formatter; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php new file mode 100644 index 0000000..8d83cd8 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\HtmlFormatter; + +/** + * Base class for all mail handlers + * + * @author Gyula Sallai + * + * @phpstan-import-type Record from \Monolog\Logger + */ +abstract class MailHandler extends AbstractProcessingHandler +{ + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + $messages = []; + + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + /** @var Record $message */ + $message = $this->processRecord($record); + $messages[] = $message; + } + + if (!empty($messages)) { + $this->send((string) $this->getFormatter()->formatBatch($messages), $messages); + } + } + + /** + * Send a mail with the given content + * + * @param string $content formatted email body to be sent + * @param array $records the array of log records that formed this content + * + * @phpstan-param Record[] $records + */ + abstract protected function send(string $content, array $records): void; + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->send((string) $record['formatted'], [$record]); + } + + /** + * @phpstan-param non-empty-array $records + * @phpstan-return Record + */ + protected function getHighestRecord(array $records): array + { + $highestRecord = null; + foreach ($records as $record) { + if ($highestRecord === null || $highestRecord['level'] < $record['level']) { + $highestRecord = $record; + } + } + + return $highestRecord; + } + + protected function isHtmlBody(string $body): bool + { + return ($body[0] ?? null) === '<'; + } + + /** + * Gets the default formatter. + * + * @return FormatterInterface + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new HtmlFormatter(); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php new file mode 100644 index 0000000..8c61136 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Swift; +use Swift_Message; + +/** + * MandrillHandler uses cURL to send the emails to the Mandrill API + * + * @author Adam Nicholson + */ +class MandrillHandler extends MailHandler +{ + /** @var Swift_Message */ + protected $message; + /** @var string */ + protected $apiKey; + + /** + * @psalm-param Swift_Message|callable(): Swift_Message $message + * + * @param string $apiKey A valid Mandrill API key + * @param callable|Swift_Message $message An example message for real messages, only the body will be replaced + */ + public function __construct(string $apiKey, $message, $level = Logger::ERROR, bool $bubble = true) + { + parent::__construct($level, $bubble); + + if (!$message instanceof Swift_Message && is_callable($message)) { + $message = $message(); + } + if (!$message instanceof Swift_Message) { + throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callable returning it'); + } + $this->message = $message; + $this->apiKey = $apiKey; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $mime = 'text/plain'; + if ($this->isHtmlBody($content)) { + $mime = 'text/html'; + } + + $message = clone $this->message; + $message->setBody($content, $mime); + /** @phpstan-ignore-next-line */ + if (version_compare(Swift::VERSION, '6.0.0', '>=')) { + $message->setDate(new \DateTimeImmutable()); + } else { + /** @phpstan-ignore-next-line */ + $message->setDate(time()); + } + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, 'https://mandrillapp.com/api/1.0/messages/send-raw.json'); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ + 'key' => $this->apiKey, + 'raw_message' => (string) $message, + 'async' => false, + ])); + + Curl\Util::execute($ch); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php new file mode 100644 index 0000000..03d8da2 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Exception can be thrown if an extension for a handler is missing + * + * @author Christian Bergau + */ +class MissingExtensionException extends \Exception +{ +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php new file mode 100644 index 0000000..386375c --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use MongoDB\Driver\BulkWrite; +use MongoDB\Driver\Manager; +use MongoDB\Client; +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\MongoDBFormatter; + +/** + * Logs to a MongoDB database. + * + * Usage example: + * + * $log = new \Monolog\Logger('application'); + * $client = new \MongoDB\Client('mongodb://localhost:27017'); + * $mongodb = new \Monolog\Handler\MongoDBHandler($client, 'logs', 'prod'); + * $log->pushHandler($mongodb); + * + * The above examples uses the MongoDB PHP library's client class; however, the + * MongoDB\Driver\Manager class from ext-mongodb is also supported. + */ +class MongoDBHandler extends AbstractProcessingHandler +{ + /** @var \MongoDB\Collection */ + private $collection; + /** @var Client|Manager */ + private $manager; + /** @var string */ + private $namespace; + + /** + * Constructor. + * + * @param Client|Manager $mongodb MongoDB library or driver client + * @param string $database Database name + * @param string $collection Collection name + */ + public function __construct($mongodb, string $database, string $collection, $level = Logger::DEBUG, bool $bubble = true) + { + if (!($mongodb instanceof Client || $mongodb instanceof Manager)) { + throw new \InvalidArgumentException('MongoDB\Client or MongoDB\Driver\Manager instance required'); + } + + if ($mongodb instanceof Client) { + $this->collection = $mongodb->selectCollection($database, $collection); + } else { + $this->manager = $mongodb; + $this->namespace = $database . '.' . $collection; + } + + parent::__construct($level, $bubble); + } + + protected function write(array $record): void + { + if (isset($this->collection)) { + $this->collection->insertOne($record['formatted']); + } + + if (isset($this->manager, $this->namespace)) { + $bulk = new BulkWrite; + $bulk->insert($record["formatted"]); + $this->manager->executeBulkWrite($this->namespace, $bulk); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new MongoDBFormatter; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php new file mode 100644 index 0000000..9812c1d --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; + +/** + * NativeMailerHandler uses the mail() function to send the emails + * + * @author Christophe Coevoet + * @author Mark Garrett + */ +class NativeMailerHandler extends MailHandler +{ + /** + * The email addresses to which the message will be sent + * @var string[] + */ + protected $to; + + /** + * The subject of the email + * @var string + */ + protected $subject; + + /** + * Optional headers for the message + * @var string[] + */ + protected $headers = []; + + /** + * Optional parameters for the message + * @var string[] + */ + protected $parameters = []; + + /** + * The wordwrap length for the message + * @var int + */ + protected $maxColumnWidth; + + /** + * The Content-type for the message + * @var string|null + */ + protected $contentType; + + /** + * The encoding for the message + * @var string + */ + protected $encoding = 'utf-8'; + + /** + * @param string|string[] $to The receiver of the mail + * @param string $subject The subject of the mail + * @param string $from The sender of the mail + * @param int $maxColumnWidth The maximum column width that the message lines will have + */ + public function __construct($to, string $subject, string $from, $level = Logger::ERROR, bool $bubble = true, int $maxColumnWidth = 70) + { + parent::__construct($level, $bubble); + $this->to = (array) $to; + $this->subject = $subject; + $this->addHeader(sprintf('From: %s', $from)); + $this->maxColumnWidth = $maxColumnWidth; + } + + /** + * Add headers to the message + * + * @param string|string[] $headers Custom added headers + */ + public function addHeader($headers): self + { + foreach ((array) $headers as $header) { + if (strpos($header, "\n") !== false || strpos($header, "\r") !== false) { + throw new \InvalidArgumentException('Headers can not contain newline characters for security reasons'); + } + $this->headers[] = $header; + } + + return $this; + } + + /** + * Add parameters to the message + * + * @param string|string[] $parameters Custom added parameters + */ + public function addParameter($parameters): self + { + $this->parameters = array_merge($this->parameters, (array) $parameters); + + return $this; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $contentType = $this->getContentType() ?: ($this->isHtmlBody($content) ? 'text/html' : 'text/plain'); + + if ($contentType !== 'text/html') { + $content = wordwrap($content, $this->maxColumnWidth); + } + + $headers = ltrim(implode("\r\n", $this->headers) . "\r\n", "\r\n"); + $headers .= 'Content-type: ' . $contentType . '; charset=' . $this->getEncoding() . "\r\n"; + if ($contentType === 'text/html' && false === strpos($headers, 'MIME-Version:')) { + $headers .= 'MIME-Version: 1.0' . "\r\n"; + } + + $subject = $this->subject; + if ($records) { + $subjectFormatter = new LineFormatter($this->subject); + $subject = $subjectFormatter->format($this->getHighestRecord($records)); + } + + $parameters = implode(' ', $this->parameters); + foreach ($this->to as $to) { + mail($to, $subject, $content, $headers, $parameters); + } + } + + public function getContentType(): ?string + { + return $this->contentType; + } + + public function getEncoding(): string + { + return $this->encoding; + } + + /** + * @param string $contentType The content type of the email - Defaults to text/plain. Use text/html for HTML messages. + */ + public function setContentType(string $contentType): self + { + if (strpos($contentType, "\n") !== false || strpos($contentType, "\r") !== false) { + throw new \InvalidArgumentException('The content type can not contain newline characters to prevent email header injection'); + } + + $this->contentType = $contentType; + + return $this; + } + + public function setEncoding(string $encoding): self + { + if (strpos($encoding, "\n") !== false || strpos($encoding, "\r") !== false) { + throw new \InvalidArgumentException('The encoding can not contain newline characters to prevent email header injection'); + } + + $this->encoding = $encoding; + + return $this; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php new file mode 100644 index 0000000..53c3b8d --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php @@ -0,0 +1,199 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Class to record a log on a NewRelic application. + * Enabling New Relic High Security mode may prevent capture of useful information. + * + * This handler requires a NormalizerFormatter to function and expects an array in $record['formatted'] + * + * @see https://docs.newrelic.com/docs/agents/php-agent + * @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security + */ +class NewRelicHandler extends AbstractProcessingHandler +{ + /** + * Name of the New Relic application that will receive logs from this handler. + * + * @var ?string + */ + protected $appName; + + /** + * Name of the current transaction + * + * @var ?string + */ + protected $transactionName; + + /** + * Some context and extra data is passed into the handler as arrays of values. Do we send them as is + * (useful if we are using the API), or explode them for display on the NewRelic RPM website? + * + * @var bool + */ + protected $explodeArrays; + + /** + * {@inheritDoc} + * + * @param string|null $appName + * @param bool $explodeArrays + * @param string|null $transactionName + */ + public function __construct( + $level = Logger::ERROR, + bool $bubble = true, + ?string $appName = null, + bool $explodeArrays = false, + ?string $transactionName = null + ) { + parent::__construct($level, $bubble); + + $this->appName = $appName; + $this->explodeArrays = $explodeArrays; + $this->transactionName = $transactionName; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!$this->isNewRelicEnabled()) { + throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler'); + } + + if ($appName = $this->getAppName($record['context'])) { + $this->setNewRelicAppName($appName); + } + + if ($transactionName = $this->getTransactionName($record['context'])) { + $this->setNewRelicTransactionName($transactionName); + unset($record['formatted']['context']['transaction_name']); + } + + if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) { + newrelic_notice_error($record['message'], $record['context']['exception']); + unset($record['formatted']['context']['exception']); + } else { + newrelic_notice_error($record['message']); + } + + if (isset($record['formatted']['context']) && is_array($record['formatted']['context'])) { + foreach ($record['formatted']['context'] as $key => $parameter) { + if (is_array($parameter) && $this->explodeArrays) { + foreach ($parameter as $paramKey => $paramValue) { + $this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue); + } + } else { + $this->setNewRelicParameter('context_' . $key, $parameter); + } + } + } + + if (isset($record['formatted']['extra']) && is_array($record['formatted']['extra'])) { + foreach ($record['formatted']['extra'] as $key => $parameter) { + if (is_array($parameter) && $this->explodeArrays) { + foreach ($parameter as $paramKey => $paramValue) { + $this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue); + } + } else { + $this->setNewRelicParameter('extra_' . $key, $parameter); + } + } + } + } + + /** + * Checks whether the NewRelic extension is enabled in the system. + * + * @return bool + */ + protected function isNewRelicEnabled(): bool + { + return extension_loaded('newrelic'); + } + + /** + * Returns the appname where this log should be sent. Each log can override the default appname, set in this + * handler's constructor, by providing the appname in it's context. + * + * @param mixed[] $context + */ + protected function getAppName(array $context): ?string + { + if (isset($context['appname'])) { + return $context['appname']; + } + + return $this->appName; + } + + /** + * Returns the name of the current transaction. Each log can override the default transaction name, set in this + * handler's constructor, by providing the transaction_name in it's context + * + * @param mixed[] $context + */ + protected function getTransactionName(array $context): ?string + { + if (isset($context['transaction_name'])) { + return $context['transaction_name']; + } + + return $this->transactionName; + } + + /** + * Sets the NewRelic application that should receive this log. + */ + protected function setNewRelicAppName(string $appName): void + { + newrelic_set_appname($appName); + } + + /** + * Overwrites the name of the current transaction + */ + protected function setNewRelicTransactionName(string $transactionName): void + { + newrelic_name_transaction($transactionName); + } + + /** + * @param string $key + * @param mixed $value + */ + protected function setNewRelicParameter(string $key, $value): void + { + if (null === $value || is_scalar($value)) { + newrelic_add_custom_parameter($key, $value); + } else { + newrelic_add_custom_parameter($key, Utils::jsonEncode($value, null, true)); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new NormalizerFormatter(); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.php new file mode 100644 index 0000000..1be5d89 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * No-op + * + * This handler handles anything, but does nothing, and does not stop bubbling to the rest of the stack. + * This can be used for testing, or to disable a handler when overriding a configuration without + * influencing the rest of the stack. + * + * @author Roel Harbers + */ +class NoopHandler extends Handler +{ + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return true; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + return false; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php new file mode 100644 index 0000000..6488fec --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Blackhole + * + * Any record it can handle will be thrown away. This can be used + * to put on top of an existing stack to override it temporarily. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class NullHandler extends Handler +{ + /** + * @var int + */ + private $level; + + /** + * @param string|int $level The minimum logging level at which this handler will be triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = Logger::DEBUG) + { + $this->level = Logger::toMonologLevel($level); + } + + /** + * {@inheritDoc} + */ + public function isHandling(array $record): bool + { + return $record['level'] >= $this->level; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + return $record['level'] >= $this->level; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.php new file mode 100644 index 0000000..324f8c6 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; + +/** + * Handler to only pass log messages when a certain threshold of number of messages is reached. + * + * This can be useful in cases of processing a batch of data, but you're for example only interested + * in case it fails catastrophically instead of a warning for 1 or 2 events. Worse things can happen, right? + * + * Usage example: + * + * ``` + * $log = new Logger('application'); + * $handler = new SomeHandler(...) + * + * // Pass all warnings to the handler when more than 10 & all error messages when more then 5 + * $overflow = new OverflowHandler($handler, [Logger::WARNING => 10, Logger::ERROR => 5]); + * + * $log->pushHandler($overflow); + *``` + * + * @author Kris Buist + */ +class OverflowHandler extends AbstractHandler implements FormattableHandlerInterface +{ + /** @var HandlerInterface */ + private $handler; + + /** @var int[] */ + private $thresholdMap = [ + Logger::DEBUG => 0, + Logger::INFO => 0, + Logger::NOTICE => 0, + Logger::WARNING => 0, + Logger::ERROR => 0, + Logger::CRITICAL => 0, + Logger::ALERT => 0, + Logger::EMERGENCY => 0, + ]; + + /** + * Buffer of all messages passed to the handler before the threshold was reached + * + * @var mixed[][] + */ + private $buffer = []; + + /** + * @param HandlerInterface $handler + * @param int[] $thresholdMap Dictionary of logger level => threshold + */ + public function __construct( + HandlerInterface $handler, + array $thresholdMap = [], + $level = Logger::DEBUG, + bool $bubble = true + ) { + $this->handler = $handler; + foreach ($thresholdMap as $thresholdLevel => $threshold) { + $this->thresholdMap[$thresholdLevel] = $threshold; + } + parent::__construct($level, $bubble); + } + + /** + * Handles a record. + * + * All records may be passed to this method, and the handler should discard + * those that it does not want to handle. + * + * The return value of this function controls the bubbling process of the handler stack. + * Unless the bubbling is interrupted (by returning true), the Logger class will keep on + * calling further handlers in the stack with a given log record. + * + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($record['level'] < $this->level) { + return false; + } + + $level = $record['level']; + + if (!isset($this->thresholdMap[$level])) { + $this->thresholdMap[$level] = 0; + } + + if ($this->thresholdMap[$level] > 0) { + // The overflow threshold is not yet reached, so we're buffering the record and lowering the threshold by 1 + $this->thresholdMap[$level]--; + $this->buffer[$level][] = $record; + + return false === $this->bubble; + } + + if ($this->thresholdMap[$level] == 0) { + // This current message is breaking the threshold. Flush the buffer and continue handling the current record + foreach ($this->buffer[$level] ?? [] as $buffered) { + $this->handler->handle($buffered); + } + $this->thresholdMap[$level]--; + unset($this->buffer[$level]); + } + + $this->handler->handle($record); + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + $this->handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + if ($this->handler instanceof FormattableHandlerInterface) { + return $this->handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($this->handler).' does not support formatters.'); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php new file mode 100644 index 0000000..62d943f --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php @@ -0,0 +1,263 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; +use PhpConsole\Connector; +use PhpConsole\Handler as VendorPhpConsoleHandler; +use PhpConsole\Helper; + +/** + * Monolog handler for Google Chrome extension "PHP Console" + * + * Display PHP error/debug log messages in Google Chrome console and notification popups, executes PHP code remotely + * + * Usage: + * 1. Install Google Chrome extension [now dead and removed from the chrome store] + * 2. See overview https://github.com/barbushin/php-console#overview + * 3. Install PHP Console library https://github.com/barbushin/php-console#installation + * 4. Example (result will looks like http://i.hizliresim.com/vg3Pz4.png) + * + * $logger = new \Monolog\Logger('all', array(new \Monolog\Handler\PHPConsoleHandler())); + * \Monolog\ErrorHandler::register($logger); + * echo $undefinedVar; + * $logger->debug('SELECT * FROM users', array('db', 'time' => 0.012)); + * PC::debug($_SERVER); // PHP Console debugger for any type of vars + * + * @author Sergey Barbushin https://www.linkedin.com/in/barbushin + * + * @phpstan-import-type Record from \Monolog\Logger + * @deprecated Since 2.8.0 and 3.2.0, PHPConsole is abandoned and thus we will drop this handler in Monolog 4 + */ +class PHPConsoleHandler extends AbstractProcessingHandler +{ + /** @var array */ + private $options = [ + 'enabled' => true, // bool Is PHP Console server enabled + 'classesPartialsTraceIgnore' => ['Monolog\\'], // array Hide calls of classes started with... + 'debugTagsKeysInContext' => [0, 'tag'], // bool Is PHP Console server enabled + 'useOwnErrorsHandler' => false, // bool Enable errors handling + 'useOwnExceptionsHandler' => false, // bool Enable exceptions handling + 'sourcesBasePath' => null, // string Base path of all project sources to strip in errors source paths + 'registerHelper' => true, // bool Register PhpConsole\Helper that allows short debug calls like PC::debug($var, 'ta.g.s') + 'serverEncoding' => null, // string|null Server internal encoding + 'headersLimit' => null, // int|null Set headers size limit for your web-server + 'password' => null, // string|null Protect PHP Console connection by password + 'enableSslOnlyMode' => false, // bool Force connection by SSL for clients with PHP Console installed + 'ipMasks' => [], // array Set IP masks of clients that will be allowed to connect to PHP Console: array('192.168.*.*', '127.0.0.1') + 'enableEvalListener' => false, // bool Enable eval request to be handled by eval dispatcher(if enabled, 'password' option is also required) + 'dumperDetectCallbacks' => false, // bool Convert callback items in dumper vars to (callback SomeClass::someMethod) strings + 'dumperLevelLimit' => 5, // int Maximum dumped vars array or object nested dump level + 'dumperItemsCountLimit' => 100, // int Maximum dumped var same level array items or object properties number + 'dumperItemSizeLimit' => 5000, // int Maximum length of any string or dumped array item + 'dumperDumpSizeLimit' => 500000, // int Maximum approximate size of dumped vars result formatted in JSON + 'detectDumpTraceAndSource' => false, // bool Autodetect and append trace data to debug + 'dataStorage' => null, // \PhpConsole\Storage|null Fixes problem with custom $_SESSION handler(see http://goo.gl/Ne8juJ) + ]; + + /** @var Connector */ + private $connector; + + /** + * @param array $options See \Monolog\Handler\PHPConsoleHandler::$options for more details + * @param Connector|null $connector Instance of \PhpConsole\Connector class (optional) + * @throws \RuntimeException + */ + public function __construct(array $options = [], ?Connector $connector = null, $level = Logger::DEBUG, bool $bubble = true) + { + if (!class_exists('PhpConsole\Connector')) { + throw new \RuntimeException('PHP Console library not found. See https://github.com/barbushin/php-console#installation'); + } + parent::__construct($level, $bubble); + $this->options = $this->initOptions($options); + $this->connector = $this->initConnector($connector); + } + + /** + * @param array $options + * + * @return array + */ + private function initOptions(array $options): array + { + $wrongOptions = array_diff(array_keys($options), array_keys($this->options)); + if ($wrongOptions) { + throw new \RuntimeException('Unknown options: ' . implode(', ', $wrongOptions)); + } + + return array_replace($this->options, $options); + } + + private function initConnector(?Connector $connector = null): Connector + { + if (!$connector) { + if ($this->options['dataStorage']) { + Connector::setPostponeStorage($this->options['dataStorage']); + } + $connector = Connector::getInstance(); + } + + if ($this->options['registerHelper'] && !Helper::isRegistered()) { + Helper::register(); + } + + if ($this->options['enabled'] && $connector->isActiveClient()) { + if ($this->options['useOwnErrorsHandler'] || $this->options['useOwnExceptionsHandler']) { + $handler = VendorPhpConsoleHandler::getInstance(); + $handler->setHandleErrors($this->options['useOwnErrorsHandler']); + $handler->setHandleExceptions($this->options['useOwnExceptionsHandler']); + $handler->start(); + } + if ($this->options['sourcesBasePath']) { + $connector->setSourcesBasePath($this->options['sourcesBasePath']); + } + if ($this->options['serverEncoding']) { + $connector->setServerEncoding($this->options['serverEncoding']); + } + if ($this->options['password']) { + $connector->setPassword($this->options['password']); + } + if ($this->options['enableSslOnlyMode']) { + $connector->enableSslOnlyMode(); + } + if ($this->options['ipMasks']) { + $connector->setAllowedIpMasks($this->options['ipMasks']); + } + if ($this->options['headersLimit']) { + $connector->setHeadersLimit($this->options['headersLimit']); + } + if ($this->options['detectDumpTraceAndSource']) { + $connector->getDebugDispatcher()->detectTraceAndSource = true; + } + $dumper = $connector->getDumper(); + $dumper->levelLimit = $this->options['dumperLevelLimit']; + $dumper->itemsCountLimit = $this->options['dumperItemsCountLimit']; + $dumper->itemSizeLimit = $this->options['dumperItemSizeLimit']; + $dumper->dumpSizeLimit = $this->options['dumperDumpSizeLimit']; + $dumper->detectCallbacks = $this->options['dumperDetectCallbacks']; + if ($this->options['enableEvalListener']) { + $connector->startEvalRequestsListener(); + } + } + + return $connector; + } + + public function getConnector(): Connector + { + return $this->connector; + } + + /** + * @return array + */ + public function getOptions(): array + { + return $this->options; + } + + public function handle(array $record): bool + { + if ($this->options['enabled'] && $this->connector->isActiveClient()) { + return parent::handle($record); + } + + return !$this->bubble; + } + + /** + * Writes the record down to the log of the implementing handler + */ + protected function write(array $record): void + { + if ($record['level'] < Logger::NOTICE) { + $this->handleDebugRecord($record); + } elseif (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) { + $this->handleExceptionRecord($record); + } else { + $this->handleErrorRecord($record); + } + } + + /** + * @phpstan-param Record $record + */ + private function handleDebugRecord(array $record): void + { + $tags = $this->getRecordTags($record); + $message = $record['message']; + if ($record['context']) { + $message .= ' ' . Utils::jsonEncode($this->connector->getDumper()->dump(array_filter($record['context'])), null, true); + } + $this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']); + } + + /** + * @phpstan-param Record $record + */ + private function handleExceptionRecord(array $record): void + { + $this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']); + } + + /** + * @phpstan-param Record $record + */ + private function handleErrorRecord(array $record): void + { + $context = $record['context']; + + $this->connector->getErrorsDispatcher()->dispatchError( + $context['code'] ?? null, + $context['message'] ?? $record['message'], + $context['file'] ?? null, + $context['line'] ?? null, + $this->options['classesPartialsTraceIgnore'] + ); + } + + /** + * @phpstan-param Record $record + * @return string + */ + private function getRecordTags(array &$record) + { + $tags = null; + if (!empty($record['context'])) { + $context = & $record['context']; + foreach ($this->options['debugTagsKeysInContext'] as $key) { + if (!empty($context[$key])) { + $tags = $context[$key]; + if ($key === 0) { + array_shift($context); + } else { + unset($context[$key]); + } + break; + } + } + } + + return $tags ?: strtolower($record['level_name']); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter('%message%'); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.php new file mode 100644 index 0000000..5dd22ad --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.php @@ -0,0 +1,191 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores to STDIN of any process, specified by a command. + * + * Usage example: + *
      + * $log = new Logger('myLogger');
      + * $log->pushHandler(new ProcessHandler('/usr/bin/php /var/www/monolog/someScript.php'));
      + * 
      + * + * @author Kolja Zuelsdorf + */ +class ProcessHandler extends AbstractProcessingHandler +{ + /** + * Holds the process to receive data on its STDIN. + * + * @var resource|bool|null + */ + private $process; + + /** + * @var string + */ + private $command; + + /** + * @var string|null + */ + private $cwd; + + /** + * @var resource[] + */ + private $pipes = []; + + /** + * @var array + */ + protected const DESCRIPTOR_SPEC = [ + 0 => ['pipe', 'r'], // STDIN is a pipe that the child will read from + 1 => ['pipe', 'w'], // STDOUT is a pipe that the child will write to + 2 => ['pipe', 'w'], // STDERR is a pipe to catch the any errors + ]; + + /** + * @param string $command Command for the process to start. Absolute paths are recommended, + * especially if you do not use the $cwd parameter. + * @param string|null $cwd "Current working directory" (CWD) for the process to be executed in. + * @throws \InvalidArgumentException + */ + public function __construct(string $command, $level = Logger::DEBUG, bool $bubble = true, ?string $cwd = null) + { + if ($command === '') { + throw new \InvalidArgumentException('The command argument must be a non-empty string.'); + } + if ($cwd === '') { + throw new \InvalidArgumentException('The optional CWD argument must be a non-empty string or null.'); + } + + parent::__construct($level, $bubble); + + $this->command = $command; + $this->cwd = $cwd; + } + + /** + * Writes the record down to the log of the implementing handler + * + * @throws \UnexpectedValueException + */ + protected function write(array $record): void + { + $this->ensureProcessIsStarted(); + + $this->writeProcessInput($record['formatted']); + + $errors = $this->readProcessErrors(); + if (empty($errors) === false) { + throw new \UnexpectedValueException(sprintf('Errors while writing to process: %s', $errors)); + } + } + + /** + * Makes sure that the process is actually started, and if not, starts it, + * assigns the stream pipes, and handles startup errors, if any. + */ + private function ensureProcessIsStarted(): void + { + if (is_resource($this->process) === false) { + $this->startProcess(); + + $this->handleStartupErrors(); + } + } + + /** + * Starts the actual process and sets all streams to non-blocking. + */ + private function startProcess(): void + { + $this->process = proc_open($this->command, static::DESCRIPTOR_SPEC, $this->pipes, $this->cwd); + + foreach ($this->pipes as $pipe) { + stream_set_blocking($pipe, false); + } + } + + /** + * Selects the STDERR stream, handles upcoming startup errors, and throws an exception, if any. + * + * @throws \UnexpectedValueException + */ + private function handleStartupErrors(): void + { + $selected = $this->selectErrorStream(); + if (false === $selected) { + throw new \UnexpectedValueException('Something went wrong while selecting a stream.'); + } + + $errors = $this->readProcessErrors(); + + if (is_resource($this->process) === false || empty($errors) === false) { + throw new \UnexpectedValueException( + sprintf('The process "%s" could not be opened: ' . $errors, $this->command) + ); + } + } + + /** + * Selects the STDERR stream. + * + * @return int|bool + */ + protected function selectErrorStream() + { + $empty = []; + $errorPipes = [$this->pipes[2]]; + + return stream_select($errorPipes, $empty, $empty, 1); + } + + /** + * Reads the errors of the process, if there are any. + * + * @codeCoverageIgnore + * @return string Empty string if there are no errors. + */ + protected function readProcessErrors(): string + { + return (string) stream_get_contents($this->pipes[2]); + } + + /** + * Writes to the input stream of the opened process. + * + * @codeCoverageIgnore + */ + protected function writeProcessInput(string $string): void + { + fwrite($this->pipes[0], $string); + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + if (is_resource($this->process)) { + foreach ($this->pipes as $pipe) { + fclose($pipe); + } + proc_close($this->process); + $this->process = null; + } + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php new file mode 100644 index 0000000..7ef1283 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Processor\ProcessorInterface; + +/** + * Interface to describe loggers that have processors + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +interface ProcessableHandlerInterface +{ + /** + * Adds a processor in the stack. + * + * @psalm-param ProcessorInterface|callable(Record): Record $callback + * + * @param ProcessorInterface|callable $callback + * @return HandlerInterface self + */ + public function pushProcessor(callable $callback): HandlerInterface; + + /** + * Removes the processor on top of the stack and returns it. + * + * @psalm-return ProcessorInterface|callable(Record): Record $callback + * + * @throws \LogicException In case the processor stack is empty + * @return callable|ProcessorInterface + */ + public function popProcessor(): callable; +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php new file mode 100644 index 0000000..7c0a30b --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\ResettableInterface; +use Monolog\Processor\ProcessorInterface; + +/** + * Helper trait for implementing ProcessableInterface + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +trait ProcessableHandlerTrait +{ + /** + * @var callable[] + * @phpstan-var array + */ + protected $processors = []; + + /** + * {@inheritDoc} + */ + public function pushProcessor(callable $callback): HandlerInterface + { + array_unshift($this->processors, $callback); + + return $this; + } + + /** + * {@inheritDoc} + */ + public function popProcessor(): callable + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + + return array_shift($this->processors); + } + + /** + * Processes a record. + * + * @phpstan-param Record $record + * @phpstan-return Record + */ + protected function processRecord(array $record): array + { + foreach ($this->processors as $processor) { + $record = $processor($record); + } + + return $record; + } + + protected function resetProcessors(): void + { + foreach ($this->processors as $processor) { + if ($processor instanceof ResettableInterface) { + $processor->reset(); + } + } + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php new file mode 100644 index 0000000..d60549f --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Psr\Log\LoggerInterface; +use Monolog\Formatter\FormatterInterface; + +/** + * Proxies log messages to an existing PSR-3 compliant logger. + * + * If a formatter is configured, the formatter's output MUST be a string and the + * formatted message will be fed to the wrapped PSR logger instead of the original + * log record's message. + * + * @author Michael Moussa + */ +class PsrHandler extends AbstractHandler implements FormattableHandlerInterface +{ + /** + * PSR-3 compliant logger + * + * @var LoggerInterface + */ + protected $logger; + + /** + * @var FormatterInterface|null + */ + protected $formatter; + + /** + * @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied + */ + public function __construct(LoggerInterface $logger, $level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + + $this->logger = $logger; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if (!$this->isHandling($record)) { + return false; + } + + if ($this->formatter) { + $formatted = $this->formatter->format($record); + $this->logger->log(strtolower($record['level_name']), (string) $formatted, $record['context']); + } else { + $this->logger->log(strtolower($record['level_name']), $record['message'], $record['context']); + } + + return false === $this->bubble; + } + + /** + * Sets the formatter. + * + * @param FormatterInterface $formatter + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $this->formatter = $formatter; + + return $this; + } + + /** + * Gets the formatter. + * + * @return FormatterInterface + */ + public function getFormatter(): FormatterInterface + { + if (!$this->formatter) { + throw new \LogicException('No formatter has been set and this handler does not have a default formatter'); + } + + return $this->formatter; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php new file mode 100644 index 0000000..257407f --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php @@ -0,0 +1,246 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Psr\Log\LogLevel; + +/** + * Sends notifications through the pushover api to mobile phones + * + * @author Sebastian Göttschkes + * @see https://www.pushover.net/api + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class PushoverHandler extends SocketHandler +{ + /** @var string */ + private $token; + /** @var array */ + private $users; + /** @var string */ + private $title; + /** @var string|int|null */ + private $user = null; + /** @var int */ + private $retry; + /** @var int */ + private $expire; + + /** @var int */ + private $highPriorityLevel; + /** @var int */ + private $emergencyLevel; + /** @var bool */ + private $useFormattedMessage = false; + + /** + * All parameters that can be sent to Pushover + * @see https://pushover.net/api + * @var array + */ + private $parameterNames = [ + 'token' => true, + 'user' => true, + 'message' => true, + 'device' => true, + 'title' => true, + 'url' => true, + 'url_title' => true, + 'priority' => true, + 'timestamp' => true, + 'sound' => true, + 'retry' => true, + 'expire' => true, + 'callback' => true, + ]; + + /** + * Sounds the api supports by default + * @see https://pushover.net/api#sounds + * @var string[] + */ + private $sounds = [ + 'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming', + 'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb', + 'persistent', 'echo', 'updown', 'none', + ]; + + /** + * @param string $token Pushover api token + * @param string|array $users Pushover user id or array of ids the message will be sent to + * @param string|null $title Title sent to the Pushover API + * @param bool $useSSL Whether to connect via SSL. Required when pushing messages to users that are not + * the pushover.net app owner. OpenSSL is required for this option. + * @param string|int $highPriorityLevel The minimum logging level at which this handler will start + * sending "high priority" requests to the Pushover API + * @param string|int $emergencyLevel The minimum logging level at which this handler will start + * sending "emergency" requests to the Pushover API + * @param int $retry The retry parameter specifies how often (in seconds) the Pushover servers will + * send the same notification to the user. + * @param int $expire The expire parameter specifies how many seconds your notification will continue + * to be retried for (every retry seconds). + * + * @phpstan-param string|array $users + * @phpstan-param Level|LevelName|LogLevel::* $highPriorityLevel + * @phpstan-param Level|LevelName|LogLevel::* $emergencyLevel + */ + public function __construct( + string $token, + $users, + ?string $title = null, + $level = Logger::CRITICAL, + bool $bubble = true, + bool $useSSL = true, + $highPriorityLevel = Logger::CRITICAL, + $emergencyLevel = Logger::EMERGENCY, + int $retry = 30, + int $expire = 25200, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + $connectionString = $useSSL ? 'ssl://api.pushover.net:443' : 'api.pushover.net:80'; + parent::__construct( + $connectionString, + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + + $this->token = $token; + $this->users = (array) $users; + $this->title = $title ?: (string) gethostname(); + $this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel); + $this->emergencyLevel = Logger::toMonologLevel($emergencyLevel); + $this->retry = $retry; + $this->expire = $expire; + } + + protected function generateDataStream(array $record): string + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record): string + { + // Pushover has a limit of 512 characters on title and message combined. + $maxMessageLength = 512 - strlen($this->title); + + $message = ($this->useFormattedMessage) ? $record['formatted'] : $record['message']; + $message = Utils::substr($message, 0, $maxMessageLength); + + $timestamp = $record['datetime']->getTimestamp(); + + $dataArray = [ + 'token' => $this->token, + 'user' => $this->user, + 'message' => $message, + 'title' => $this->title, + 'timestamp' => $timestamp, + ]; + + if (isset($record['level']) && $record['level'] >= $this->emergencyLevel) { + $dataArray['priority'] = 2; + $dataArray['retry'] = $this->retry; + $dataArray['expire'] = $this->expire; + } elseif (isset($record['level']) && $record['level'] >= $this->highPriorityLevel) { + $dataArray['priority'] = 1; + } + + // First determine the available parameters + $context = array_intersect_key($record['context'], $this->parameterNames); + $extra = array_intersect_key($record['extra'], $this->parameterNames); + + // Least important info should be merged with subsequent info + $dataArray = array_merge($extra, $context, $dataArray); + + // Only pass sounds that are supported by the API + if (isset($dataArray['sound']) && !in_array($dataArray['sound'], $this->sounds)) { + unset($dataArray['sound']); + } + + return http_build_query($dataArray); + } + + private function buildHeader(string $content): string + { + $header = "POST /1/messages.json HTTP/1.1\r\n"; + $header .= "Host: api.pushover.net\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + protected function write(array $record): void + { + foreach ($this->users as $user) { + $this->user = $user; + + parent::write($record); + $this->closeSocket(); + } + + $this->user = null; + } + + /** + * @param int|string $value + * + * @phpstan-param Level|LevelName|LogLevel::* $value + */ + public function setHighPriorityLevel($value): self + { + $this->highPriorityLevel = Logger::toMonologLevel($value); + + return $this; + } + + /** + * @param int|string $value + * + * @phpstan-param Level|LevelName|LogLevel::* $value + */ + public function setEmergencyLevel($value): self + { + $this->emergencyLevel = Logger::toMonologLevel($value); + + return $this; + } + + /** + * Use the formatted message? + */ + public function useFormattedMessage(bool $value): self + { + $this->useFormattedMessage = $value; + + return $this; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php new file mode 100644 index 0000000..7c4e6fd --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; + +/** + * Logs to a Redis key using rpush + * + * usage example: + * + * $log = new Logger('application'); + * $redis = new RedisHandler(new Predis\Client("tcp://localhost:6379"), "logs", "prod"); + * $log->pushHandler($redis); + * + * @author Thomas Tourlourat + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class RedisHandler extends AbstractProcessingHandler +{ + /** @var \Predis\Client<\Predis\Client>|\Redis */ + private $redisClient; + /** @var string */ + private $redisKey; + /** @var int */ + protected $capSize; + + /** + * @param \Predis\Client<\Predis\Client>|\Redis $redis The redis instance + * @param string $key The key name to push records to + * @param int $capSize Number of entries to limit list size to, 0 = unlimited + */ + public function __construct($redis, string $key, $level = Logger::DEBUG, bool $bubble = true, int $capSize = 0) + { + if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) { + throw new \InvalidArgumentException('Predis\Client or Redis instance required'); + } + + $this->redisClient = $redis; + $this->redisKey = $key; + $this->capSize = $capSize; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if ($this->capSize) { + $this->writeCapped($record); + } else { + $this->redisClient->rpush($this->redisKey, $record["formatted"]); + } + } + + /** + * Write and cap the collection + * Writes the record to the redis list and caps its + * + * @phpstan-param FormattedRecord $record + */ + protected function writeCapped(array $record): void + { + if ($this->redisClient instanceof \Redis) { + $mode = defined('\Redis::MULTI') ? \Redis::MULTI : 1; + $this->redisClient->multi($mode) + ->rpush($this->redisKey, $record["formatted"]) + ->ltrim($this->redisKey, -$this->capSize, -1) + ->exec(); + } else { + $redisKey = $this->redisKey; + $capSize = $this->capSize; + $this->redisClient->transaction(function ($tx) use ($record, $redisKey, $capSize) { + $tx->rpush($redisKey, $record["formatted"]); + $tx->ltrim($redisKey, -$capSize, -1); + }); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter(); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php new file mode 100644 index 0000000..8ec5153 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; + +/** + * Sends the message to a Redis Pub/Sub channel using PUBLISH + * + * usage example: + * + * $log = new Logger('application'); + * $redis = new RedisPubSubHandler(new Predis\Client("tcp://localhost:6379"), "logs", Logger::WARNING); + * $log->pushHandler($redis); + * + * @author Gaëtan Faugère + */ +class RedisPubSubHandler extends AbstractProcessingHandler +{ + /** @var \Predis\Client<\Predis\Client>|\Redis */ + private $redisClient; + /** @var string */ + private $channelKey; + + /** + * @param \Predis\Client<\Predis\Client>|\Redis $redis The redis instance + * @param string $key The channel key to publish records to + */ + public function __construct($redis, string $key, $level = Logger::DEBUG, bool $bubble = true) + { + if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) { + throw new \InvalidArgumentException('Predis\Client or Redis instance required'); + } + + $this->redisClient = $redis; + $this->channelKey = $key; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->redisClient->publish($this->channelKey, $record["formatted"]); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + return new LineFormatter(); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php new file mode 100644 index 0000000..8d03ec4 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Rollbar\RollbarLogger; +use Throwable; +use Monolog\Logger; + +/** + * Sends errors to Rollbar + * + * If the context data contains a `payload` key, that is used as an array + * of payload options to RollbarLogger's log method. + * + * Rollbar's context info will contain the context + extra keys from the log record + * merged, and then on top of that a few keys: + * + * - level (rollbar level name) + * - monolog_level (monolog level name, raw level, as rollbar only has 5 but monolog 8) + * - channel + * - datetime (unix timestamp) + * + * @author Paul Statezny + */ +class RollbarHandler extends AbstractProcessingHandler +{ + /** + * @var RollbarLogger + */ + protected $rollbarLogger; + + /** @var string[] */ + protected $levelMap = [ + Logger::DEBUG => 'debug', + Logger::INFO => 'info', + Logger::NOTICE => 'info', + Logger::WARNING => 'warning', + Logger::ERROR => 'error', + Logger::CRITICAL => 'critical', + Logger::ALERT => 'critical', + Logger::EMERGENCY => 'critical', + ]; + + /** + * Records whether any log records have been added since the last flush of the rollbar notifier + * + * @var bool + */ + private $hasRecords = false; + + /** @var bool */ + protected $initialized = false; + + /** + * @param RollbarLogger $rollbarLogger RollbarLogger object constructed with valid token + */ + public function __construct(RollbarLogger $rollbarLogger, $level = Logger::ERROR, bool $bubble = true) + { + $this->rollbarLogger = $rollbarLogger; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!$this->initialized) { + // __destructor() doesn't get called on Fatal errors + register_shutdown_function(array($this, 'close')); + $this->initialized = true; + } + + $context = $record['context']; + $context = array_merge($context, $record['extra'], [ + 'level' => $this->levelMap[$record['level']], + 'monolog_level' => $record['level_name'], + 'channel' => $record['channel'], + 'datetime' => $record['datetime']->format('U'), + ]); + + if (isset($context['exception']) && $context['exception'] instanceof Throwable) { + $exception = $context['exception']; + unset($context['exception']); + $toLog = $exception; + } else { + $toLog = $record['message']; + } + + // @phpstan-ignore-next-line + $this->rollbarLogger->log($context['level'], $toLog, $context); + + $this->hasRecords = true; + } + + public function flush(): void + { + if ($this->hasRecords) { + $this->rollbarLogger->flush(); + $this->hasRecords = false; + } + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + $this->flush(); + } + + /** + * {@inheritDoc} + */ + public function reset() + { + $this->flush(); + + parent::reset(); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php new file mode 100644 index 0000000..aced9ac --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use InvalidArgumentException; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Stores logs to files that are rotated every day and a limited number of files are kept. + * + * This rotation is only intended to be used as a workaround. Using logrotate to + * handle the rotation is strongly encouraged when you can use it. + * + * @author Christophe Coevoet + * @author Jordi Boggiano + */ +class RotatingFileHandler extends StreamHandler +{ + public const FILE_PER_DAY = 'Y-m-d'; + public const FILE_PER_MONTH = 'Y-m'; + public const FILE_PER_YEAR = 'Y'; + + /** @var string */ + protected $filename; + /** @var int */ + protected $maxFiles; + /** @var bool */ + protected $mustRotate; + /** @var \DateTimeImmutable */ + protected $nextRotation; + /** @var string */ + protected $filenameFormat; + /** @var string */ + protected $dateFormat; + + /** + * @param string $filename + * @param int $maxFiles The maximal amount of files to keep (0 means unlimited) + * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) + * @param bool $useLocking Try to lock log file before doing any writes + */ + public function __construct(string $filename, int $maxFiles = 0, $level = Logger::DEBUG, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false) + { + $this->filename = Utils::canonicalizePath($filename); + $this->maxFiles = $maxFiles; + $this->nextRotation = new \DateTimeImmutable('tomorrow'); + $this->filenameFormat = '{filename}-{date}'; + $this->dateFormat = static::FILE_PER_DAY; + + parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission, $useLocking); + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + parent::close(); + + if (true === $this->mustRotate) { + $this->rotate(); + } + } + + /** + * {@inheritDoc} + */ + public function reset() + { + parent::reset(); + + if (true === $this->mustRotate) { + $this->rotate(); + } + } + + public function setFilenameFormat(string $filenameFormat, string $dateFormat): self + { + if (!preg_match('{^[Yy](([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) { + throw new InvalidArgumentException( + 'Invalid date format - format must be one of '. + 'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") '. + 'or RotatingFileHandler::FILE_PER_YEAR ("Y"), or you can set one of the '. + 'date formats using slashes, underscores and/or dots instead of dashes.' + ); + } + if (substr_count($filenameFormat, '{date}') === 0) { + throw new InvalidArgumentException( + 'Invalid filename format - format must contain at least `{date}`, because otherwise rotating is impossible.' + ); + } + $this->filenameFormat = $filenameFormat; + $this->dateFormat = $dateFormat; + $this->url = $this->getTimedFilename(); + $this->close(); + + return $this; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + // on the first record written, if the log is new, we should rotate (once per day) + if (null === $this->mustRotate) { + $this->mustRotate = null === $this->url || !file_exists($this->url); + } + + if ($this->nextRotation <= $record['datetime']) { + $this->mustRotate = true; + $this->close(); + } + + parent::write($record); + } + + /** + * Rotates the files. + */ + protected function rotate(): void + { + // update filename + $this->url = $this->getTimedFilename(); + $this->nextRotation = new \DateTimeImmutable('tomorrow'); + + // skip GC of old logs if files are unlimited + if (0 === $this->maxFiles) { + return; + } + + $logFiles = glob($this->getGlobPattern()); + if (false === $logFiles) { + // failed to glob + return; + } + + if ($this->maxFiles >= count($logFiles)) { + // no files to remove + return; + } + + // Sorting the files by name to remove the older ones + usort($logFiles, function ($a, $b) { + return strcmp($b, $a); + }); + + foreach (array_slice($logFiles, $this->maxFiles) as $file) { + if (is_writable($file)) { + // suppress errors here as unlink() might fail if two processes + // are cleaning up/rotating at the same time + set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline): bool { + return false; + }); + unlink($file); + restore_error_handler(); + } + } + + $this->mustRotate = false; + } + + protected function getTimedFilename(): string + { + $fileInfo = pathinfo($this->filename); + $timedFilename = str_replace( + ['{filename}', '{date}'], + [$fileInfo['filename'], date($this->dateFormat)], + $fileInfo['dirname'] . '/' . $this->filenameFormat + ); + + if (isset($fileInfo['extension'])) { + $timedFilename .= '.'.$fileInfo['extension']; + } + + return $timedFilename; + } + + protected function getGlobPattern(): string + { + $fileInfo = pathinfo($this->filename); + $glob = str_replace( + ['{filename}', '{date}'], + [$fileInfo['filename'], str_replace( + ['Y', 'y', 'm', 'd'], + ['[0-9][0-9][0-9][0-9]', '[0-9][0-9]', '[0-9][0-9]', '[0-9][0-9]'], + $this->dateFormat) + ], + $fileInfo['dirname'] . '/' . $this->filenameFormat + ); + if (isset($fileInfo['extension'])) { + $glob .= '.'.$fileInfo['extension']; + } + + return $glob; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php new file mode 100644 index 0000000..e86fa0d --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; + +/** + * Sampling handler + * + * A sampled event stream can be useful for logging high frequency events in + * a production environment where you only need an idea of what is happening + * and are not concerned with capturing every occurrence. Since the decision to + * handle or not handle a particular event is determined randomly, the + * resulting sampled log is not guaranteed to contain 1/N of the events that + * occurred in the application, but based on the Law of large numbers, it will + * tend to be close to this ratio with a large number of attempts. + * + * @author Bryan Davis + * @author Kunal Mehta + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +class SamplingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface +{ + use ProcessableHandlerTrait; + + /** + * @var HandlerInterface|callable + * @phpstan-var HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface + */ + protected $handler; + + /** + * @var int $factor + */ + protected $factor; + + /** + * @psalm-param HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface $handler + * + * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $samplingHandler). + * @param int $factor Sample factor (e.g. 10 means every ~10th record is sampled) + */ + public function __construct($handler, int $factor) + { + parent::__construct(); + $this->handler = $handler; + $this->factor = $factor; + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + public function isHandling(array $record): bool + { + return $this->getHandler($record)->isHandling($record); + } + + public function handle(array $record): bool + { + if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + $this->getHandler($record)->handle($record); + } + + return false === $this->bubble; + } + + /** + * Return the nested handler + * + * If the handler was provided as a factory callable, this will trigger the handler's instantiation. + * + * @phpstan-param Record|array{level: Level}|null $record + * + * @return HandlerInterface + */ + public function getHandler(array $record = null) + { + if (!$this->handler instanceof HandlerInterface) { + $this->handler = ($this->handler)($record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + return $this->handler; + } + + /** + * {@inheritDoc} + */ + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + $handler->setFormatter($formatter); + + return $this; + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } + + /** + * {@inheritDoc} + */ + public function getFormatter(): FormatterInterface + { + $handler = $this->getHandler(); + if ($handler instanceof FormattableHandlerInterface) { + return $handler->getFormatter(); + } + + throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.'); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php new file mode 100644 index 0000000..b8db34a --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * SendGridrHandler uses the SendGrid API v2 function to send Log emails, more information in https://sendgrid.com/docs/API_Reference/Web_API/mail.html + * + * @author Ricardo Fontanelli + */ +class SendGridHandler extends MailHandler +{ + /** + * The SendGrid API User + * @var string + */ + protected $apiUser; + + /** + * The SendGrid API Key + * @var string + */ + protected $apiKey; + + /** + * The email addresses to which the message will be sent + * @var string + */ + protected $from; + + /** + * The email addresses to which the message will be sent + * @var string[] + */ + protected $to; + + /** + * The subject of the email + * @var string + */ + protected $subject; + + /** + * @param string $apiUser The SendGrid API User + * @param string $apiKey The SendGrid API Key + * @param string $from The sender of the email + * @param string|string[] $to The recipients of the email + * @param string $subject The subject of the mail + */ + public function __construct(string $apiUser, string $apiKey, string $from, $to, string $subject, $level = Logger::ERROR, bool $bubble = true) + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the SendGridHandler'); + } + + parent::__construct($level, $bubble); + $this->apiUser = $apiUser; + $this->apiKey = $apiKey; + $this->from = $from; + $this->to = (array) $to; + $this->subject = $subject; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $message = []; + $message['api_user'] = $this->apiUser; + $message['api_key'] = $this->apiKey; + $message['from'] = $this->from; + foreach ($this->to as $recipient) { + $message['to[]'] = $recipient; + } + $message['subject'] = $this->subject; + $message['date'] = date('r'); + + if ($this->isHtmlBody($content)) { + $message['html'] = $content; + } else { + $message['text'] = $content; + } + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'https://api.sendgrid.com/api/mail.send.json'); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($message)); + Curl\Util::execute($ch, 2); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php new file mode 100644 index 0000000..0d05668 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php @@ -0,0 +1,387 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\Slack; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Slack record utility helping to log to Slack webhooks or API. + * + * @author Greg Kedzierski + * @author Haralan Dobrev + * @see https://api.slack.com/incoming-webhooks + * @see https://api.slack.com/docs/message-attachments + * + * @phpstan-import-type FormattedRecord from \Monolog\Handler\AbstractProcessingHandler + * @phpstan-import-type Record from \Monolog\Logger + */ +class SlackRecord +{ + public const COLOR_DANGER = 'danger'; + + public const COLOR_WARNING = 'warning'; + + public const COLOR_GOOD = 'good'; + + public const COLOR_DEFAULT = '#e3e4e6'; + + /** + * Slack channel (encoded ID or name) + * @var string|null + */ + private $channel; + + /** + * Name of a bot + * @var string|null + */ + private $username; + + /** + * User icon e.g. 'ghost', 'http://example.com/user.png' + * @var string|null + */ + private $userIcon; + + /** + * Whether the message should be added to Slack as attachment (plain text otherwise) + * @var bool + */ + private $useAttachment; + + /** + * Whether the the context/extra messages added to Slack as attachments are in a short style + * @var bool + */ + private $useShortAttachment; + + /** + * Whether the attachment should include context and extra data + * @var bool + */ + private $includeContextAndExtra; + + /** + * Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + * @var string[] + */ + private $excludeFields; + + /** + * @var ?FormatterInterface + */ + private $formatter; + + /** + * @var NormalizerFormatter + */ + private $normalizerFormatter; + + /** + * @param string[] $excludeFields + */ + public function __construct( + ?string $channel = null, + ?string $username = null, + bool $useAttachment = true, + ?string $userIcon = null, + bool $useShortAttachment = false, + bool $includeContextAndExtra = false, + array $excludeFields = array(), + FormatterInterface $formatter = null + ) { + $this + ->setChannel($channel) + ->setUsername($username) + ->useAttachment($useAttachment) + ->setUserIcon($userIcon) + ->useShortAttachment($useShortAttachment) + ->includeContextAndExtra($includeContextAndExtra) + ->excludeFields($excludeFields) + ->setFormatter($formatter); + + if ($this->includeContextAndExtra) { + $this->normalizerFormatter = new NormalizerFormatter(); + } + } + + /** + * Returns required data in format that Slack + * is expecting. + * + * @phpstan-param FormattedRecord $record + * @phpstan-return mixed[] + */ + public function getSlackData(array $record): array + { + $dataArray = array(); + $record = $this->removeExcludedFields($record); + + if ($this->username) { + $dataArray['username'] = $this->username; + } + + if ($this->channel) { + $dataArray['channel'] = $this->channel; + } + + if ($this->formatter && !$this->useAttachment) { + /** @phpstan-ignore-next-line */ + $message = $this->formatter->format($record); + } else { + $message = $record['message']; + } + + if ($this->useAttachment) { + $attachment = array( + 'fallback' => $message, + 'text' => $message, + 'color' => $this->getAttachmentColor($record['level']), + 'fields' => array(), + 'mrkdwn_in' => array('fields'), + 'ts' => $record['datetime']->getTimestamp(), + 'footer' => $this->username, + 'footer_icon' => $this->userIcon, + ); + + if ($this->useShortAttachment) { + $attachment['title'] = $record['level_name']; + } else { + $attachment['title'] = 'Message'; + $attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name']); + } + + if ($this->includeContextAndExtra) { + foreach (array('extra', 'context') as $key) { + if (empty($record[$key])) { + continue; + } + + if ($this->useShortAttachment) { + $attachment['fields'][] = $this->generateAttachmentField( + (string) $key, + $record[$key] + ); + } else { + // Add all extra fields as individual fields in attachment + $attachment['fields'] = array_merge( + $attachment['fields'], + $this->generateAttachmentFields($record[$key]) + ); + } + } + } + + $dataArray['attachments'] = array($attachment); + } else { + $dataArray['text'] = $message; + } + + if ($this->userIcon) { + if (filter_var($this->userIcon, FILTER_VALIDATE_URL)) { + $dataArray['icon_url'] = $this->userIcon; + } else { + $dataArray['icon_emoji'] = ":{$this->userIcon}:"; + } + } + + return $dataArray; + } + + /** + * Returns a Slack message attachment color associated with + * provided level. + */ + public function getAttachmentColor(int $level): string + { + switch (true) { + case $level >= Logger::ERROR: + return static::COLOR_DANGER; + case $level >= Logger::WARNING: + return static::COLOR_WARNING; + case $level >= Logger::INFO: + return static::COLOR_GOOD; + default: + return static::COLOR_DEFAULT; + } + } + + /** + * Stringifies an array of key/value pairs to be used in attachment fields + * + * @param mixed[] $fields + */ + public function stringify(array $fields): string + { + /** @var Record $fields */ + $normalized = $this->normalizerFormatter->format($fields); + + $hasSecondDimension = count(array_filter($normalized, 'is_array')); + $hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric')); + + return $hasSecondDimension || $hasNonNumericKeys + ? Utils::jsonEncode($normalized, JSON_PRETTY_PRINT|Utils::DEFAULT_JSON_FLAGS) + : Utils::jsonEncode($normalized, Utils::DEFAULT_JSON_FLAGS); + } + + /** + * Channel used by the bot when posting + * + * @param ?string $channel + * + * @return static + */ + public function setChannel(?string $channel = null): self + { + $this->channel = $channel; + + return $this; + } + + /** + * Username used by the bot when posting + * + * @param ?string $username + * + * @return static + */ + public function setUsername(?string $username = null): self + { + $this->username = $username; + + return $this; + } + + public function useAttachment(bool $useAttachment = true): self + { + $this->useAttachment = $useAttachment; + + return $this; + } + + public function setUserIcon(?string $userIcon = null): self + { + $this->userIcon = $userIcon; + + if (\is_string($userIcon)) { + $this->userIcon = trim($userIcon, ':'); + } + + return $this; + } + + public function useShortAttachment(bool $useShortAttachment = false): self + { + $this->useShortAttachment = $useShortAttachment; + + return $this; + } + + public function includeContextAndExtra(bool $includeContextAndExtra = false): self + { + $this->includeContextAndExtra = $includeContextAndExtra; + + if ($this->includeContextAndExtra) { + $this->normalizerFormatter = new NormalizerFormatter(); + } + + return $this; + } + + /** + * @param string[] $excludeFields + */ + public function excludeFields(array $excludeFields = []): self + { + $this->excludeFields = $excludeFields; + + return $this; + } + + public function setFormatter(?FormatterInterface $formatter = null): self + { + $this->formatter = $formatter; + + return $this; + } + + /** + * Generates attachment field + * + * @param string|mixed[] $value + * + * @return array{title: string, value: string, short: false} + */ + private function generateAttachmentField(string $title, $value): array + { + $value = is_array($value) + ? sprintf('```%s```', substr($this->stringify($value), 0, 1990)) + : $value; + + return array( + 'title' => ucfirst($title), + 'value' => $value, + 'short' => false, + ); + } + + /** + * Generates a collection of attachment fields from array + * + * @param mixed[] $data + * + * @return array + */ + private function generateAttachmentFields(array $data): array + { + /** @var Record $data */ + $normalized = $this->normalizerFormatter->format($data); + + $fields = array(); + foreach ($normalized as $key => $value) { + $fields[] = $this->generateAttachmentField((string) $key, $value); + } + + return $fields; + } + + /** + * Get a copy of record with fields excluded according to $this->excludeFields + * + * @phpstan-param FormattedRecord $record + * + * @return mixed[] + */ + private function removeExcludedFields(array $record): array + { + foreach ($this->excludeFields as $field) { + $keys = explode('.', $field); + $node = &$record; + $lastKey = end($keys); + foreach ($keys as $key) { + if (!isset($node[$key])) { + break; + } + if ($lastKey === $key) { + unset($node[$key]); + break; + } + $node = &$node[$key]; + } + } + + return $record; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php new file mode 100644 index 0000000..0e1e813 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php @@ -0,0 +1,256 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Handler\Slack\SlackRecord; + +/** + * Sends notifications through Slack API + * + * @author Greg Kedzierski + * @see https://api.slack.com/ + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class SlackHandler extends SocketHandler +{ + /** + * Slack API token + * @var string + */ + private $token; + + /** + * Instance of the SlackRecord util class preparing data for Slack API. + * @var SlackRecord + */ + private $slackRecord; + + /** + * @param string $token Slack API token + * @param string $channel Slack channel (encoded ID or name) + * @param string|null $username Name of a bot + * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) + * @param string|null $iconEmoji The emoji name to use (or null) + * @param bool $useShortAttachment Whether the context/extra messages added to Slack as attachments are in a short style + * @param bool $includeContextAndExtra Whether the attachment should include context and extra data + * @param string[] $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + * @throws MissingExtensionException If no OpenSSL PHP extension configured + */ + public function __construct( + string $token, + string $channel, + ?string $username = null, + bool $useAttachment = true, + ?string $iconEmoji = null, + $level = Logger::CRITICAL, + bool $bubble = true, + bool $useShortAttachment = false, + bool $includeContextAndExtra = false, + array $excludeFields = array(), + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the SlackHandler'); + } + + parent::__construct( + 'ssl://slack.com:443', + $level, + $bubble, + $persistent, + $timeout, + $writingTimeout, + $connectionTimeout, + $chunkSize + ); + + $this->slackRecord = new SlackRecord( + $channel, + $username, + $useAttachment, + $iconEmoji, + $useShortAttachment, + $includeContextAndExtra, + $excludeFields + ); + + $this->token = $token; + } + + public function getSlackRecord(): SlackRecord + { + return $this->slackRecord; + } + + public function getToken(): string + { + return $this->token; + } + + /** + * {@inheritDoc} + */ + protected function generateDataStream(array $record): string + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the body of API call + * + * @phpstan-param FormattedRecord $record + */ + private function buildContent(array $record): string + { + $dataArray = $this->prepareContentData($record); + + return http_build_query($dataArray); + } + + /** + * @phpstan-param FormattedRecord $record + * @return string[] + */ + protected function prepareContentData(array $record): array + { + $dataArray = $this->slackRecord->getSlackData($record); + $dataArray['token'] = $this->token; + + if (!empty($dataArray['attachments'])) { + $dataArray['attachments'] = Utils::jsonEncode($dataArray['attachments']); + } + + return $dataArray; + } + + /** + * Builds the header of the API Call + */ + private function buildHeader(string $content): string + { + $header = "POST /api/chat.postMessage HTTP/1.1\r\n"; + $header .= "Host: slack.com\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + parent::write($record); + $this->finalizeWrite(); + } + + /** + * Finalizes the request by reading some bytes and then closing the socket + * + * If we do not read some but close the socket too early, slack sometimes + * drops the request entirely. + */ + protected function finalizeWrite(): void + { + $res = $this->getResource(); + if (is_resource($res)) { + @fread($res, 2048); + } + $this->closeSocket(); + } + + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + parent::setFormatter($formatter); + $this->slackRecord->setFormatter($formatter); + + return $this; + } + + public function getFormatter(): FormatterInterface + { + $formatter = parent::getFormatter(); + $this->slackRecord->setFormatter($formatter); + + return $formatter; + } + + /** + * Channel used by the bot when posting + */ + public function setChannel(string $channel): self + { + $this->slackRecord->setChannel($channel); + + return $this; + } + + /** + * Username used by the bot when posting + */ + public function setUsername(string $username): self + { + $this->slackRecord->setUsername($username); + + return $this; + } + + public function useAttachment(bool $useAttachment): self + { + $this->slackRecord->useAttachment($useAttachment); + + return $this; + } + + public function setIconEmoji(string $iconEmoji): self + { + $this->slackRecord->setUserIcon($iconEmoji); + + return $this; + } + + public function useShortAttachment(bool $useShortAttachment): self + { + $this->slackRecord->useShortAttachment($useShortAttachment); + + return $this; + } + + public function includeContextAndExtra(bool $includeContextAndExtra): self + { + $this->slackRecord->includeContextAndExtra($includeContextAndExtra); + + return $this; + } + + /** + * @param string[] $excludeFields + */ + public function excludeFields(array $excludeFields): self + { + $this->slackRecord->excludeFields($excludeFields); + + return $this; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php new file mode 100644 index 0000000..f49be34 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Handler\Slack\SlackRecord; + +/** + * Sends notifications through Slack Webhooks + * + * @author Haralan Dobrev + * @see https://api.slack.com/incoming-webhooks + */ +class SlackWebhookHandler extends AbstractProcessingHandler +{ + /** + * Slack Webhook token + * @var string + */ + private $webhookUrl; + + /** + * Instance of the SlackRecord util class preparing data for Slack API. + * @var SlackRecord + */ + private $slackRecord; + + /** + * @param string $webhookUrl Slack Webhook URL + * @param string|null $channel Slack channel (encoded ID or name) + * @param string|null $username Name of a bot + * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) + * @param string|null $iconEmoji The emoji name to use (or null) + * @param bool $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style + * @param bool $includeContextAndExtra Whether the attachment should include context and extra data + * @param string[] $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + */ + public function __construct( + string $webhookUrl, + ?string $channel = null, + ?string $username = null, + bool $useAttachment = true, + ?string $iconEmoji = null, + bool $useShortAttachment = false, + bool $includeContextAndExtra = false, + $level = Logger::CRITICAL, + bool $bubble = true, + array $excludeFields = array() + ) { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the SlackWebhookHandler'); + } + + parent::__construct($level, $bubble); + + $this->webhookUrl = $webhookUrl; + + $this->slackRecord = new SlackRecord( + $channel, + $username, + $useAttachment, + $iconEmoji, + $useShortAttachment, + $includeContextAndExtra, + $excludeFields + ); + } + + public function getSlackRecord(): SlackRecord + { + return $this->slackRecord; + } + + public function getWebhookUrl(): string + { + return $this->webhookUrl; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $postData = $this->slackRecord->getSlackData($record); + $postString = Utils::jsonEncode($postData); + + $ch = curl_init(); + $options = array( + CURLOPT_URL => $this->webhookUrl, + CURLOPT_POST => true, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => array('Content-type: application/json'), + CURLOPT_POSTFIELDS => $postString, + ); + if (defined('CURLOPT_SAFE_UPLOAD')) { + $options[CURLOPT_SAFE_UPLOAD] = true; + } + + curl_setopt_array($ch, $options); + + Curl\Util::execute($ch); + } + + public function setFormatter(FormatterInterface $formatter): HandlerInterface + { + parent::setFormatter($formatter); + $this->slackRecord->setFormatter($formatter); + + return $this; + } + + public function getFormatter(): FormatterInterface + { + $formatter = parent::getFormatter(); + $this->slackRecord->setFormatter($formatter); + + return $formatter; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php new file mode 100644 index 0000000..1e309f8 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php @@ -0,0 +1,448 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores to any socket - uses fsockopen() or pfsockopen(). + * + * @author Pablo de Leon Belloc + * @see http://php.net/manual/en/function.fsockopen.php + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class SocketHandler extends AbstractProcessingHandler +{ + /** @var string */ + private $connectionString; + /** @var float */ + private $connectionTimeout; + /** @var resource|null */ + private $resource; + /** @var float */ + private $timeout; + /** @var float */ + private $writingTimeout; + /** @var ?int */ + private $lastSentBytes = null; + /** @var ?int */ + private $chunkSize; + /** @var bool */ + private $persistent; + /** @var ?int */ + private $errno = null; + /** @var ?string */ + private $errstr = null; + /** @var ?float */ + private $lastWritingAt = null; + + /** + * @param string $connectionString Socket connection string + * @param bool $persistent Flag to enable/disable persistent connections + * @param float $timeout Socket timeout to wait until the request is being aborted + * @param float $writingTimeout Socket timeout to wait until the request should've been sent/written + * @param float|null $connectionTimeout Socket connect timeout to wait until the connection should've been + * established + * @param int|null $chunkSize Sets the chunk size. Only has effect during connection in the writing cycle + * + * @throws \InvalidArgumentException If an invalid timeout value (less than 0) is passed. + */ + public function __construct( + string $connectionString, + $level = Logger::DEBUG, + bool $bubble = true, + bool $persistent = false, + float $timeout = 0.0, + float $writingTimeout = 10.0, + ?float $connectionTimeout = null, + ?int $chunkSize = null + ) { + parent::__construct($level, $bubble); + $this->connectionString = $connectionString; + + if ($connectionTimeout !== null) { + $this->validateTimeout($connectionTimeout); + } + + $this->connectionTimeout = $connectionTimeout ?? (float) ini_get('default_socket_timeout'); + $this->persistent = $persistent; + $this->validateTimeout($timeout); + $this->timeout = $timeout; + $this->validateTimeout($writingTimeout); + $this->writingTimeout = $writingTimeout; + $this->chunkSize = $chunkSize; + } + + /** + * Connect (if necessary) and write to the socket + * + * {@inheritDoc} + * + * @throws \UnexpectedValueException + * @throws \RuntimeException + */ + protected function write(array $record): void + { + $this->connectIfNotConnected(); + $data = $this->generateDataStream($record); + $this->writeToSocket($data); + } + + /** + * We will not close a PersistentSocket instance so it can be reused in other requests. + */ + public function close(): void + { + if (!$this->isPersistent()) { + $this->closeSocket(); + } + } + + /** + * Close socket, if open + */ + public function closeSocket(): void + { + if (is_resource($this->resource)) { + fclose($this->resource); + $this->resource = null; + } + } + + /** + * Set socket connection to be persistent. It only has effect before the connection is initiated. + */ + public function setPersistent(bool $persistent): self + { + $this->persistent = $persistent; + + return $this; + } + + /** + * Set connection timeout. Only has effect before we connect. + * + * @see http://php.net/manual/en/function.fsockopen.php + */ + public function setConnectionTimeout(float $seconds): self + { + $this->validateTimeout($seconds); + $this->connectionTimeout = $seconds; + + return $this; + } + + /** + * Set write timeout. Only has effect before we connect. + * + * @see http://php.net/manual/en/function.stream-set-timeout.php + */ + public function setTimeout(float $seconds): self + { + $this->validateTimeout($seconds); + $this->timeout = $seconds; + + return $this; + } + + /** + * Set writing timeout. Only has effect during connection in the writing cycle. + * + * @param float $seconds 0 for no timeout + */ + public function setWritingTimeout(float $seconds): self + { + $this->validateTimeout($seconds); + $this->writingTimeout = $seconds; + + return $this; + } + + /** + * Set chunk size. Only has effect during connection in the writing cycle. + */ + public function setChunkSize(int $bytes): self + { + $this->chunkSize = $bytes; + + return $this; + } + + /** + * Get current connection string + */ + public function getConnectionString(): string + { + return $this->connectionString; + } + + /** + * Get persistent setting + */ + public function isPersistent(): bool + { + return $this->persistent; + } + + /** + * Get current connection timeout setting + */ + public function getConnectionTimeout(): float + { + return $this->connectionTimeout; + } + + /** + * Get current in-transfer timeout + */ + public function getTimeout(): float + { + return $this->timeout; + } + + /** + * Get current local writing timeout + * + * @return float + */ + public function getWritingTimeout(): float + { + return $this->writingTimeout; + } + + /** + * Get current chunk size + */ + public function getChunkSize(): ?int + { + return $this->chunkSize; + } + + /** + * Check to see if the socket is currently available. + * + * UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details. + */ + public function isConnected(): bool + { + return is_resource($this->resource) + && !feof($this->resource); // on TCP - other party can close connection. + } + + /** + * Wrapper to allow mocking + * + * @return resource|false + */ + protected function pfsockopen() + { + return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + + /** + * Wrapper to allow mocking + * + * @return resource|false + */ + protected function fsockopen() + { + return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + + /** + * Wrapper to allow mocking + * + * @see http://php.net/manual/en/function.stream-set-timeout.php + * + * @return bool + */ + protected function streamSetTimeout() + { + $seconds = floor($this->timeout); + $microseconds = round(($this->timeout - $seconds) * 1e6); + + if (!is_resource($this->resource)) { + throw new \LogicException('streamSetTimeout called but $this->resource is not a resource'); + } + + return stream_set_timeout($this->resource, (int) $seconds, (int) $microseconds); + } + + /** + * Wrapper to allow mocking + * + * @see http://php.net/manual/en/function.stream-set-chunk-size.php + * + * @return int|bool + */ + protected function streamSetChunkSize() + { + if (!is_resource($this->resource)) { + throw new \LogicException('streamSetChunkSize called but $this->resource is not a resource'); + } + + if (null === $this->chunkSize) { + throw new \LogicException('streamSetChunkSize called but $this->chunkSize is not set'); + } + + return stream_set_chunk_size($this->resource, $this->chunkSize); + } + + /** + * Wrapper to allow mocking + * + * @return int|bool + */ + protected function fwrite(string $data) + { + if (!is_resource($this->resource)) { + throw new \LogicException('fwrite called but $this->resource is not a resource'); + } + + return @fwrite($this->resource, $data); + } + + /** + * Wrapper to allow mocking + * + * @return mixed[]|bool + */ + protected function streamGetMetadata() + { + if (!is_resource($this->resource)) { + throw new \LogicException('streamGetMetadata called but $this->resource is not a resource'); + } + + return stream_get_meta_data($this->resource); + } + + private function validateTimeout(float $value): void + { + if ($value < 0) { + throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got $value)"); + } + } + + private function connectIfNotConnected(): void + { + if ($this->isConnected()) { + return; + } + $this->connect(); + } + + /** + * @phpstan-param FormattedRecord $record + */ + protected function generateDataStream(array $record): string + { + return (string) $record['formatted']; + } + + /** + * @return resource|null + */ + protected function getResource() + { + return $this->resource; + } + + private function connect(): void + { + $this->createSocketResource(); + $this->setSocketTimeout(); + $this->setStreamChunkSize(); + } + + private function createSocketResource(): void + { + if ($this->isPersistent()) { + $resource = $this->pfsockopen(); + } else { + $resource = $this->fsockopen(); + } + if (is_bool($resource)) { + throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)"); + } + $this->resource = $resource; + } + + private function setSocketTimeout(): void + { + if (!$this->streamSetTimeout()) { + throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()"); + } + } + + private function setStreamChunkSize(): void + { + if ($this->chunkSize && !$this->streamSetChunkSize()) { + throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()"); + } + } + + private function writeToSocket(string $data): void + { + $length = strlen($data); + $sent = 0; + $this->lastSentBytes = $sent; + while ($this->isConnected() && $sent < $length) { + if (0 == $sent) { + $chunk = $this->fwrite($data); + } else { + $chunk = $this->fwrite(substr($data, $sent)); + } + if ($chunk === false) { + throw new \RuntimeException("Could not write to socket"); + } + $sent += $chunk; + $socketInfo = $this->streamGetMetadata(); + if (is_array($socketInfo) && $socketInfo['timed_out']) { + throw new \RuntimeException("Write timed-out"); + } + + if ($this->writingIsTimedOut($sent)) { + throw new \RuntimeException("Write timed-out, no data sent for `{$this->writingTimeout}` seconds, probably we got disconnected (sent $sent of $length)"); + } + } + if (!$this->isConnected() && $sent < $length) { + throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)"); + } + } + + private function writingIsTimedOut(int $sent): bool + { + // convert to ms + if (0.0 == $this->writingTimeout) { + return false; + } + + if ($sent !== $this->lastSentBytes) { + $this->lastWritingAt = microtime(true); + $this->lastSentBytes = $sent; + + return false; + } else { + usleep(100); + } + + if ((microtime(true) - $this->lastWritingAt) >= $this->writingTimeout) { + $this->closeSocket(); + + return true; + } + + return false; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.php new file mode 100644 index 0000000..9ce8737 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Aws\Sqs\SqsClient; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Writes to any sqs queue. + * + * @author Martijn van Calker + */ +class SqsHandler extends AbstractProcessingHandler +{ + /** 256 KB in bytes - maximum message size in SQS */ + protected const MAX_MESSAGE_SIZE = 262144; + /** 100 KB in bytes - head message size for new error log */ + protected const HEAD_MESSAGE_SIZE = 102400; + + /** @var SqsClient */ + private $client; + /** @var string */ + private $queueUrl; + + public function __construct(SqsClient $sqsClient, string $queueUrl, $level = Logger::DEBUG, bool $bubble = true) + { + parent::__construct($level, $bubble); + + $this->client = $sqsClient; + $this->queueUrl = $queueUrl; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!isset($record['formatted']) || 'string' !== gettype($record['formatted'])) { + throw new \InvalidArgumentException('SqsHandler accepts only formatted records as a string' . Utils::getRecordMessageForException($record)); + } + + $messageBody = $record['formatted']; + if (strlen($messageBody) >= static::MAX_MESSAGE_SIZE) { + $messageBody = Utils::substr($messageBody, 0, static::HEAD_MESSAGE_SIZE); + } + + $this->client->sendMessage([ + 'QueueUrl' => $this->queueUrl, + 'MessageBody' => $messageBody, + ]); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php new file mode 100644 index 0000000..574979d --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; + +/** + * Stores to any stream resource + * + * Can be used to store into php://stderr, remote and local files, etc. + * + * @author Jordi Boggiano + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class StreamHandler extends AbstractProcessingHandler +{ + /** @const int */ + protected const MAX_CHUNK_SIZE = 2147483647; + /** @const int 10MB */ + protected const DEFAULT_CHUNK_SIZE = 10 * 1024 * 1024; + /** @var int */ + protected $streamChunkSize; + /** @var resource|null */ + protected $stream; + /** @var ?string */ + protected $url = null; + /** @var ?string */ + private $errorMessage = null; + /** @var ?int */ + protected $filePermission; + /** @var bool */ + protected $useLocking; + /** @var true|null */ + private $dirCreated = null; + + /** + * @param resource|string $stream If a missing path can't be created, an UnexpectedValueException will be thrown on first write + * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) + * @param bool $useLocking Try to lock log file before doing any writes + * + * @throws \InvalidArgumentException If stream is not a resource or string + */ + public function __construct($stream, $level = Logger::DEBUG, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false) + { + parent::__construct($level, $bubble); + + if (($phpMemoryLimit = Utils::expandIniShorthandBytes(ini_get('memory_limit'))) !== false) { + if ($phpMemoryLimit > 0) { + // use max 10% of allowed memory for the chunk size, and at least 100KB + $this->streamChunkSize = min(static::MAX_CHUNK_SIZE, max((int) ($phpMemoryLimit / 10), 100 * 1024)); + } else { + // memory is unlimited, set to the default 10MB + $this->streamChunkSize = static::DEFAULT_CHUNK_SIZE; + } + } else { + // no memory limit information, set to the default 10MB + $this->streamChunkSize = static::DEFAULT_CHUNK_SIZE; + } + + if (is_resource($stream)) { + $this->stream = $stream; + + stream_set_chunk_size($this->stream, $this->streamChunkSize); + } elseif (is_string($stream)) { + $this->url = Utils::canonicalizePath($stream); + } else { + throw new \InvalidArgumentException('A stream must either be a resource or a string.'); + } + + $this->filePermission = $filePermission; + $this->useLocking = $useLocking; + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + if ($this->url && is_resource($this->stream)) { + fclose($this->stream); + } + $this->stream = null; + $this->dirCreated = null; + } + + /** + * Return the currently active stream if it is open + * + * @return resource|null + */ + public function getStream() + { + return $this->stream; + } + + /** + * Return the stream URL if it was configured with a URL and not an active resource + * + * @return string|null + */ + public function getUrl(): ?string + { + return $this->url; + } + + /** + * @return int + */ + public function getStreamChunkSize(): int + { + return $this->streamChunkSize; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!is_resource($this->stream)) { + $url = $this->url; + if (null === $url || '' === $url) { + throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().' . Utils::getRecordMessageForException($record)); + } + $this->createDir($url); + $this->errorMessage = null; + set_error_handler([$this, 'customErrorHandler']); + $stream = fopen($url, 'a'); + if ($this->filePermission !== null) { + @chmod($url, $this->filePermission); + } + restore_error_handler(); + if (!is_resource($stream)) { + $this->stream = null; + + throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened in append mode: '.$this->errorMessage, $url) . Utils::getRecordMessageForException($record)); + } + stream_set_chunk_size($stream, $this->streamChunkSize); + $this->stream = $stream; + } + + $stream = $this->stream; + if (!is_resource($stream)) { + throw new \LogicException('No stream was opened yet' . Utils::getRecordMessageForException($record)); + } + + if ($this->useLocking) { + // ignoring errors here, there's not much we can do about them + flock($stream, LOCK_EX); + } + + $this->streamWrite($stream, $record); + + if ($this->useLocking) { + flock($stream, LOCK_UN); + } + } + + /** + * Write to stream + * @param resource $stream + * @param array $record + * + * @phpstan-param FormattedRecord $record + */ + protected function streamWrite($stream, array $record): void + { + fwrite($stream, (string) $record['formatted']); + } + + private function customErrorHandler(int $code, string $msg): bool + { + $this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg); + + return true; + } + + private function getDirFromStream(string $stream): ?string + { + $pos = strpos($stream, '://'); + if ($pos === false) { + return dirname($stream); + } + + if ('file://' === substr($stream, 0, 7)) { + return dirname(substr($stream, 7)); + } + + return null; + } + + private function createDir(string $url): void + { + // Do not try to create dir if it has already been tried. + if ($this->dirCreated) { + return; + } + + $dir = $this->getDirFromStream($url); + if (null !== $dir && !is_dir($dir)) { + $this->errorMessage = null; + set_error_handler([$this, 'customErrorHandler']); + $status = mkdir($dir, 0777, true); + restore_error_handler(); + if (false === $status && !is_dir($dir) && strpos((string) $this->errorMessage, 'File exists') === false) { + throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and it could not be created: '.$this->errorMessage, $dir)); + } + } + $this->dirCreated = true; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php new file mode 100644 index 0000000..038d684 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; +use Swift_Message; +use Swift; + +/** + * SwiftMailerHandler uses Swift_Mailer to send the emails + * + * @author Gyula Sallai + * + * @phpstan-import-type Record from \Monolog\Logger + * @deprecated Since Monolog 2.6. Use SymfonyMailerHandler instead. + */ +class SwiftMailerHandler extends MailHandler +{ + /** @var \Swift_Mailer */ + protected $mailer; + /** @var Swift_Message|callable(string, Record[]): Swift_Message */ + private $messageTemplate; + + /** + * @psalm-param Swift_Message|callable(string, Record[]): Swift_Message $message + * + * @param \Swift_Mailer $mailer The mailer to use + * @param callable|Swift_Message $message An example message for real messages, only the body will be replaced + */ + public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, bool $bubble = true) + { + parent::__construct($level, $bubble); + + @trigger_error('The SwiftMailerHandler is deprecated since Monolog 2.6. Use SymfonyMailerHandler instead.', E_USER_DEPRECATED); + + $this->mailer = $mailer; + $this->messageTemplate = $message; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $this->mailer->send($this->buildMessage($content, $records)); + } + + /** + * Gets the formatter for the Swift_Message subject. + * + * @param string|null $format The format of the subject + */ + protected function getSubjectFormatter(?string $format): FormatterInterface + { + return new LineFormatter($format); + } + + /** + * Creates instance of Swift_Message to be sent + * + * @param string $content formatted email body to be sent + * @param array $records Log records that formed the content + * @return Swift_Message + * + * @phpstan-param Record[] $records + */ + protected function buildMessage(string $content, array $records): Swift_Message + { + $message = null; + if ($this->messageTemplate instanceof Swift_Message) { + $message = clone $this->messageTemplate; + $message->generateId(); + } elseif (is_callable($this->messageTemplate)) { + $message = ($this->messageTemplate)($content, $records); + } + + if (!$message instanceof Swift_Message) { + $record = reset($records); + throw new \InvalidArgumentException('Could not resolve message as instance of Swift_Message or a callable returning it' . ($record ? Utils::getRecordMessageForException($record) : '')); + } + + if ($records) { + $subjectFormatter = $this->getSubjectFormatter($message->getSubject()); + $message->setSubject($subjectFormatter->format($this->getHighestRecord($records))); + } + + $mime = 'text/plain'; + if ($this->isHtmlBody($content)) { + $mime = 'text/html'; + } + + $message->setBody($content, $mime); + /** @phpstan-ignore-next-line */ + if (version_compare(Swift::VERSION, '6.0.0', '>=')) { + $message->setDate(new \DateTimeImmutable()); + } else { + /** @phpstan-ignore-next-line */ + $message->setDate(time()); + } + + return $message; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php new file mode 100644 index 0000000..fb5cf18 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Mailer\Transport\TransportInterface; +use Symfony\Component\Mime\Email; + +/** + * SymfonyMailerHandler uses Symfony's Mailer component to send the emails + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class SymfonyMailerHandler extends MailHandler +{ + /** @var MailerInterface|TransportInterface */ + protected $mailer; + /** @var Email|callable(string, Record[]): Email */ + private $emailTemplate; + + /** + * @psalm-param Email|callable(string, Record[]): Email $email + * + * @param MailerInterface|TransportInterface $mailer The mailer to use + * @param callable|Email $email An email template, the subject/body will be replaced + */ + public function __construct($mailer, $email, $level = Logger::ERROR, bool $bubble = true) + { + parent::__construct($level, $bubble); + + $this->mailer = $mailer; + $this->emailTemplate = $email; + } + + /** + * {@inheritDoc} + */ + protected function send(string $content, array $records): void + { + $this->mailer->send($this->buildMessage($content, $records)); + } + + /** + * Gets the formatter for the Swift_Message subject. + * + * @param string|null $format The format of the subject + */ + protected function getSubjectFormatter(?string $format): FormatterInterface + { + return new LineFormatter($format); + } + + /** + * Creates instance of Email to be sent + * + * @param string $content formatted email body to be sent + * @param array $records Log records that formed the content + * + * @phpstan-param Record[] $records + */ + protected function buildMessage(string $content, array $records): Email + { + $message = null; + if ($this->emailTemplate instanceof Email) { + $message = clone $this->emailTemplate; + } elseif (is_callable($this->emailTemplate)) { + $message = ($this->emailTemplate)($content, $records); + } + + if (!$message instanceof Email) { + $record = reset($records); + throw new \InvalidArgumentException('Could not resolve message as instance of Email or a callable returning it' . ($record ? Utils::getRecordMessageForException($record) : '')); + } + + if ($records) { + $subjectFormatter = $this->getSubjectFormatter($message->getSubject()); + $message->subject($subjectFormatter->format($this->getHighestRecord($records))); + } + + if ($this->isHtmlBody($content)) { + if (null !== ($charset = $message->getHtmlCharset())) { + $message->html($content, $charset); + } else { + $message->html($content); + } + } else { + if (null !== ($charset = $message->getTextCharset())) { + $message->text($content, $charset); + } else { + $message->text($content); + } + } + + return $message->date(new \DateTimeImmutable()); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php new file mode 100644 index 0000000..8d6bfcb --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Utils; + +/** + * Logs to syslog service. + * + * usage example: + * + * $log = new Logger('application'); + * $syslog = new SyslogHandler('myfacility', 'local6'); + * $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%"); + * $syslog->setFormatter($formatter); + * $log->pushHandler($syslog); + * + * @author Sven Paulus + */ +class SyslogHandler extends AbstractSyslogHandler +{ + /** @var string */ + protected $ident; + /** @var int */ + protected $logopts; + + /** + * @param string $ident + * @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant + * @param int $logopts Option flags for the openlog() call, defaults to LOG_PID + */ + public function __construct(string $ident, $facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true, int $logopts = LOG_PID) + { + parent::__construct($facility, $level, $bubble); + + $this->ident = $ident; + $this->logopts = $logopts; + } + + /** + * {@inheritDoc} + */ + public function close(): void + { + closelog(); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + if (!openlog($this->ident, $this->logopts, $this->facility)) { + throw new \LogicException('Can\'t open syslog for ident "'.$this->ident.'" and facility "'.$this->facility.'"' . Utils::getRecordMessageForException($record)); + } + syslog($this->logLevels[$record['level']], (string) $record['formatted']); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php new file mode 100644 index 0000000..edd8282 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\SyslogUdp; + +use Monolog\Utils; +use Socket; + +class UdpSocket +{ + protected const DATAGRAM_MAX_LENGTH = 65023; + + /** @var string */ + protected $ip; + /** @var int */ + protected $port; + /** @var resource|Socket|null */ + protected $socket = null; + + public function __construct(string $ip, int $port = 514) + { + $this->ip = $ip; + $this->port = $port; + } + + /** + * @param string $line + * @param string $header + * @return void + */ + public function write($line, $header = "") + { + $this->send($this->assembleMessage($line, $header)); + } + + public function close(): void + { + if (is_resource($this->socket) || $this->socket instanceof Socket) { + socket_close($this->socket); + $this->socket = null; + } + } + + /** + * @return resource|Socket + */ + protected function getSocket() + { + if (null !== $this->socket) { + return $this->socket; + } + + $domain = AF_INET; + $protocol = SOL_UDP; + // Check if we are using unix sockets. + if ($this->port === 0) { + $domain = AF_UNIX; + $protocol = IPPROTO_IP; + } + + $this->socket = socket_create($domain, SOCK_DGRAM, $protocol) ?: null; + if (null === $this->socket) { + throw new \RuntimeException('The UdpSocket to '.$this->ip.':'.$this->port.' could not be opened via socket_create'); + } + + return $this->socket; + } + + protected function send(string $chunk): void + { + socket_sendto($this->getSocket(), $chunk, strlen($chunk), $flags = 0, $this->ip, $this->port); + } + + protected function assembleMessage(string $line, string $header): string + { + $chunkSize = static::DATAGRAM_MAX_LENGTH - strlen($header); + + return $header . Utils::substr($line, 0, $chunkSize); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php new file mode 100644 index 0000000..c2d09b1 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use DateTimeInterface; +use Monolog\Logger; +use Monolog\Handler\SyslogUdp\UdpSocket; +use Monolog\Utils; + +/** + * A Handler for logging to a remote syslogd server. + * + * @author Jesper Skovgaard Nielsen + * @author Dominik Kukacka + */ +class SyslogUdpHandler extends AbstractSyslogHandler +{ + const RFC3164 = 0; + const RFC5424 = 1; + const RFC5424e = 2; + + /** @var array */ + private $dateFormats = array( + self::RFC3164 => 'M d H:i:s', + self::RFC5424 => \DateTime::RFC3339, + self::RFC5424e => \DateTime::RFC3339_EXTENDED, + ); + + /** @var UdpSocket */ + protected $socket; + /** @var string */ + protected $ident; + /** @var self::RFC* */ + protected $rfc; + + /** + * @param string $host Either IP/hostname or a path to a unix socket (port must be 0 then) + * @param int $port Port number, or 0 if $host is a unix socket + * @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param string $ident Program name or tag for each log message. + * @param int $rfc RFC to format the message for. + * @throws MissingExtensionException + * + * @phpstan-param self::RFC* $rfc + */ + public function __construct(string $host, int $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true, string $ident = 'php', int $rfc = self::RFC5424) + { + if (!extension_loaded('sockets')) { + throw new MissingExtensionException('The sockets extension is required to use the SyslogUdpHandler'); + } + + parent::__construct($facility, $level, $bubble); + + $this->ident = $ident; + $this->rfc = $rfc; + + $this->socket = new UdpSocket($host, $port); + } + + protected function write(array $record): void + { + $lines = $this->splitMessageIntoLines($record['formatted']); + + $header = $this->makeCommonSyslogHeader($this->logLevels[$record['level']], $record['datetime']); + + foreach ($lines as $line) { + $this->socket->write($line, $header); + } + } + + public function close(): void + { + $this->socket->close(); + } + + /** + * @param string|string[] $message + * @return string[] + */ + private function splitMessageIntoLines($message): array + { + if (is_array($message)) { + $message = implode("\n", $message); + } + + $lines = preg_split('/$\R?^/m', (string) $message, -1, PREG_SPLIT_NO_EMPTY); + if (false === $lines) { + $pcreErrorCode = preg_last_error(); + throw new \RuntimeException('Could not preg_split: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode)); + } + + return $lines; + } + + /** + * Make common syslog header (see rfc5424 or rfc3164) + */ + protected function makeCommonSyslogHeader(int $severity, DateTimeInterface $datetime): string + { + $priority = $severity + $this->facility; + + if (!$pid = getmypid()) { + $pid = '-'; + } + + if (!$hostname = gethostname()) { + $hostname = '-'; + } + + if ($this->rfc === self::RFC3164) { + // see https://github.com/phpstan/phpstan/issues/5348 + // @phpstan-ignore-next-line + $dateNew = $datetime->setTimezone(new \DateTimeZone('UTC')); + $date = $dateNew->format($this->dateFormats[$this->rfc]); + + return "<$priority>" . + $date . " " . + $hostname . " " . + $this->ident . "[" . $pid . "]: "; + } + + $date = $datetime->format($this->dateFormats[$this->rfc]); + + return "<$priority>1 " . + $date . " " . + $hostname . " " . + $this->ident . " " . + $pid . " - - "; + } + + /** + * Inject your own socket, mainly used for testing + */ + public function setSocket(UdpSocket $socket): self + { + $this->socket = $socket; + + return $this; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php new file mode 100644 index 0000000..37d4a00 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php @@ -0,0 +1,274 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use RuntimeException; +use Monolog\Logger; +use Monolog\Utils; + +/** + * Handler send logs to Telegram using Telegram Bot API. + * + * How to use: + * 1) Create telegram bot with https://telegram.me/BotFather + * 2) Create a telegram channel where logs will be recorded. + * 3) Add created bot from step 1 to the created channel from step 2. + * + * Use telegram bot API key from step 1 and channel name with '@' prefix from step 2 to create instance of TelegramBotHandler + * + * @link https://core.telegram.org/bots/api + * + * @author Mazur Alexandr + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class TelegramBotHandler extends AbstractProcessingHandler +{ + private const BOT_API = 'https://api.telegram.org/bot'; + + /** + * The available values of parseMode according to the Telegram api documentation + */ + private const AVAILABLE_PARSE_MODES = [ + 'HTML', + 'MarkdownV2', + 'Markdown', // legacy mode without underline and strikethrough, use MarkdownV2 instead + ]; + + /** + * The maximum number of characters allowed in a message according to the Telegram api documentation + */ + private const MAX_MESSAGE_LENGTH = 4096; + + /** + * Telegram bot access token provided by BotFather. + * Create telegram bot with https://telegram.me/BotFather and use access token from it. + * @var string + */ + private $apiKey; + + /** + * Telegram channel name. + * Since to start with '@' symbol as prefix. + * @var string + */ + private $channel; + + /** + * The kind of formatting that is used for the message. + * See available options at https://core.telegram.org/bots/api#formatting-options + * or in AVAILABLE_PARSE_MODES + * @var ?string + */ + private $parseMode; + + /** + * Disables link previews for links in the message. + * @var ?bool + */ + private $disableWebPagePreview; + + /** + * Sends the message silently. Users will receive a notification with no sound. + * @var ?bool + */ + private $disableNotification; + + /** + * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages. + * False - truncates a message that is too long. + * @var bool + */ + private $splitLongMessages; + + /** + * Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests). + * @var bool + */ + private $delayBetweenMessages; + + /** + * @param string $apiKey Telegram bot access token provided by BotFather + * @param string $channel Telegram channel name + * @param bool $splitLongMessages Split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages + * @param bool $delayBetweenMessages Adds delay between sending a split message according to Telegram API + * @throws MissingExtensionException + */ + public function __construct( + string $apiKey, + string $channel, + $level = Logger::DEBUG, + bool $bubble = true, + string $parseMode = null, + bool $disableWebPagePreview = null, + bool $disableNotification = null, + bool $splitLongMessages = false, + bool $delayBetweenMessages = false + ) + { + if (!extension_loaded('curl')) { + throw new MissingExtensionException('The curl extension is needed to use the TelegramBotHandler'); + } + + parent::__construct($level, $bubble); + + $this->apiKey = $apiKey; + $this->channel = $channel; + $this->setParseMode($parseMode); + $this->disableWebPagePreview($disableWebPagePreview); + $this->disableNotification($disableNotification); + $this->splitLongMessages($splitLongMessages); + $this->delayBetweenMessages($delayBetweenMessages); + } + + public function setParseMode(string $parseMode = null): self + { + if ($parseMode !== null && !in_array($parseMode, self::AVAILABLE_PARSE_MODES)) { + throw new \InvalidArgumentException('Unknown parseMode, use one of these: ' . implode(', ', self::AVAILABLE_PARSE_MODES) . '.'); + } + + $this->parseMode = $parseMode; + + return $this; + } + + public function disableWebPagePreview(bool $disableWebPagePreview = null): self + { + $this->disableWebPagePreview = $disableWebPagePreview; + + return $this; + } + + public function disableNotification(bool $disableNotification = null): self + { + $this->disableNotification = $disableNotification; + + return $this; + } + + /** + * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages. + * False - truncates a message that is too long. + * @param bool $splitLongMessages + * @return $this + */ + public function splitLongMessages(bool $splitLongMessages = false): self + { + $this->splitLongMessages = $splitLongMessages; + + return $this; + } + + /** + * Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests). + * @param bool $delayBetweenMessages + * @return $this + */ + public function delayBetweenMessages(bool $delayBetweenMessages = false): self + { + $this->delayBetweenMessages = $delayBetweenMessages; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + /** @var Record[] $messages */ + $messages = []; + + foreach ($records as $record) { + if (!$this->isHandling($record)) { + continue; + } + + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + $messages[] = $record; + } + + if (!empty($messages)) { + $this->send((string)$this->getFormatter()->formatBatch($messages)); + } + } + + /** + * @inheritDoc + */ + protected function write(array $record): void + { + $this->send($record['formatted']); + } + + /** + * Send request to @link https://api.telegram.org/bot on SendMessage action. + * @param string $message + */ + protected function send(string $message): void + { + $messages = $this->handleMessageLength($message); + + foreach ($messages as $key => $msg) { + if ($this->delayBetweenMessages && $key > 0) { + sleep(1); + } + + $this->sendCurl($msg); + } + } + + protected function sendCurl(string $message): void + { + $ch = curl_init(); + $url = self::BOT_API . $this->apiKey . '/SendMessage'; + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ + 'text' => $message, + 'chat_id' => $this->channel, + 'parse_mode' => $this->parseMode, + 'disable_web_page_preview' => $this->disableWebPagePreview, + 'disable_notification' => $this->disableNotification, + ])); + + $result = Curl\Util::execute($ch); + if (!is_string($result)) { + throw new RuntimeException('Telegram API error. Description: No response'); + } + $result = json_decode($result, true); + + if ($result['ok'] === false) { + throw new RuntimeException('Telegram API error. Description: ' . $result['description']); + } + } + + /** + * Handle a message that is too long: truncates or splits into several + * @param string $message + * @return string[] + */ + private function handleMessageLength(string $message): array + { + $truncatedMarker = ' (...truncated)'; + if (!$this->splitLongMessages && strlen($message) > self::MAX_MESSAGE_LENGTH) { + return [Utils::substr($message, 0, self::MAX_MESSAGE_LENGTH - strlen($truncatedMarker)) . $truncatedMarker]; + } + + return str_split($message, self::MAX_MESSAGE_LENGTH); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php new file mode 100644 index 0000000..809b5c8 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php @@ -0,0 +1,231 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Used for testing purposes. + * + * It records all records and gives you access to them for verification. + * + * @author Jordi Boggiano + * + * @method bool hasEmergency($record) + * @method bool hasAlert($record) + * @method bool hasCritical($record) + * @method bool hasError($record) + * @method bool hasWarning($record) + * @method bool hasNotice($record) + * @method bool hasInfo($record) + * @method bool hasDebug($record) + * + * @method bool hasEmergencyRecords() + * @method bool hasAlertRecords() + * @method bool hasCriticalRecords() + * @method bool hasErrorRecords() + * @method bool hasWarningRecords() + * @method bool hasNoticeRecords() + * @method bool hasInfoRecords() + * @method bool hasDebugRecords() + * + * @method bool hasEmergencyThatContains($message) + * @method bool hasAlertThatContains($message) + * @method bool hasCriticalThatContains($message) + * @method bool hasErrorThatContains($message) + * @method bool hasWarningThatContains($message) + * @method bool hasNoticeThatContains($message) + * @method bool hasInfoThatContains($message) + * @method bool hasDebugThatContains($message) + * + * @method bool hasEmergencyThatMatches($message) + * @method bool hasAlertThatMatches($message) + * @method bool hasCriticalThatMatches($message) + * @method bool hasErrorThatMatches($message) + * @method bool hasWarningThatMatches($message) + * @method bool hasNoticeThatMatches($message) + * @method bool hasInfoThatMatches($message) + * @method bool hasDebugThatMatches($message) + * + * @method bool hasEmergencyThatPasses($message) + * @method bool hasAlertThatPasses($message) + * @method bool hasCriticalThatPasses($message) + * @method bool hasErrorThatPasses($message) + * @method bool hasWarningThatPasses($message) + * @method bool hasNoticeThatPasses($message) + * @method bool hasInfoThatPasses($message) + * @method bool hasDebugThatPasses($message) + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class TestHandler extends AbstractProcessingHandler +{ + /** @var Record[] */ + protected $records = []; + /** @var array */ + protected $recordsByLevel = []; + /** @var bool */ + private $skipReset = false; + + /** + * @return array + * + * @phpstan-return Record[] + */ + public function getRecords() + { + return $this->records; + } + + /** + * @return void + */ + public function clear() + { + $this->records = []; + $this->recordsByLevel = []; + } + + /** + * @return void + */ + public function reset() + { + if (!$this->skipReset) { + $this->clear(); + } + } + + /** + * @return void + */ + public function setSkipReset(bool $skipReset) + { + $this->skipReset = $skipReset; + } + + /** + * @param string|int $level Logging level value or name + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecords($level): bool + { + return isset($this->recordsByLevel[Logger::toMonologLevel($level)]); + } + + /** + * @param string|array $record Either a message string or an array containing message and optionally context keys that will be checked against all records + * @param string|int $level Logging level value or name + * + * @phpstan-param array{message: string, context?: mixed[]}|string $record + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecord($record, $level): bool + { + if (is_string($record)) { + $record = array('message' => $record); + } + + return $this->hasRecordThatPasses(function ($rec) use ($record) { + if ($rec['message'] !== $record['message']) { + return false; + } + if (isset($record['context']) && $rec['context'] !== $record['context']) { + return false; + } + + return true; + }, $level); + } + + /** + * @param string|int $level Logging level value or name + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecordThatContains(string $message, $level): bool + { + return $this->hasRecordThatPasses(function ($rec) use ($message) { + return strpos($rec['message'], $message) !== false; + }, $level); + } + + /** + * @param string|int $level Logging level value or name + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecordThatMatches(string $regex, $level): bool + { + return $this->hasRecordThatPasses(function (array $rec) use ($regex): bool { + return preg_match($regex, $rec['message']) > 0; + }, $level); + } + + /** + * @param string|int $level Logging level value or name + * @return bool + * + * @psalm-param callable(Record, int): mixed $predicate + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function hasRecordThatPasses(callable $predicate, $level) + { + $level = Logger::toMonologLevel($level); + + if (!isset($this->recordsByLevel[$level])) { + return false; + } + + foreach ($this->recordsByLevel[$level] as $i => $rec) { + if ($predicate($rec, $i)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->recordsByLevel[$record['level']][] = $record; + $this->records[] = $record; + } + + /** + * @param string $method + * @param mixed[] $args + * @return bool + */ + public function __call($method, $args) + { + if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { + $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; + $level = constant('Monolog\Logger::' . strtoupper($matches[2])); + $callback = [$this, $genericMethod]; + if (is_callable($callback)) { + $args[] = $level; + + return call_user_func_array($callback, $args); + } + } + + throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()'); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php new file mode 100644 index 0000000..42cb657 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +trait WebRequestRecognizerTrait +{ + /** + * Checks if PHP's serving a web request + * @return bool + */ + protected function isWebRequest(): bool + { + return 'cli' !== \PHP_SAPI && 'phpdbg' !== \PHP_SAPI; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php new file mode 100644 index 0000000..a0a8faa --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Forwards records to multiple handlers suppressing failures of each handler + * and continuing through to give every handler a chance to succeed. + * + * @author Craig D'Amelio + * + * @phpstan-import-type Record from \Monolog\Logger + */ +class WhatFailureGroupHandler extends GroupHandler +{ + /** + * {@inheritDoc} + */ + public function handle(array $record): bool + { + if ($this->processors) { + /** @var Record $record */ + $record = $this->processRecord($record); + } + + foreach ($this->handlers as $handler) { + try { + $handler->handle($record); + } catch (\Throwable $e) { + // What failure? + } + } + + return false === $this->bubble; + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records): void + { + if ($this->processors) { + $processed = array(); + foreach ($records as $record) { + $processed[] = $this->processRecord($record); + } + /** @var Record[] $records */ + $records = $processed; + } + + foreach ($this->handlers as $handler) { + try { + $handler->handleBatch($records); + } catch (\Throwable $e) { + // What failure? + } + } + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php b/msd/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php new file mode 100644 index 0000000..d046fc5 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Logger; + +/** + * Handler sending logs to Zend Monitor + * + * @author Christian Bergau + * @author Jason Davis + * + * @phpstan-import-type FormattedRecord from AbstractProcessingHandler + */ +class ZendMonitorHandler extends AbstractProcessingHandler +{ + /** + * Monolog level / ZendMonitor Custom Event priority map + * + * @var array + */ + protected $levelMap = []; + + /** + * @throws MissingExtensionException + */ + public function __construct($level = Logger::DEBUG, bool $bubble = true) + { + if (!function_exists('zend_monitor_custom_event')) { + throw new MissingExtensionException( + 'You must have Zend Server installed with Zend Monitor enabled in order to use this handler' + ); + } + //zend monitor constants are not defined if zend monitor is not enabled. + $this->levelMap = [ + Logger::DEBUG => \ZEND_MONITOR_EVENT_SEVERITY_INFO, + Logger::INFO => \ZEND_MONITOR_EVENT_SEVERITY_INFO, + Logger::NOTICE => \ZEND_MONITOR_EVENT_SEVERITY_INFO, + Logger::WARNING => \ZEND_MONITOR_EVENT_SEVERITY_WARNING, + Logger::ERROR => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, + Logger::CRITICAL => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, + Logger::ALERT => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, + Logger::EMERGENCY => \ZEND_MONITOR_EVENT_SEVERITY_ERROR, + ]; + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record): void + { + $this->writeZendMonitorCustomEvent( + Logger::getLevelName($record['level']), + $record['message'], + $record['formatted'], + $this->levelMap[$record['level']] + ); + } + + /** + * Write to Zend Monitor Events + * @param string $type Text displayed in "Class Name (custom)" field + * @param string $message Text displayed in "Error String" + * @param array $formatted Displayed in Custom Variables tab + * @param int $severity Set the event severity level (-1,0,1) + * + * @phpstan-param FormattedRecord $formatted + */ + protected function writeZendMonitorCustomEvent(string $type, string $message, array $formatted, int $severity): void + { + zend_monitor_custom_event($type, $message, $formatted, $severity); + } + + /** + * {@inheritDoc} + */ + public function getDefaultFormatter(): FormatterInterface + { + return new NormalizerFormatter(); + } + + /** + * @return array + */ + public function getLevelMap(): array + { + return $this->levelMap; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/LogRecord.php b/msd/vendor/monolog/monolog/src/Monolog/LogRecord.php new file mode 100644 index 0000000..78c6d08 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/LogRecord.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use ArrayAccess; + +/** + * Monolog log record interface for forward compatibility with Monolog 3.0 + * + * This is just present in Monolog 2.4+ to allow interoperable code to be written against + * both versions by type-hinting arguments as `array|\Monolog\LogRecord $record` + * + * Do not rely on this interface for other purposes, and do not implement it. + * + * @author Jordi Boggiano + * @template-extends \ArrayAccess<'message'|'level'|'context'|'level_name'|'channel'|'datetime'|'extra'|'formatted', mixed> + * @phpstan-import-type Record from Logger + */ +interface LogRecord extends \ArrayAccess +{ + /** + * @phpstan-return Record + */ + public function toArray(): array; +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Logger.php b/msd/vendor/monolog/monolog/src/Monolog/Logger.php new file mode 100644 index 0000000..f1850ae --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Logger.php @@ -0,0 +1,701 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use DateTimeZone; +use Monolog\Handler\HandlerInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\InvalidArgumentException; +use Psr\Log\LogLevel; +use Throwable; +use Stringable; + +/** + * Monolog log channel + * + * It contains a stack of Handlers and a stack of Processors, + * and uses them to store records that are added to it. + * + * @author Jordi Boggiano + * + * @phpstan-type Level Logger::DEBUG|Logger::INFO|Logger::NOTICE|Logger::WARNING|Logger::ERROR|Logger::CRITICAL|Logger::ALERT|Logger::EMERGENCY + * @phpstan-type LevelName 'DEBUG'|'INFO'|'NOTICE'|'WARNING'|'ERROR'|'CRITICAL'|'ALERT'|'EMERGENCY' + * @phpstan-type Record array{message: string, context: mixed[], level: Level, level_name: LevelName, channel: string, datetime: \DateTimeImmutable, extra: mixed[]} + */ +class Logger implements LoggerInterface, ResettableInterface +{ + /** + * Detailed debug information + */ + public const DEBUG = 100; + + /** + * Interesting events + * + * Examples: User logs in, SQL logs. + */ + public const INFO = 200; + + /** + * Uncommon events + */ + public const NOTICE = 250; + + /** + * Exceptional occurrences that are not errors + * + * Examples: Use of deprecated APIs, poor use of an API, + * undesirable things that are not necessarily wrong. + */ + public const WARNING = 300; + + /** + * Runtime errors + */ + public const ERROR = 400; + + /** + * Critical conditions + * + * Example: Application component unavailable, unexpected exception. + */ + public const CRITICAL = 500; + + /** + * Action must be taken immediately + * + * Example: Entire website down, database unavailable, etc. + * This should trigger the SMS alerts and wake you up. + */ + public const ALERT = 550; + + /** + * Urgent alert. + */ + public const EMERGENCY = 600; + + /** + * Monolog API version + * + * This is only bumped when API breaks are done and should + * follow the major version of the library + * + * @var int + */ + public const API = 2; + + /** + * This is a static variable and not a constant to serve as an extension point for custom levels + * + * @var array $levels Logging levels with the levels as key + * + * @phpstan-var array $levels Logging levels with the levels as key + */ + protected static $levels = [ + self::DEBUG => 'DEBUG', + self::INFO => 'INFO', + self::NOTICE => 'NOTICE', + self::WARNING => 'WARNING', + self::ERROR => 'ERROR', + self::CRITICAL => 'CRITICAL', + self::ALERT => 'ALERT', + self::EMERGENCY => 'EMERGENCY', + ]; + + /** + * Mapping between levels numbers defined in RFC 5424 and Monolog ones + * + * @phpstan-var array $rfc_5424_levels + */ + private const RFC_5424_LEVELS = [ + 7 => self::DEBUG, + 6 => self::INFO, + 5 => self::NOTICE, + 4 => self::WARNING, + 3 => self::ERROR, + 2 => self::CRITICAL, + 1 => self::ALERT, + 0 => self::EMERGENCY, + ]; + + /** + * @var string + */ + protected $name; + + /** + * The handler stack + * + * @var HandlerInterface[] + */ + protected $handlers; + + /** + * Processors that will process all log records + * + * To process records of a single handler instead, add the processor on that specific handler + * + * @var callable[] + */ + protected $processors; + + /** + * @var bool + */ + protected $microsecondTimestamps = true; + + /** + * @var DateTimeZone + */ + protected $timezone; + + /** + * @var callable|null + */ + protected $exceptionHandler; + + /** + * @var int Keeps track of depth to prevent infinite logging loops + */ + private $logDepth = 0; + + /** + * @var bool Whether to detect infinite logging loops + * + * This can be disabled via {@see useLoggingLoopDetection} if you have async handlers that do not play well with this + */ + private $detectCycles = true; + + /** + * @psalm-param array $processors + * + * @param string $name The logging channel, a simple descriptive name that is attached to all log records + * @param HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc. + * @param callable[] $processors Optional array of processors + * @param DateTimeZone|null $timezone Optional timezone, if not provided date_default_timezone_get() will be used + */ + public function __construct(string $name, array $handlers = [], array $processors = [], ?DateTimeZone $timezone = null) + { + $this->name = $name; + $this->setHandlers($handlers); + $this->processors = $processors; + $this->timezone = $timezone ?: new DateTimeZone(date_default_timezone_get() ?: 'UTC'); + } + + public function getName(): string + { + return $this->name; + } + + /** + * Return a new cloned instance with the name changed + */ + public function withName(string $name): self + { + $new = clone $this; + $new->name = $name; + + return $new; + } + + /** + * Pushes a handler on to the stack. + */ + public function pushHandler(HandlerInterface $handler): self + { + array_unshift($this->handlers, $handler); + + return $this; + } + + /** + * Pops a handler from the stack + * + * @throws \LogicException If empty handler stack + */ + public function popHandler(): HandlerInterface + { + if (!$this->handlers) { + throw new \LogicException('You tried to pop from an empty handler stack.'); + } + + return array_shift($this->handlers); + } + + /** + * Set handlers, replacing all existing ones. + * + * If a map is passed, keys will be ignored. + * + * @param HandlerInterface[] $handlers + */ + public function setHandlers(array $handlers): self + { + $this->handlers = []; + foreach (array_reverse($handlers) as $handler) { + $this->pushHandler($handler); + } + + return $this; + } + + /** + * @return HandlerInterface[] + */ + public function getHandlers(): array + { + return $this->handlers; + } + + /** + * Adds a processor on to the stack. + */ + public function pushProcessor(callable $callback): self + { + array_unshift($this->processors, $callback); + + return $this; + } + + /** + * Removes the processor on top of the stack and returns it. + * + * @throws \LogicException If empty processor stack + * @return callable + */ + public function popProcessor(): callable + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + + return array_shift($this->processors); + } + + /** + * @return callable[] + */ + public function getProcessors(): array + { + return $this->processors; + } + + /** + * Control the use of microsecond resolution timestamps in the 'datetime' + * member of new records. + * + * As of PHP7.1 microseconds are always included by the engine, so + * there is no performance penalty and Monolog 2 enabled microseconds + * by default. This function lets you disable them though in case you want + * to suppress microseconds from the output. + * + * @param bool $micro True to use microtime() to create timestamps + */ + public function useMicrosecondTimestamps(bool $micro): self + { + $this->microsecondTimestamps = $micro; + + return $this; + } + + public function useLoggingLoopDetection(bool $detectCycles): self + { + $this->detectCycles = $detectCycles; + + return $this; + } + + /** + * Adds a log record. + * + * @param int $level The logging level (a Monolog or RFC 5424 level) + * @param string $message The log message + * @param mixed[] $context The log context + * @param DateTimeImmutable $datetime Optional log date to log into the past or future + * @return bool Whether the record has been processed + * + * @phpstan-param Level $level + */ + public function addRecord(int $level, string $message, array $context = [], DateTimeImmutable $datetime = null): bool + { + if (isset(self::RFC_5424_LEVELS[$level])) { + $level = self::RFC_5424_LEVELS[$level]; + } + + if ($this->detectCycles) { + $this->logDepth += 1; + } + if ($this->logDepth === 3) { + $this->warning('A possible infinite logging loop was detected and aborted. It appears some of your handler code is triggering logging, see the previous log record for a hint as to what may be the cause.'); + return false; + } elseif ($this->logDepth >= 5) { // log depth 4 is let through so we can log the warning above + return false; + } + + try { + $record = null; + + foreach ($this->handlers as $handler) { + if (null === $record) { + // skip creating the record as long as no handler is going to handle it + if (!$handler->isHandling(['level' => $level])) { + continue; + } + + $levelName = static::getLevelName($level); + + $record = [ + 'message' => $message, + 'context' => $context, + 'level' => $level, + 'level_name' => $levelName, + 'channel' => $this->name, + 'datetime' => $datetime ?? new DateTimeImmutable($this->microsecondTimestamps, $this->timezone), + 'extra' => [], + ]; + + try { + foreach ($this->processors as $processor) { + $record = $processor($record); + } + } catch (Throwable $e) { + $this->handleException($e, $record); + + return true; + } + } + + // once the record exists, send it to all handlers as long as the bubbling chain is not interrupted + try { + if (true === $handler->handle($record)) { + break; + } + } catch (Throwable $e) { + $this->handleException($e, $record); + + return true; + } + } + } finally { + if ($this->detectCycles) { + $this->logDepth--; + } + } + + return null !== $record; + } + + /** + * Ends a log cycle and frees all resources used by handlers. + * + * Closing a Handler means flushing all buffers and freeing any open resources/handles. + * Handlers that have been closed should be able to accept log records again and re-open + * themselves on demand, but this may not always be possible depending on implementation. + * + * This is useful at the end of a request and will be called automatically on every handler + * when they get destructed. + */ + public function close(): void + { + foreach ($this->handlers as $handler) { + $handler->close(); + } + } + + /** + * Ends a log cycle and resets all handlers and processors to their initial state. + * + * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal + * state, and getting it back to a state in which it can receive log records again. + * + * This is useful in case you want to avoid logs leaking between two requests or jobs when you + * have a long running process like a worker or an application server serving multiple requests + * in one process. + */ + public function reset(): void + { + foreach ($this->handlers as $handler) { + if ($handler instanceof ResettableInterface) { + $handler->reset(); + } + } + + foreach ($this->processors as $processor) { + if ($processor instanceof ResettableInterface) { + $processor->reset(); + } + } + } + + /** + * Gets all supported logging levels. + * + * @return array Assoc array with human-readable level names => level codes. + * @phpstan-return array + */ + public static function getLevels(): array + { + return array_flip(static::$levels); + } + + /** + * Gets the name of the logging level. + * + * @throws \Psr\Log\InvalidArgumentException If level is not defined + * + * @phpstan-param Level $level + * @phpstan-return LevelName + */ + public static function getLevelName(int $level): string + { + if (!isset(static::$levels[$level])) { + throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels))); + } + + return static::$levels[$level]; + } + + /** + * Converts PSR-3 levels to Monolog ones if necessary + * + * @param string|int $level Level number (monolog) or name (PSR-3) + * @throws \Psr\Log\InvalidArgumentException If level is not defined + * + * @phpstan-param Level|LevelName|LogLevel::* $level + * @phpstan-return Level + */ + public static function toMonologLevel($level): int + { + if (is_string($level)) { + if (is_numeric($level)) { + /** @phpstan-ignore-next-line */ + return intval($level); + } + + // Contains chars of all log levels and avoids using strtoupper() which may have + // strange results depending on locale (for example, "i" will become "İ" in Turkish locale) + $upper = strtr($level, 'abcdefgilmnortuwy', 'ABCDEFGILMNORTUWY'); + if (defined(__CLASS__.'::'.$upper)) { + return constant(__CLASS__ . '::' . $upper); + } + + throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels) + static::$levels)); + } + + if (!is_int($level)) { + throw new InvalidArgumentException('Level "'.var_export($level, true).'" is not defined, use one of: '.implode(', ', array_keys(static::$levels) + static::$levels)); + } + + return $level; + } + + /** + * Checks whether the Logger has a handler that listens on the given level + * + * @phpstan-param Level $level + */ + public function isHandling(int $level): bool + { + $record = [ + 'level' => $level, + ]; + + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + + return false; + } + + /** + * Set a custom exception handler that will be called if adding a new record fails + * + * The callable will receive an exception object and the record that failed to be logged + */ + public function setExceptionHandler(?callable $callback): self + { + $this->exceptionHandler = $callback; + + return $this; + } + + public function getExceptionHandler(): ?callable + { + return $this->exceptionHandler; + } + + /** + * Adds a log record at an arbitrary level. + * + * This method allows for compatibility with common interfaces. + * + * @param mixed $level The log level (a Monolog, PSR-3 or RFC 5424 level) + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function log($level, $message, array $context = []): void + { + if (!is_int($level) && !is_string($level)) { + throw new \InvalidArgumentException('$level is expected to be a string or int'); + } + + if (isset(self::RFC_5424_LEVELS[$level])) { + $level = self::RFC_5424_LEVELS[$level]; + } + + $level = static::toMonologLevel($level); + + $this->addRecord($level, (string) $message, $context); + } + + /** + * Adds a log record at the DEBUG level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function debug($message, array $context = []): void + { + $this->addRecord(static::DEBUG, (string) $message, $context); + } + + /** + * Adds a log record at the INFO level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function info($message, array $context = []): void + { + $this->addRecord(static::INFO, (string) $message, $context); + } + + /** + * Adds a log record at the NOTICE level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function notice($message, array $context = []): void + { + $this->addRecord(static::NOTICE, (string) $message, $context); + } + + /** + * Adds a log record at the WARNING level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function warning($message, array $context = []): void + { + $this->addRecord(static::WARNING, (string) $message, $context); + } + + /** + * Adds a log record at the ERROR level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function error($message, array $context = []): void + { + $this->addRecord(static::ERROR, (string) $message, $context); + } + + /** + * Adds a log record at the CRITICAL level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function critical($message, array $context = []): void + { + $this->addRecord(static::CRITICAL, (string) $message, $context); + } + + /** + * Adds a log record at the ALERT level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function alert($message, array $context = []): void + { + $this->addRecord(static::ALERT, (string) $message, $context); + } + + /** + * Adds a log record at the EMERGENCY level. + * + * This method allows for compatibility with common interfaces. + * + * @param string|Stringable $message The log message + * @param mixed[] $context The log context + */ + public function emergency($message, array $context = []): void + { + $this->addRecord(static::EMERGENCY, (string) $message, $context); + } + + /** + * Sets the timezone to be used for the timestamp of log records. + */ + public function setTimezone(DateTimeZone $tz): self + { + $this->timezone = $tz; + + return $this; + } + + /** + * Returns the timezone to be used for the timestamp of log records. + */ + public function getTimezone(): DateTimeZone + { + return $this->timezone; + } + + /** + * Delegates exception management to the custom exception handler, + * or throws the exception if no custom handler is set. + * + * @param array $record + * @phpstan-param Record $record + */ + protected function handleException(Throwable $e, array $record): void + { + if (!$this->exceptionHandler) { + throw $e; + } + + ($this->exceptionHandler)($e, $record); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php b/msd/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php new file mode 100644 index 0000000..9bd4cdd --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Injects Git branch and Git commit SHA in all records + * + * @author Nick Otter + * @author Jordi Boggiano + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class GitProcessor implements ProcessorInterface +{ + /** @var int */ + private $level; + /** @var array{branch: string, commit: string}|array|null */ + private static $cache = null; + + /** + * @param string|int $level The minimum logging level at which this Processor will be triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = Logger::DEBUG) + { + $this->level = Logger::toMonologLevel($level); + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + $record['extra']['git'] = self::getGitInfo(); + + return $record; + } + + /** + * @return array{branch: string, commit: string}|array + */ + private static function getGitInfo(): array + { + if (self::$cache) { + return self::$cache; + } + + $branches = `git branch -v --no-abbrev`; + if ($branches && preg_match('{^\* (.+?)\s+([a-f0-9]{40})(?:\s|$)}m', $branches, $matches)) { + return self::$cache = [ + 'branch' => $matches[1], + 'commit' => $matches[2], + ]; + } + + return self::$cache = []; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php b/msd/vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php new file mode 100644 index 0000000..158d26b --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects value of gethostname in all records + */ +class HostnameProcessor implements ProcessorInterface +{ + /** @var string */ + private static $host; + + public function __construct() + { + self::$host = (string) gethostname(); + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $record['extra']['hostname'] = self::$host; + + return $record; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php b/msd/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php new file mode 100644 index 0000000..2389b31 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Injects line/file:class/function where the log message came from + * + * Warning: This only works if the handler processes the logs directly. + * If you put the processor on a handler that is behind a FingersCrossedHandler + * for example, the processor will only be called once the trigger level is reached, + * and all the log records will have the same file/line/.. data from the call that + * triggered the FingersCrossedHandler. + * + * @author Jordi Boggiano + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class IntrospectionProcessor implements ProcessorInterface +{ + /** @var int */ + private $level; + /** @var string[] */ + private $skipClassesPartials; + /** @var int */ + private $skipStackFramesCount; + /** @var string[] */ + private $skipFunctions = [ + 'call_user_func', + 'call_user_func_array', + ]; + + /** + * @param string|int $level The minimum logging level at which this Processor will be triggered + * @param string[] $skipClassesPartials + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = Logger::DEBUG, array $skipClassesPartials = [], int $skipStackFramesCount = 0) + { + $this->level = Logger::toMonologLevel($level); + $this->skipClassesPartials = array_merge(['Monolog\\'], $skipClassesPartials); + $this->skipStackFramesCount = $skipStackFramesCount; + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + + // skip first since it's always the current method + array_shift($trace); + // the call_user_func call is also skipped + array_shift($trace); + + $i = 0; + + while ($this->isTraceClassOrSkippedFunction($trace, $i)) { + if (isset($trace[$i]['class'])) { + foreach ($this->skipClassesPartials as $part) { + if (strpos($trace[$i]['class'], $part) !== false) { + $i++; + + continue 2; + } + } + } elseif (in_array($trace[$i]['function'], $this->skipFunctions)) { + $i++; + + continue; + } + + break; + } + + $i += $this->skipStackFramesCount; + + // we should have the call source now + $record['extra'] = array_merge( + $record['extra'], + [ + 'file' => isset($trace[$i - 1]['file']) ? $trace[$i - 1]['file'] : null, + 'line' => isset($trace[$i - 1]['line']) ? $trace[$i - 1]['line'] : null, + 'class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : null, + 'callType' => isset($trace[$i]['type']) ? $trace[$i]['type'] : null, + 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null, + ] + ); + + return $record; + } + + /** + * @param array[] $trace + */ + private function isTraceClassOrSkippedFunction(array $trace, int $index): bool + { + if (!isset($trace[$index])) { + return false; + } + + return isset($trace[$index]['class']) || in_array($trace[$index]['function'], $this->skipFunctions); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php b/msd/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php new file mode 100644 index 0000000..1776f6d --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects memory_get_peak_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryPeakUsageProcessor extends MemoryProcessor +{ + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $usage = memory_get_peak_usage($this->realUsage); + + if ($this->useFormatting) { + $usage = $this->formatBytes($usage); + } + + $record['extra']['memory_peak_usage'] = $usage; + + return $record; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php b/msd/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php new file mode 100644 index 0000000..e613980 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Some methods that are common for all memory processors + * + * @author Rob Jensen + */ +abstract class MemoryProcessor implements ProcessorInterface +{ + /** + * @var bool If true, get the real size of memory allocated from system. Else, only the memory used by emalloc() is reported. + */ + protected $realUsage; + + /** + * @var bool If true, then format memory size to human readable string (MB, KB, B depending on size) + */ + protected $useFormatting; + + /** + * @param bool $realUsage Set this to true to get the real size of memory allocated from system. + * @param bool $useFormatting If true, then format memory size to human readable string (MB, KB, B depending on size) + */ + public function __construct(bool $realUsage = true, bool $useFormatting = true) + { + $this->realUsage = $realUsage; + $this->useFormatting = $useFormatting; + } + + /** + * Formats bytes into a human readable string if $this->useFormatting is true, otherwise return $bytes as is + * + * @param int $bytes + * @return string|int Formatted string if $this->useFormatting is true, otherwise return $bytes as int + */ + protected function formatBytes(int $bytes) + { + if (!$this->useFormatting) { + return $bytes; + } + + if ($bytes > 1024 * 1024) { + return round($bytes / 1024 / 1024, 2).' MB'; + } elseif ($bytes > 1024) { + return round($bytes / 1024, 2).' KB'; + } + + return $bytes . ' B'; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php b/msd/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php new file mode 100644 index 0000000..0c2654f --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects memory_get_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryUsageProcessor extends MemoryProcessor +{ + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $usage = memory_get_usage($this->realUsage); + + if ($this->useFormatting) { + $usage = $this->formatBytes($usage); + } + + $record['extra']['memory_usage'] = $usage; + + return $record; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php b/msd/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php new file mode 100644 index 0000000..c7ae377 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Logger; +use Psr\Log\LogLevel; + +/** + * Injects Hg branch and Hg revision number in all records + * + * @author Jonathan A. Schweder + * + * @phpstan-import-type LevelName from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + */ +class MercurialProcessor implements ProcessorInterface +{ + /** @var Level */ + private $level; + /** @var array{branch: string, revision: string}|array|null */ + private static $cache = null; + + /** + * @param int|string $level The minimum logging level at which this Processor will be triggered + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function __construct($level = Logger::DEBUG) + { + $this->level = Logger::toMonologLevel($level); + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + $record['extra']['hg'] = self::getMercurialInfo(); + + return $record; + } + + /** + * @return array{branch: string, revision: string}|array + */ + private static function getMercurialInfo(): array + { + if (self::$cache) { + return self::$cache; + } + + $result = explode(' ', trim(`hg id -nb`)); + + if (count($result) >= 3) { + return self::$cache = [ + 'branch' => $result[1], + 'revision' => $result[2], + ]; + } + + return self::$cache = []; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php b/msd/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php new file mode 100644 index 0000000..23ebfd1 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Adds value of getmypid into records + * + * @author Andreas Hörnicke + */ +class ProcessIdProcessor implements ProcessorInterface +{ + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $record['extra']['process_id'] = getmypid(); + + return $record; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php b/msd/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php new file mode 100644 index 0000000..9777f85 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * An optional interface to allow labelling Monolog processors. + * + * @author Nicolas Grekas + * + * @phpstan-import-type Record from \Monolog\Logger + */ +interface ProcessorInterface +{ + /** + * @return array The processed record + * + * @phpstan-param Record $record + * @phpstan-return Record + */ + public function __invoke(array $record); +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php b/msd/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php new file mode 100644 index 0000000..d143cc3 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Utils; + +/** + * Processes a record's message according to PSR-3 rules + * + * It replaces {foo} with the value from $context['foo'] + * + * @author Jordi Boggiano + */ +class PsrLogMessageProcessor implements ProcessorInterface +{ + public const SIMPLE_DATE = "Y-m-d\TH:i:s.uP"; + + /** @var string|null */ + private $dateFormat; + + /** @var bool */ + private $removeUsedContextFields; + + /** + * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format + * @param bool $removeUsedContextFields If set to true the fields interpolated into message gets unset + */ + public function __construct(?string $dateFormat = null, bool $removeUsedContextFields = false) + { + $this->dateFormat = $dateFormat; + $this->removeUsedContextFields = $removeUsedContextFields; + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + if (false === strpos($record['message'], '{')) { + return $record; + } + + $replacements = []; + foreach ($record['context'] as $key => $val) { + $placeholder = '{' . $key . '}'; + if (strpos($record['message'], $placeholder) === false) { + continue; + } + + if (is_null($val) || is_scalar($val) || (is_object($val) && method_exists($val, "__toString"))) { + $replacements[$placeholder] = $val; + } elseif ($val instanceof \DateTimeInterface) { + if (!$this->dateFormat && $val instanceof \Monolog\DateTimeImmutable) { + // handle monolog dates using __toString if no specific dateFormat was asked for + // so that it follows the useMicroseconds flag + $replacements[$placeholder] = (string) $val; + } else { + $replacements[$placeholder] = $val->format($this->dateFormat ?: static::SIMPLE_DATE); + } + } elseif (is_object($val)) { + $replacements[$placeholder] = '[object '.Utils::getClass($val).']'; + } elseif (is_array($val)) { + $replacements[$placeholder] = 'array'.Utils::jsonEncode($val, null, true); + } else { + $replacements[$placeholder] = '['.gettype($val).']'; + } + + if ($this->removeUsedContextFields) { + unset($record['context'][$key]); + } + } + + $record['message'] = strtr($record['message'], $replacements); + + return $record; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php b/msd/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php new file mode 100644 index 0000000..52ed13a --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Adds a tags array into record + * + * @author Martijn Riemers + */ +class TagProcessor implements ProcessorInterface +{ + /** @var string[] */ + private $tags; + + /** + * @param string[] $tags + */ + public function __construct(array $tags = []) + { + $this->setTags($tags); + } + + /** + * @param string[] $tags + */ + public function addTags(array $tags = []): self + { + $this->tags = array_merge($this->tags, $tags); + + return $this; + } + + /** + * @param string[] $tags + */ + public function setTags(array $tags = []): self + { + $this->tags = $tags; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $record['extra']['tags'] = $this->tags; + + return $record; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php b/msd/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php new file mode 100644 index 0000000..87f424f --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\ResettableInterface; + +/** + * Adds a unique identifier into records + * + * @author Simon Mönch + */ +class UidProcessor implements ProcessorInterface, ResettableInterface +{ + /** @var string */ + private $uid; + + public function __construct(int $length = 7) + { + if ($length > 32 || $length < 1) { + throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32'); + } + + $this->uid = $this->generateUid($length); + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + $record['extra']['uid'] = $this->uid; + + return $record; + } + + public function getUid(): string + { + return $this->uid; + } + + public function reset() + { + $this->uid = $this->generateUid(strlen($this->uid)); + } + + private function generateUid(int $length): string + { + return substr(bin2hex(random_bytes((int) ceil($length / 2))), 0, $length); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php b/msd/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php new file mode 100644 index 0000000..6f0d476 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects url/method and remote IP of the current web request in all records + * + * @author Jordi Boggiano + */ +class WebProcessor implements ProcessorInterface +{ + /** + * @var array|\ArrayAccess + */ + protected $serverData; + + /** + * Default fields + * + * Array is structured as [key in record.extra => key in $serverData] + * + * @var array + */ + protected $extraFields = [ + 'url' => 'REQUEST_URI', + 'ip' => 'REMOTE_ADDR', + 'http_method' => 'REQUEST_METHOD', + 'server' => 'SERVER_NAME', + 'referrer' => 'HTTP_REFERER', + 'user_agent' => 'HTTP_USER_AGENT', + ]; + + /** + * @param array|\ArrayAccess|null $serverData Array or object w/ ArrayAccess that provides access to the $_SERVER data + * @param array|array|null $extraFields Field names and the related key inside $serverData to be added (or just a list of field names to use the default configured $serverData mapping). If not provided it defaults to: [url, ip, http_method, server, referrer] + unique_id if present in server data + */ + public function __construct($serverData = null, array $extraFields = null) + { + if (null === $serverData) { + $this->serverData = &$_SERVER; + } elseif (is_array($serverData) || $serverData instanceof \ArrayAccess) { + $this->serverData = $serverData; + } else { + throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.'); + } + + $defaultEnabled = ['url', 'ip', 'http_method', 'server', 'referrer']; + if (isset($this->serverData['UNIQUE_ID'])) { + $this->extraFields['unique_id'] = 'UNIQUE_ID'; + $defaultEnabled[] = 'unique_id'; + } + + if (null === $extraFields) { + $extraFields = $defaultEnabled; + } + if (isset($extraFields[0])) { + foreach (array_keys($this->extraFields) as $fieldName) { + if (!in_array($fieldName, $extraFields)) { + unset($this->extraFields[$fieldName]); + } + } + } else { + $this->extraFields = $extraFields; + } + } + + /** + * {@inheritDoc} + */ + public function __invoke(array $record): array + { + // skip processing if for some reason request data + // is not present (CLI or wonky SAPIs) + if (!isset($this->serverData['REQUEST_URI'])) { + return $record; + } + + $record['extra'] = $this->appendExtraFields($record['extra']); + + return $record; + } + + public function addExtraField(string $extraName, string $serverName): self + { + $this->extraFields[$extraName] = $serverName; + + return $this; + } + + /** + * @param mixed[] $extra + * @return mixed[] + */ + private function appendExtraFields(array $extra): array + { + foreach ($this->extraFields as $extraName => $serverName) { + $extra[$extraName] = $this->serverData[$serverName] ?? null; + } + + return $extra; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Registry.php b/msd/vendor/monolog/monolog/src/Monolog/Registry.php new file mode 100644 index 0000000..7272bf9 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Registry.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use InvalidArgumentException; + +/** + * Monolog log registry + * + * Allows to get `Logger` instances in the global scope + * via static method calls on this class. + * + * + * $application = new Monolog\Logger('application'); + * $api = new Monolog\Logger('api'); + * + * Monolog\Registry::addLogger($application); + * Monolog\Registry::addLogger($api); + * + * function testLogger() + * { + * Monolog\Registry::api()->error('Sent to $api Logger instance'); + * Monolog\Registry::application()->error('Sent to $application Logger instance'); + * } + * + * + * @author Tomas Tatarko + */ +class Registry +{ + /** + * List of all loggers in the registry (by named indexes) + * + * @var Logger[] + */ + private static $loggers = []; + + /** + * Adds new logging channel to the registry + * + * @param Logger $logger Instance of the logging channel + * @param string|null $name Name of the logging channel ($logger->getName() by default) + * @param bool $overwrite Overwrite instance in the registry if the given name already exists? + * @throws \InvalidArgumentException If $overwrite set to false and named Logger instance already exists + * @return void + */ + public static function addLogger(Logger $logger, ?string $name = null, bool $overwrite = false) + { + $name = $name ?: $logger->getName(); + + if (isset(self::$loggers[$name]) && !$overwrite) { + throw new InvalidArgumentException('Logger with the given name already exists'); + } + + self::$loggers[$name] = $logger; + } + + /** + * Checks if such logging channel exists by name or instance + * + * @param string|Logger $logger Name or logger instance + */ + public static function hasLogger($logger): bool + { + if ($logger instanceof Logger) { + $index = array_search($logger, self::$loggers, true); + + return false !== $index; + } + + return isset(self::$loggers[$logger]); + } + + /** + * Removes instance from registry by name or instance + * + * @param string|Logger $logger Name or logger instance + */ + public static function removeLogger($logger): void + { + if ($logger instanceof Logger) { + if (false !== ($idx = array_search($logger, self::$loggers, true))) { + unset(self::$loggers[$idx]); + } + } else { + unset(self::$loggers[$logger]); + } + } + + /** + * Clears the registry + */ + public static function clear(): void + { + self::$loggers = []; + } + + /** + * Gets Logger instance from the registry + * + * @param string $name Name of the requested Logger instance + * @throws \InvalidArgumentException If named Logger instance is not in the registry + */ + public static function getInstance($name): Logger + { + if (!isset(self::$loggers[$name])) { + throw new InvalidArgumentException(sprintf('Requested "%s" logger instance is not in the registry', $name)); + } + + return self::$loggers[$name]; + } + + /** + * Gets Logger instance from the registry via static method call + * + * @param string $name Name of the requested Logger instance + * @param mixed[] $arguments Arguments passed to static method call + * @throws \InvalidArgumentException If named Logger instance is not in the registry + * @return Logger Requested instance of Logger + */ + public static function __callStatic($name, $arguments) + { + return self::getInstance($name); + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/ResettableInterface.php b/msd/vendor/monolog/monolog/src/Monolog/ResettableInterface.php new file mode 100644 index 0000000..a8668a6 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/ResettableInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +/** + * Handler or Processor implementing this interface will be reset when Logger::reset() is called. + * + * Resetting ends a log cycle gets them back to their initial state. + * + * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal + * state, and getting it back to a state in which it can receive log records again. + * + * This is useful in case you want to avoid logs leaking between two requests or jobs when you + * have a long running process like a worker or an application server serving multiple requests + * in one process. + * + * @author Grégoire Pineau + */ +interface ResettableInterface +{ + /** + * @return void + */ + public function reset(); +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/SignalHandler.php b/msd/vendor/monolog/monolog/src/Monolog/SignalHandler.php new file mode 100644 index 0000000..56b95a5 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/SignalHandler.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; +use ReflectionExtension; + +/** + * Monolog POSIX signal handler + * + * @author Robert Gust-Bardon + * + * @phpstan-import-type Level from \Monolog\Logger + * @phpstan-import-type LevelName from \Monolog\Logger + */ +class SignalHandler +{ + /** @var LoggerInterface */ + private $logger; + + /** @var array SIG_DFL, SIG_IGN or previous callable */ + private $previousSignalHandler = []; + /** @var array */ + private $signalLevelMap = []; + /** @var array */ + private $signalRestartSyscalls = []; + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * @param int|string $level Level or level name + * @param bool $callPrevious + * @param bool $restartSyscalls + * @param bool|null $async + * @return $this + * + * @phpstan-param Level|LevelName|LogLevel::* $level + */ + public function registerSignalHandler(int $signo, $level = LogLevel::CRITICAL, bool $callPrevious = true, bool $restartSyscalls = true, ?bool $async = true): self + { + if (!extension_loaded('pcntl') || !function_exists('pcntl_signal')) { + return $this; + } + + $level = Logger::toMonologLevel($level); + + if ($callPrevious) { + $handler = pcntl_signal_get_handler($signo); + $this->previousSignalHandler[$signo] = $handler; + } else { + unset($this->previousSignalHandler[$signo]); + } + $this->signalLevelMap[$signo] = $level; + $this->signalRestartSyscalls[$signo] = $restartSyscalls; + + if ($async !== null) { + pcntl_async_signals($async); + } + + pcntl_signal($signo, [$this, 'handleSignal'], $restartSyscalls); + + return $this; + } + + /** + * @param mixed $siginfo + */ + public function handleSignal(int $signo, $siginfo = null): void + { + static $signals = []; + + if (!$signals && extension_loaded('pcntl')) { + $pcntl = new ReflectionExtension('pcntl'); + // HHVM 3.24.2 returns an empty array. + foreach ($pcntl->getConstants() ?: get_defined_constants(true)['Core'] as $name => $value) { + if (substr($name, 0, 3) === 'SIG' && $name[3] !== '_' && is_int($value)) { + $signals[$value] = $name; + } + } + } + + $level = $this->signalLevelMap[$signo] ?? LogLevel::CRITICAL; + $signal = $signals[$signo] ?? $signo; + $context = $siginfo ?? []; + $this->logger->log($level, sprintf('Program received signal %s', $signal), $context); + + if (!isset($this->previousSignalHandler[$signo])) { + return; + } + + if ($this->previousSignalHandler[$signo] === SIG_DFL) { + if (extension_loaded('pcntl') && function_exists('pcntl_signal') && function_exists('pcntl_sigprocmask') && function_exists('pcntl_signal_dispatch') + && extension_loaded('posix') && function_exists('posix_getpid') && function_exists('posix_kill') + ) { + $restartSyscalls = $this->signalRestartSyscalls[$signo] ?? true; + pcntl_signal($signo, SIG_DFL, $restartSyscalls); + pcntl_sigprocmask(SIG_UNBLOCK, [$signo], $oldset); + posix_kill(posix_getpid(), $signo); + pcntl_signal_dispatch(); + pcntl_sigprocmask(SIG_SETMASK, $oldset); + pcntl_signal($signo, [$this, 'handleSignal'], $restartSyscalls); + } + } elseif (is_callable($this->previousSignalHandler[$signo])) { + $this->previousSignalHandler[$signo]($signo, $siginfo); + } + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Test/TestCase.php b/msd/vendor/monolog/monolog/src/Monolog/Test/TestCase.php new file mode 100644 index 0000000..3416ffb --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Test/TestCase.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Test; + +use Monolog\Logger; +use Monolog\DateTimeImmutable; +use Monolog\Formatter\FormatterInterface; + +/** + * Lets you easily generate log records and a dummy formatter for testing purposes + * + * @author Jordi Boggiano + * + * @phpstan-import-type Record from \Monolog\Logger + * @phpstan-import-type Level from \Monolog\Logger + * + * @internal feel free to reuse this to test your own handlers, this is marked internal to avoid issues with PHPStorm https://github.com/Seldaek/monolog/issues/1677 + */ +class TestCase extends \PHPUnit\Framework\TestCase +{ + public function tearDown(): void + { + parent::tearDown(); + + if (isset($this->handler)) { + unset($this->handler); + } + } + + /** + * @param mixed[] $context + * + * @return array Record + * + * @phpstan-param Level $level + * @phpstan-return Record + */ + protected function getRecord(int $level = Logger::WARNING, string $message = 'test', array $context = []): array + { + return [ + 'message' => (string) $message, + 'context' => $context, + 'level' => $level, + 'level_name' => Logger::getLevelName($level), + 'channel' => 'test', + 'datetime' => new DateTimeImmutable(true), + 'extra' => [], + ]; + } + + /** + * @phpstan-return Record[] + */ + protected function getMultipleRecords(): array + { + return [ + $this->getRecord(Logger::DEBUG, 'debug message 1'), + $this->getRecord(Logger::DEBUG, 'debug message 2'), + $this->getRecord(Logger::INFO, 'information'), + $this->getRecord(Logger::WARNING, 'warning'), + $this->getRecord(Logger::ERROR, 'error'), + ]; + } + + protected function getIdentityFormatter(): FormatterInterface + { + $formatter = $this->createMock(FormatterInterface::class); + $formatter->expects($this->any()) + ->method('format') + ->will($this->returnCallback(function ($record) { + return $record['message']; + })); + + return $formatter; + } +} diff --git a/msd/vendor/monolog/monolog/src/Monolog/Utils.php b/msd/vendor/monolog/monolog/src/Monolog/Utils.php new file mode 100644 index 0000000..32b54f2 --- /dev/null +++ b/msd/vendor/monolog/monolog/src/Monolog/Utils.php @@ -0,0 +1,284 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +final class Utils +{ + const DEFAULT_JSON_FLAGS = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION | JSON_INVALID_UTF8_SUBSTITUTE | JSON_PARTIAL_OUTPUT_ON_ERROR; + + public static function getClass(object $object): string + { + $class = \get_class($object); + + if (false === ($pos = \strpos($class, "@anonymous\0"))) { + return $class; + } + + if (false === ($parent = \get_parent_class($class))) { + return \substr($class, 0, $pos + 10); + } + + return $parent . '@anonymous'; + } + + public static function substr(string $string, int $start, ?int $length = null): string + { + if (extension_loaded('mbstring')) { + return mb_strcut($string, $start, $length); + } + + return substr($string, $start, (null === $length) ? strlen($string) : $length); + } + + /** + * Makes sure if a relative path is passed in it is turned into an absolute path + * + * @param string $streamUrl stream URL or path without protocol + */ + public static function canonicalizePath(string $streamUrl): string + { + $prefix = ''; + if ('file://' === substr($streamUrl, 0, 7)) { + $streamUrl = substr($streamUrl, 7); + $prefix = 'file://'; + } + + // other type of stream, not supported + if (false !== strpos($streamUrl, '://')) { + return $streamUrl; + } + + // already absolute + if (substr($streamUrl, 0, 1) === '/' || substr($streamUrl, 1, 1) === ':' || substr($streamUrl, 0, 2) === '\\\\') { + return $prefix.$streamUrl; + } + + $streamUrl = getcwd() . '/' . $streamUrl; + + return $prefix.$streamUrl; + } + + /** + * Return the JSON representation of a value + * + * @param mixed $data + * @param int $encodeFlags flags to pass to json encode, defaults to DEFAULT_JSON_FLAGS + * @param bool $ignoreErrors whether to ignore encoding errors or to throw on error, when ignored and the encoding fails, "null" is returned which is valid json for null + * @throws \RuntimeException if encoding fails and errors are not ignored + * @return string when errors are ignored and the encoding fails, "null" is returned which is valid json for null + */ + public static function jsonEncode($data, ?int $encodeFlags = null, bool $ignoreErrors = false): string + { + if (null === $encodeFlags) { + $encodeFlags = self::DEFAULT_JSON_FLAGS; + } + + if ($ignoreErrors) { + $json = @json_encode($data, $encodeFlags); + if (false === $json) { + return 'null'; + } + + return $json; + } + + $json = json_encode($data, $encodeFlags); + if (false === $json) { + $json = self::handleJsonError(json_last_error(), $data); + } + + return $json; + } + + /** + * Handle a json_encode failure. + * + * If the failure is due to invalid string encoding, try to clean the + * input and encode again. If the second encoding attempt fails, the + * initial error is not encoding related or the input can't be cleaned then + * raise a descriptive exception. + * + * @param int $code return code of json_last_error function + * @param mixed $data data that was meant to be encoded + * @param int $encodeFlags flags to pass to json encode, defaults to JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION + * @throws \RuntimeException if failure can't be corrected + * @return string JSON encoded data after error correction + */ + public static function handleJsonError(int $code, $data, ?int $encodeFlags = null): string + { + if ($code !== JSON_ERROR_UTF8) { + self::throwEncodeError($code, $data); + } + + if (is_string($data)) { + self::detectAndCleanUtf8($data); + } elseif (is_array($data)) { + array_walk_recursive($data, array('Monolog\Utils', 'detectAndCleanUtf8')); + } else { + self::throwEncodeError($code, $data); + } + + if (null === $encodeFlags) { + $encodeFlags = self::DEFAULT_JSON_FLAGS; + } + + $json = json_encode($data, $encodeFlags); + + if ($json === false) { + self::throwEncodeError(json_last_error(), $data); + } + + return $json; + } + + /** + * @internal + */ + public static function pcreLastErrorMessage(int $code): string + { + if (PHP_VERSION_ID >= 80000) { + return preg_last_error_msg(); + } + + $constants = (get_defined_constants(true))['pcre']; + $constants = array_filter($constants, function ($key) { + return substr($key, -6) == '_ERROR'; + }, ARRAY_FILTER_USE_KEY); + + $constants = array_flip($constants); + + return $constants[$code] ?? 'UNDEFINED_ERROR'; + } + + /** + * Throws an exception according to a given code with a customized message + * + * @param int $code return code of json_last_error function + * @param mixed $data data that was meant to be encoded + * @throws \RuntimeException + * + * @return never + */ + private static function throwEncodeError(int $code, $data): void + { + switch ($code) { + case JSON_ERROR_DEPTH: + $msg = 'Maximum stack depth exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Underflow or the modes mismatch'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Unexpected control character found'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $msg = 'Unknown error'; + } + + throw new \RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true)); + } + + /** + * Detect invalid UTF-8 string characters and convert to valid UTF-8. + * + * Valid UTF-8 input will be left unmodified, but strings containing + * invalid UTF-8 codepoints will be reencoded as UTF-8 with an assumed + * original encoding of ISO-8859-15. This conversion may result in + * incorrect output if the actual encoding was not ISO-8859-15, but it + * will be clean UTF-8 output and will not rely on expensive and fragile + * detection algorithms. + * + * Function converts the input in place in the passed variable so that it + * can be used as a callback for array_walk_recursive. + * + * @param mixed $data Input to check and convert if needed, passed by ref + */ + private static function detectAndCleanUtf8(&$data): void + { + if (is_string($data) && !preg_match('//u', $data)) { + $data = preg_replace_callback( + '/[\x80-\xFF]+/', + function ($m) { + return function_exists('mb_convert_encoding') ? mb_convert_encoding($m[0], 'UTF-8', 'ISO-8859-1') : utf8_encode($m[0]); + }, + $data + ); + if (!is_string($data)) { + $pcreErrorCode = preg_last_error(); + throw new \RuntimeException('Failed to preg_replace_callback: ' . $pcreErrorCode . ' / ' . self::pcreLastErrorMessage($pcreErrorCode)); + } + $data = str_replace( + ['¤', '¦', '¨', '´', '¸', '¼', '½', '¾'], + ['€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'], + $data + ); + } + } + + /** + * Converts a string with a valid 'memory_limit' format, to bytes. + * + * @param string|false $val + * @return int|false Returns an integer representing bytes. Returns FALSE in case of error. + */ + public static function expandIniShorthandBytes($val) + { + if (!is_string($val)) { + return false; + } + + // support -1 + if ((int) $val < 0) { + return (int) $val; + } + + if (!preg_match('/^\s*(?\d+)(?:\.\d+)?\s*(?[gmk]?)\s*$/i', $val, $match)) { + return false; + } + + $val = (int) $match['val']; + switch (strtolower($match['unit'] ?? '')) { + case 'g': + $val *= 1024; + case 'm': + $val *= 1024; + case 'k': + $val *= 1024; + } + + return $val; + } + + /** + * @param array $record + */ + public static function getRecordMessageForException(array $record): string + { + $context = ''; + $extra = ''; + try { + if ($record['context']) { + $context = "\nContext: " . json_encode($record['context']); + } + if ($record['extra']) { + $extra = "\nExtra: " . json_encode($record['extra']); + } + } catch (\Throwable $e) { + // noop + } + + return "\nThe exception occurred while attempting to log: " . $record['message'] . $context . $extra; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/AUTHORS b/msd/vendor/phpseclib/phpseclib/AUTHORS new file mode 100644 index 0000000..a534e2a --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/AUTHORS @@ -0,0 +1,7 @@ +phpseclib Lead Developer: TerraFrost (Jim Wigginton) + +phpseclib Developers: monnerat (Patrick Monnerat) + bantu (Andreas Fischer) + petrich (Hans-Jürgen Petrich) + GrahamCampbell (Graham Campbell) + hc-jworman \ No newline at end of file diff --git a/msd/vendor/phpseclib/phpseclib/BACKERS.md b/msd/vendor/phpseclib/phpseclib/BACKERS.md new file mode 100644 index 0000000..f942f48 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/BACKERS.md @@ -0,0 +1,14 @@ +# Backers + +phpseclib ongoing development is made possible by [Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme) and by contributions by users like you. Thank you. + +## Backers + +- Allan Simon +- [ChargeOver](https://chargeover.com/) +- Raghu Veer Dendukuri +- Zane Hooper +- [Setasign](https://www.setasign.com/) +- [Charles Severance](https://github.com/csev) +- [Rachel Fish](https://github.com/itsrachelfish) +- Tharyrok \ No newline at end of file diff --git a/msd/vendor/phpseclib/phpseclib/LICENSE b/msd/vendor/phpseclib/phpseclib/LICENSE new file mode 100644 index 0000000..384bb52 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2011-2019 TerraFrost and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/msd/vendor/phpseclib/phpseclib/README.md b/msd/vendor/phpseclib/phpseclib/README.md new file mode 100644 index 0000000..9be5517 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/README.md @@ -0,0 +1,101 @@ +# phpseclib - PHP Secure Communications Library + +[![Build Status](https://travis-ci.com/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.com/github/phpseclib/phpseclib) + +## Supporting phpseclib + +- [Become a backer or sponsor on Patreon](https://www.patreon.com/phpseclib) +- [One-time donation via PayPal or crypto-currencies](http://sourceforge.net/donate/index.php?group_id=198487) +- [Subscribe to Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme) + +## Introduction + +MIT-licensed pure-PHP implementations of the following: + +SSH-2, SFTP, X.509, an arbitrary-precision integer arithmetic library, Ed25519 / Ed449 / Curve25519 / Curve449, ECDSA / ECDH (with support for 66 curves), RSA (PKCS#1 v2.2 compliant), DSA / DH, DES / 3DES / RC4 / Rijndael / AES / Blowfish / Twofish / Salsa20 / ChaCha20, GCM / Poly1305 + +* [Browse Git](https://github.com/phpseclib/phpseclib) + +## Documentation + +* [Documentation / Manual](https://phpseclib.com/) +* [API Documentation](https://api.phpseclib.com/2.0/) (generated by Doctum) + +## Branches + +### master + +* Development Branch +* Unstable API +* Do not use in production + +### 3.0 + +* Long term support (LTS) release +* Major expansion of cryptographic primitives +* Minimum PHP version: 5.6.1 +* PSR-4 autoloading with namespace rooted at `\phpseclib3` +* Install via Composer: `composer require phpseclib/phpseclib:~3.0` + +### 2.0 + +* Long term support (LTS) release +* Modernized version of 1.0 +* Minimum PHP version: 5.3.3 +* PSR-4 autoloading with namespace rooted at `\phpseclib` +* Install via Composer: `composer require phpseclib/phpseclib:~2.0` + +### 1.0 + +* Long term support (LTS) release +* PHP4 compatible +* Composer compatible (PSR-0 autoloading) +* Install using Composer: `composer require phpseclib/phpseclib:~1.0` +* Install using PEAR: See [phpseclib PEAR Channel Documentation](http://phpseclib.sourceforge.net/pear.htm) +* [Download 1.0.20 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.20.zip/download) + +## Security contact information + +To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. + +## Support + +Need Support? + +* [Checkout Questions and Answers on Stack Overflow](http://stackoverflow.com/questions/tagged/phpseclib) +* [Create a Support Ticket on GitHub](https://github.com/phpseclib/phpseclib/issues/new) +* [Browse the Support Forum](http://www.frostjedi.com/phpbb/viewforum.php?f=46) (no longer in use) + +## Special Thanks + +Special Thanks to our $50+ sponsors!: + +- Allan Simon +- [ChargeOver](https://chargeover.com/) + +## Contributing + +1. Fork the Project + +2. Ensure you have Composer installed (see [Composer Download Instructions](https://getcomposer.org/download/)) + +3. Install Development Dependencies + + ``` sh + composer install + ``` + +4. Create a Feature Branch + +5. (Recommended) Run the Test Suite + + ``` sh + vendor/bin/phpunit + ``` +6. (Recommended) Check whether your code conforms to our Coding Standards by running + + ``` sh + vendor/bin/phing -f build/build.xml sniff + ``` + +7. Send us a Pull Request diff --git a/msd/vendor/phpseclib/phpseclib/appveyor.yml b/msd/vendor/phpseclib/phpseclib/appveyor.yml new file mode 100644 index 0000000..210a903 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/appveyor.yml @@ -0,0 +1,27 @@ +build: false +shallow_clone: false +platform: + - x86 + - x64 +clone_folder: C:\projects\phpseclib + +install: + - cinst -y OpenSSL.Light + - SET PATH=C:\Program Files\OpenSSL;%PATH% + - sc config wuauserv start= auto + - net start wuauserv + - cinst -y php --version 5.6.30 + - cd c:\tools\php56 + - copy php.ini-production php.ini + - echo date.timezone="UTC" >> php.ini + - echo extension_dir=ext >> php.ini + - echo extension=php_openssl.dll >> php.ini + - echo extension=php_gmp.dll >> php.ini + - cd C:\projects\phpseclib + - SET PATH=C:\tools\php56;%PATH% + - php.exe -r "readfile('http://getcomposer.org/installer');" | php.exe + - php.exe composer.phar install --prefer-source --no-interaction + +test_script: + - cd C:\projects\phpseclib + - vendor\bin\phpunit.bat tests/Windows32Test.php \ No newline at end of file diff --git a/msd/vendor/phpseclib/phpseclib/composer.json b/msd/vendor/phpseclib/phpseclib/composer.json new file mode 100644 index 0000000..3fbffa6 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/composer.json @@ -0,0 +1,76 @@ +{ + "name": "phpseclib/phpseclib", + "type": "library", + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "keywords": [ + "security", + "crypto", + "cryptography", + "encryption", + "signature", + "signing", + "rsa", + "aes", + "blowfish", + "twofish", + "ssh", + "sftp", + "x509", + "x.509", + "asn1", + "asn.1", + "BigInteger" + ], + "homepage": "http://phpseclib.sourceforge.net", + "license": "MIT", + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phing/phing": "~2.7", + "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4", + "squizlabs/php_codesniffer": "~2.0" + }, + "suggest": { + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-xml": "Install the XML extension to load XML formatted public keys." + }, + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib\\": "phpseclib/" + } + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php new file mode 100644 index 0000000..27e88a1 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php @@ -0,0 +1,126 @@ + + * setKey('abcdefghijklmnop'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $aes->decrypt($aes->encrypt($plaintext)); + * ?> + * + * + * @category Crypt + * @package AES + * @author Jim Wigginton + * @copyright 2008 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +/** + * Pure-PHP implementation of AES. + * + * @package AES + * @author Jim Wigginton + * @access public + */ +class AES extends Rijndael +{ + /** + * Dummy function + * + * Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, this function is, technically, available, but it doesn't do anything. + * + * @see \phpseclib\Crypt\Rijndael::setBlockLength() + * @access public + * @param int $length + */ + function setBlockLength($length) + { + return; + } + + /** + * Sets the key length + * + * Valid key lengths are 128, 192, and 256. If the length is less than 128, it will be rounded up to + * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount. + * + * @see \phpseclib\Crypt\Rijndael:setKeyLength() + * @access public + * @param int $length + */ + function setKeyLength($length) + { + switch ($length) { + case 160: + $length = 192; + break; + case 224: + $length = 256; + } + parent::setKeyLength($length); + } + + /** + * Sets the key. + * + * Rijndael supports five different key lengths, AES only supports three. + * + * @see \phpseclib\Crypt\Rijndael:setKey() + * @see setKeyLength() + * @access public + * @param string $key + */ + function setKey($key) + { + parent::setKey($key); + + if (!$this->explicit_key_length) { + $length = strlen($key); + switch (true) { + case $length <= 16: + $this->key_length = 16; + break; + case $length <= 24: + $this->key_length = 24; + break; + default: + $this->key_length = 32; + } + $this->_setEngine(); + } + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php new file mode 100644 index 0000000..f3f9097 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php @@ -0,0 +1,2907 @@ + + * @author Hans-Juergen Petrich + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +/** + * Base Class for all \phpseclib\Crypt\* cipher classes + * + * @package Base + * @author Jim Wigginton + * @author Hans-Juergen Petrich + */ +abstract class Base +{ + /**#@+ + * @access public + * @see \phpseclib\Crypt\Base::encrypt() + * @see \phpseclib\Crypt\Base::decrypt() + */ + /** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ + const MODE_CTR = -1; + /** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ + const MODE_ECB = 1; + /** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + */ + const MODE_CBC = 2; + /** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 + */ + const MODE_CFB = 3; + /** + * Encrypt / decrypt using the Cipher Feedback mode (8bit) + */ + const MODE_CFB8 = 6; + /** + * Encrypt / decrypt using the Output Feedback mode (8bit) + */ + const MODE_OFB8 = 7; + /** + * Encrypt / decrypt using the Output Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 + */ + const MODE_OFB = 4; + /** + * Encrypt / decrypt using streaming mode. + */ + const MODE_STREAM = 5; + /**#@-*/ + + /** + * Whirlpool available flag + * + * @see \phpseclib\Crypt\Base::_hashInlineCryptFunction() + * @var bool + * @access private + */ + static $WHIRLPOOL_AVAILABLE; + + /**#@+ + * @access private + * @see \phpseclib\Crypt\Base::__construct() + */ + /** + * Base value for the internal implementation $engine switch + */ + const ENGINE_INTERNAL = 1; + /** + * Base value for the mcrypt implementation $engine switch + */ + const ENGINE_MCRYPT = 2; + /** + * Base value for the mcrypt implementation $engine switch + */ + const ENGINE_OPENSSL = 3; + /**#@-*/ + + /** + * The Encryption Mode + * + * @see self::__construct() + * @var int + * @access private + */ + var $mode; + + /** + * The Block Length of the block cipher + * + * @var int + * @access private + */ + var $block_size = 16; + + /** + * The Key + * + * @see self::setKey() + * @var string + * @access private + */ + var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + + /** + * The Initialization Vector + * + * @see self::setIV() + * @var string + * @access private + */ + var $iv = ''; + + /** + * A "sliding" Initialization Vector + * + * @see self::enableContinuousBuffer() + * @see self::_clearBuffers() + * @var string + * @access private + */ + var $encryptIV; + + /** + * A "sliding" Initialization Vector + * + * @see self::enableContinuousBuffer() + * @see self::_clearBuffers() + * @var string + * @access private + */ + var $decryptIV; + + /** + * Continuous Buffer status + * + * @see self::enableContinuousBuffer() + * @var bool + * @access private + */ + var $continuousBuffer = false; + + /** + * Encryption buffer for CTR, OFB and CFB modes + * + * @see self::encrypt() + * @see self::_clearBuffers() + * @var array + * @access private + */ + var $enbuffer; + + /** + * Decryption buffer for CTR, OFB and CFB modes + * + * @see self::decrypt() + * @see self::_clearBuffers() + * @var array + * @access private + */ + var $debuffer; + + /** + * mcrypt resource for encryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see self::encrypt() + * @var resource + * @access private + */ + var $enmcrypt; + + /** + * mcrypt resource for decryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see self::decrypt() + * @var resource + * @access private + */ + var $demcrypt; + + /** + * Does the enmcrypt resource need to be (re)initialized? + * + * @see \phpseclib\Crypt\Twofish::setKey() + * @see \phpseclib\Crypt\Twofish::setIV() + * @var bool + * @access private + */ + var $enchanged = true; + + /** + * Does the demcrypt resource need to be (re)initialized? + * + * @see \phpseclib\Crypt\Twofish::setKey() + * @see \phpseclib\Crypt\Twofish::setIV() + * @var bool + * @access private + */ + var $dechanged = true; + + /** + * mcrypt resource for CFB mode + * + * mcrypt's CFB mode, in (and only in) buffered context, + * is broken, so phpseclib implements the CFB mode by it self, + * even when the mcrypt php extension is available. + * + * In order to do the CFB-mode work (fast) phpseclib + * use a separate ECB-mode mcrypt resource. + * + * @link http://phpseclib.sourceforge.net/cfb-demo.phps + * @see self::encrypt() + * @see self::decrypt() + * @see self::_setupMcrypt() + * @var resource + * @access private + */ + var $ecb; + + /** + * Optimizing value while CFB-encrypting + * + * Only relevant if $continuousBuffer enabled + * and $engine == self::ENGINE_MCRYPT + * + * It's faster to re-init $enmcrypt if + * $buffer bytes > $cfb_init_len than + * using the $ecb resource furthermore. + * + * This value depends of the chosen cipher + * and the time it would be needed for it's + * initialization [by mcrypt_generic_init()] + * which, typically, depends on the complexity + * on its internaly Key-expanding algorithm. + * + * @see self::encrypt() + * @var int + * @access private + */ + var $cfb_init_len = 600; + + /** + * Does internal cipher state need to be (re)initialized? + * + * @see self::setKey() + * @see self::setIV() + * @see self::disableContinuousBuffer() + * @var bool + * @access private + */ + var $changed = true; + + /** + * Padding status + * + * @see self::enablePadding() + * @var bool + * @access private + */ + var $padding = true; + + /** + * Is the mode one that is paddable? + * + * @see self::__construct() + * @var bool + * @access private + */ + var $paddable = false; + + /** + * Holds which crypt engine internaly should be use, + * which will be determined automatically on __construct() + * + * Currently available $engines are: + * - self::ENGINE_OPENSSL (very fast, php-extension: openssl, extension_loaded('openssl') required) + * - self::ENGINE_MCRYPT (fast, php-extension: mcrypt, extension_loaded('mcrypt') required) + * - self::ENGINE_INTERNAL (slower, pure php-engine, no php-extension required) + * + * @see self::_setEngine() + * @see self::encrypt() + * @see self::decrypt() + * @var int + * @access private + */ + var $engine; + + /** + * Holds the preferred crypt engine + * + * @see self::_setEngine() + * @see self::setPreferredEngine() + * @var int + * @access private + */ + var $preferredEngine; + + /** + * The mcrypt specific name of the cipher + * + * Only used if $engine == self::ENGINE_MCRYPT + * + * @link http://www.php.net/mcrypt_module_open + * @link http://www.php.net/mcrypt_list_algorithms + * @see self::_setupMcrypt() + * @var string + * @access private + */ + var $cipher_name_mcrypt; + + /** + * The openssl specific name of the cipher + * + * Only used if $engine == self::ENGINE_OPENSSL + * + * @link http://www.php.net/openssl-get-cipher-methods + * @var string + * @access private + */ + var $cipher_name_openssl; + + /** + * The openssl specific name of the cipher in ECB mode + * + * If OpenSSL does not support the mode we're trying to use (CTR) + * it can still be emulated with ECB mode. + * + * @link http://www.php.net/openssl-get-cipher-methods + * @var string + * @access private + */ + var $cipher_name_openssl_ecb; + + /** + * The default salt used by setPassword() + * + * @see self::setPassword() + * @var string + * @access private + */ + var $password_default_salt = 'phpseclib/salt'; + + /** + * The name of the performance-optimized callback function + * + * Used by encrypt() / decrypt() + * only if $engine == self::ENGINE_INTERNAL + * + * @see self::encrypt() + * @see self::decrypt() + * @see self::_setupInlineCrypt() + * @see self::$use_inline_crypt + * @var Callback + * @access private + */ + var $inline_crypt; + + /** + * Holds whether performance-optimized $inline_crypt() can/should be used. + * + * @see self::encrypt() + * @see self::decrypt() + * @see self::inline_crypt + * @var mixed + * @access private + */ + var $use_inline_crypt = true; + + /** + * If OpenSSL can be used in ECB but not in CTR we can emulate CTR + * + * @see self::_openssl_ctr_process() + * @var bool + * @access private + */ + var $openssl_emulate_ctr = false; + + /** + * Determines what options are passed to openssl_encrypt/decrypt + * + * @see self::isValidEngine() + * @var mixed + * @access private + */ + var $openssl_options; + + /** + * Has the key length explicitly been set or should it be derived from the key, itself? + * + * @see self::setKeyLength() + * @var bool + * @access private + */ + var $explicit_key_length = false; + + /** + * Don't truncate / null pad key + * + * @see self::_clearBuffers() + * @var bool + * @access private + */ + var $skip_key_adjustment = false; + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. + * + * $mode could be: + * + * - self::MODE_ECB + * + * - self::MODE_CBC + * + * - self::MODE_CTR + * + * - self::MODE_CFB + * + * - self::MODE_OFB + * + * If not explicitly set, self::MODE_CBC will be used. + * + * @param int $mode + * @access public + */ + function __construct($mode = self::MODE_CBC) + { + // $mode dependent settings + switch ($mode) { + case self::MODE_ECB: + $this->paddable = true; + $this->mode = self::MODE_ECB; + break; + case self::MODE_CTR: + case self::MODE_CFB: + case self::MODE_CFB8: + case self::MODE_OFB8: + case self::MODE_OFB: + case self::MODE_STREAM: + $this->mode = $mode; + break; + case self::MODE_CBC: + default: + $this->paddable = true; + $this->mode = self::MODE_CBC; + } + + $this->_setEngine(); + + // Determining whether inline crypting can be used by the cipher + if ($this->use_inline_crypt !== false) { + $this->use_inline_crypt = version_compare(PHP_VERSION, '5.3.0') >= 0 || function_exists('create_function'); + } + + if (!defined('PHP_INT_SIZE')) { + define('PHP_INT_SIZE', 4); + } + + if (!defined('CRYPT_BASE_USE_REG_INTVAL')) { + switch (true) { + // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster + case (PHP_OS & "\xDF\xDF\xDF") === 'WIN': + case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM': + case PHP_INT_SIZE == 8: + define('CRYPT_BASE_USE_REG_INTVAL', true); + break; + case (php_uname('m') & "\xDF\xDF\xDF") == 'ARM': + switch (true) { + /* PHP 7.0.0 introduced a bug that affected 32-bit ARM processors: + + https://github.com/php/php-src/commit/716da71446ebbd40fa6cf2cea8a4b70f504cc3cd + + altho the changelogs make no mention of it, this bug was fixed with this commit: + + https://github.com/php/php-src/commit/c1729272b17a1fe893d1a54e423d3b71470f3ee8 + + affected versions of PHP are: 7.0.x, 7.1.0 - 7.1.23 and 7.2.0 - 7.2.11 */ + case PHP_VERSION_ID >= 70000 && PHP_VERSION_ID <= 70123: + case PHP_VERSION_ID >= 70200 && PHP_VERSION_ID <= 70211: + define('CRYPT_BASE_USE_REG_INTVAL', false); + break; + default: + define('CRYPT_BASE_USE_REG_INTVAL', true); + } + } + } + } + + /** + * Sets the initialization vector. (optional) + * + * SetIV is not required when self::MODE_ECB (or ie for AES: \phpseclib\Crypt\AES::MODE_ECB) is being used. If not explicitly set, it'll be assumed + * to be all zero's. + * + * @access public + * @param string $iv + * @internal Can be overwritten by a sub class, but does not have to be + */ + function setIV($iv) + { + if ($this->mode == self::MODE_ECB) { + return; + } + + $this->iv = $iv; + $this->changed = true; + } + + /** + * Sets the key length. + * + * Keys with explicitly set lengths need to be treated accordingly + * + * @access public + * @param int $length + */ + function setKeyLength($length) + { + $this->explicit_key_length = true; + $this->changed = true; + $this->_setEngine(); + } + + /** + * Returns the current key length in bits + * + * @access public + * @return int + */ + function getKeyLength() + { + return $this->key_length << 3; + } + + /** + * Returns the current block length in bits + * + * @access public + * @return int + */ + function getBlockLength() + { + return $this->block_size << 3; + } + + /** + * Sets the key. + * + * The min/max length(s) of the key depends on the cipher which is used. + * If the key not fits the length(s) of the cipher it will paded with null bytes + * up to the closest valid key length. If the key is more than max length, + * we trim the excess bits. + * + * If the key is not explicitly set, it'll be assumed to be all null bytes. + * + * @access public + * @param string $key + * @internal Could, but not must, extend by the child Crypt_* class + */ + function setKey($key) + { + if (!$this->explicit_key_length) { + $this->setKeyLength(strlen($key) << 3); + $this->explicit_key_length = false; + } + + $this->key = $key; + $this->changed = true; + $this->_setEngine(); + } + + /** + * Sets the password. + * + * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows: + * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2} or pbkdf1: + * $hash, $salt, $count, $dkLen + * + * Where $hash (default = sha1) currently supports the following hashes: see: Crypt/Hash.php + * {@link https://en.wikipedia.org/wiki/Bcrypt bcypt}: + * $salt, $rounds, $keylen + * + * This is a modified version of bcrypt used by OpenSSH. + * + * @see Crypt/Hash.php + * @param string $password + * @param string $method + * @return bool + * @access public + * @internal Could, but not must, extend by the child Crypt_* class + */ + function setPassword($password, $method = 'pbkdf2') + { + $key = ''; + + switch ($method) { + case 'bcrypt': + $func_args = func_get_args(); + + if (!isset($func_args[2])) { + return false; + } + + $salt = $func_args[2]; + + $rounds = isset($func_args[3]) ? $func_args[3] : 16; + $keylen = isset($func_args[4]) ? $func_args[4] : $this->key_length; + + $bf = new Blowfish(); + $key = $bf->bcrypt_pbkdf($password, $salt, $keylen + $this->block_size, $rounds); + if (!$key) { + return false; + } + + $this->setKey(substr($key, 0, $keylen)); + $this->setIV(substr($key, $keylen)); + + return true; + default: // 'pbkdf2' or 'pbkdf1' + $func_args = func_get_args(); + + // Hash function + $hash = isset($func_args[2]) ? $func_args[2] : 'sha1'; + + // WPA and WPA2 use the SSID as the salt + $salt = isset($func_args[3]) ? $func_args[3] : $this->password_default_salt; + + // RFC2898#section-4.2 uses 1,000 iterations by default + // WPA and WPA2 use 4,096. + $count = isset($func_args[4]) ? $func_args[4] : 1000; + + // Keylength + if (isset($func_args[5])) { + $dkLen = $func_args[5]; + } else { + $dkLen = $method == 'pbkdf1' ? 2 * $this->key_length : $this->key_length; + } + + switch (true) { + case $method == 'pbkdf1': + $hashObj = new Hash(); + $hashObj->setHash($hash); + if ($dkLen > $hashObj->getLength()) { + user_error('Derived key too long'); + return false; + } + $t = $password . $salt; + for ($i = 0; $i < $count; ++$i) { + $t = $hashObj->hash($t); + } + $key = substr($t, 0, $dkLen); + + $this->setKey(substr($key, 0, $dkLen >> 1)); + $this->setIV(substr($key, $dkLen >> 1)); + + return true; + // Determining if php[>=5.5.0]'s hash_pbkdf2() function avail- and useable + case !function_exists('hash_pbkdf2'): + case !function_exists('hash_algos'): + case !in_array($hash, hash_algos()): + $i = 1; + $hmac = new Hash(); + $hmac->setHash($hash); + $hmac->setKey($password); + while (strlen($key) < $dkLen) { + $f = $u = $hmac->hash($salt . pack('N', $i++)); + for ($j = 2; $j <= $count; ++$j) { + $u = $hmac->hash($u); + $f^= $u; + } + $key.= $f; + } + $key = substr($key, 0, $dkLen); + break; + default: + $key = hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true); + } + } + + $this->setKey($key); + + return true; + } + + /** + * Encrypts a message. + * + * $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other cipher + * implementations may or may not pad in the same manner. Other common approaches to padding and the reasons why it's + * necessary are discussed in the following + * URL: + * + * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html} + * + * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does. + * strlen($plaintext) will still need to be a multiple of the block size, however, arbitrary values can be added to make it that + * length. + * + * @see self::decrypt() + * @access public + * @param string $plaintext + * @return string $ciphertext + * @internal Could, but not must, extend by the child Crypt_* class + */ + function encrypt($plaintext) + { + if ($this->paddable) { + $plaintext = $this->_pad($plaintext); + } + + if ($this->engine === self::ENGINE_OPENSSL) { + if ($this->changed) { + $this->_clearBuffers(); + $this->changed = false; + } + switch ($this->mode) { + case self::MODE_STREAM: + return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); + case self::MODE_ECB: + $result = @openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); + return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result; + case self::MODE_CBC: + $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV); + if (!defined('OPENSSL_RAW_DATA')) { + $result = substr($result, 0, -$this->block_size); + } + if ($this->continuousBuffer) { + $this->encryptIV = substr($result, -$this->block_size); + } + return $result; + case self::MODE_CTR: + return $this->_openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer); + case self::MODE_CFB: + // cfb loosely routines inspired by openssl's: + // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} + $ciphertext = ''; + if ($this->continuousBuffer) { + $iv = &$this->encryptIV; + $pos = &$this->enbuffer['pos']; + } else { + $iv = $this->encryptIV; + $pos = 0; + } + $len = strlen($plaintext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $this->block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $ciphertext = substr($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + $plaintext = substr($plaintext, $i); + } + + $overflow = $len % $this->block_size; + + if ($overflow) { + $ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); + $iv = $this->_string_pop($ciphertext, $this->block_size); + + $size = $len - $overflow; + $block = $iv ^ substr($plaintext, -$overflow); + $iv = substr_replace($iv, $block, 0, $overflow); + $ciphertext.= $block; + $pos = $overflow; + } elseif ($len) { + $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); + $iv = substr($ciphertext, -$this->block_size); + } + + return $ciphertext; + case self::MODE_CFB8: + $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV); + if ($this->continuousBuffer) { + if (($len = strlen($ciphertext)) >= $this->block_size) { + $this->encryptIV = substr($ciphertext, -$this->block_size); + } else { + $this->encryptIV = substr($this->encryptIV, $len - $this->block_size) . substr($ciphertext, -$len); + } + } + return $ciphertext; + case self::MODE_OFB8: + // OpenSSL has built in support for cfb8 but not ofb8 + $ciphertext = ''; + $len = strlen($plaintext); + $iv = $this->encryptIV; + + for ($i = 0; $i < $len; ++$i) { + $xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV); + $ciphertext.= $plaintext[$i] ^ $xor; + $iv = substr($iv, 1) . $xor[0]; + } + + if ($this->continuousBuffer) { + $this->encryptIV = $iv; + } + break; + case self::MODE_OFB: + return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer); + } + } + + if ($this->engine === self::ENGINE_MCRYPT) { + set_error_handler(array($this, 'do_nothing')); + + if ($this->changed) { + $this->_setupMcrypt(); + $this->changed = false; + } + if ($this->enchanged) { + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + $this->enchanged = false; + } + + // re: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} + // using mcrypt's default handing of CFB the above would output two different things. using phpseclib's + // rewritten CFB implementation the above outputs the same thing twice. + if ($this->mode == self::MODE_CFB && $this->continuousBuffer) { + $block_size = $this->block_size; + $iv = &$this->encryptIV; + $pos = &$this->enbuffer['pos']; + $len = strlen($plaintext); + $ciphertext = ''; + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + $ciphertext = substr($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + $this->enbuffer['enmcrypt_init'] = true; + } + if ($len >= $block_size) { + if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) { + if ($this->enbuffer['enmcrypt_init'] === true) { + mcrypt_generic_init($this->enmcrypt, $this->key, $iv); + $this->enbuffer['enmcrypt_init'] = false; + } + $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size)); + $iv = substr($ciphertext, -$block_size); + $len%= $block_size; + } else { + while ($len >= $block_size) { + $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size); + $ciphertext.= $iv; + $len-= $block_size; + $i+= $block_size; + } + } + } + + if ($len) { + $iv = mcrypt_generic($this->ecb, $iv); + $block = $iv ^ substr($plaintext, -$len); + $iv = substr_replace($iv, $block, 0, $len); + $ciphertext.= $block; + $pos = $len; + } + + restore_error_handler(); + + return $ciphertext; + } + + $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); + + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + } + + restore_error_handler(); + + return $ciphertext; + } + + if ($this->changed) { + $this->_setup(); + $this->changed = false; + } + if ($this->use_inline_crypt) { + $inline = $this->inline_crypt; + return $inline('encrypt', $this, $plaintext); + } + + $buffer = &$this->enbuffer; + $block_size = $this->block_size; + $ciphertext = ''; + switch ($this->mode) { + case self::MODE_ECB: + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size)); + } + break; + case self::MODE_CBC: + $xor = $this->encryptIV; + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + $block = $this->_encryptBlock($block ^ $xor); + $xor = $block; + $ciphertext.= $block; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + } + break; + case self::MODE_CTR: + $xor = $this->encryptIV; + if (strlen($buffer['ciphertext'])) { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + if (strlen($block) > strlen($buffer['ciphertext'])) { + $buffer['ciphertext'].= $this->_encryptBlock($xor); + $this->_increment_str($xor); + } + $key = $this->_string_shift($buffer['ciphertext'], $block_size); + $ciphertext.= $block ^ $key; + } + } else { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + $key = $this->_encryptBlock($xor); + $this->_increment_str($xor); + $ciphertext.= $block ^ $key; + } + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + if ($start = strlen($plaintext) % $block_size) { + $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; + } + } + break; + case self::MODE_CFB: + // cfb loosely routines inspired by openssl's: + // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} + if ($this->continuousBuffer) { + $iv = &$this->encryptIV; + $pos = &$buffer['pos']; + } else { + $iv = $this->encryptIV; + $pos = 0; + } + $len = strlen($plaintext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $ciphertext = substr($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + } + while ($len >= $block_size) { + $iv = $this->_encryptBlock($iv) ^ substr($plaintext, $i, $block_size); + $ciphertext.= $iv; + $len-= $block_size; + $i+= $block_size; + } + if ($len) { + $iv = $this->_encryptBlock($iv); + $block = $iv ^ substr($plaintext, $i); + $iv = substr_replace($iv, $block, 0, $len); + $ciphertext.= $block; + $pos = $len; + } + break; + case self::MODE_CFB8: + // compared to regular CFB, which encrypts a block at a time, + // here, we're encrypting a byte at a time + $ciphertext = ''; + $len = strlen($plaintext); + $iv = $this->encryptIV; + + for ($i = 0; $i < $len; ++$i) { + $ciphertext.= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv)); + $iv = substr($iv, 1) . $c; + } + + if ($this->continuousBuffer) { + if ($len >= $block_size) { + $this->encryptIV = substr($ciphertext, -$block_size); + } else { + $this->encryptIV = substr($this->encryptIV, $len - $block_size) . substr($ciphertext, -$len); + } + } + break; + case self::MODE_OFB8: + $ciphertext = ''; + $len = strlen($plaintext); + $iv = $this->encryptIV; + + for ($i = 0; $i < $len; ++$i) { + $xor = $this->_encryptBlock($iv); + $ciphertext.= $plaintext[$i] ^ $xor; + $iv = substr($iv, 1) . $xor[0]; + } + + if ($this->continuousBuffer) { + $this->encryptIV = $iv; + } + break; + case self::MODE_OFB: + $xor = $this->encryptIV; + if (strlen($buffer['xor'])) { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + if (strlen($block) > strlen($buffer['xor'])) { + $xor = $this->_encryptBlock($xor); + $buffer['xor'].= $xor; + } + $key = $this->_string_shift($buffer['xor'], $block_size); + $ciphertext.= $block ^ $key; + } + } else { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $xor = $this->_encryptBlock($xor); + $ciphertext.= substr($plaintext, $i, $block_size) ^ $xor; + } + $key = $xor; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + if ($start = strlen($plaintext) % $block_size) { + $buffer['xor'] = substr($key, $start) . $buffer['xor']; + } + } + break; + case self::MODE_STREAM: + $ciphertext = $this->_encryptBlock($plaintext); + break; + } + + return $ciphertext; + } + + /** + * Decrypts a message. + * + * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until + * it is. + * + * @see self::encrypt() + * @access public + * @param string $ciphertext + * @return string $plaintext + * @internal Could, but not must, extend by the child Crypt_* class + */ + function decrypt($ciphertext) + { + if ($this->paddable) { + // we pad with chr(0) since that's what mcrypt_generic does. to quote from {@link http://www.php.net/function.mcrypt-generic}: + // "The data is padded with "\0" to make sure the length of the data is n * blocksize." + $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($this->block_size - strlen($ciphertext) % $this->block_size) % $this->block_size, chr(0)); + } + + if ($this->engine === self::ENGINE_OPENSSL) { + if ($this->changed) { + $this->_clearBuffers(); + $this->changed = false; + } + switch ($this->mode) { + case self::MODE_STREAM: + $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options); + break; + case self::MODE_ECB: + if (!defined('OPENSSL_RAW_DATA')) { + $ciphertext.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true); + } + $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options); + break; + case self::MODE_CBC: + if (!defined('OPENSSL_RAW_DATA')) { + $padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size); + $ciphertext.= substr(@openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size); + $offset = 2 * $this->block_size; + } else { + $offset = $this->block_size; + } + $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV); + if ($this->continuousBuffer) { + $this->decryptIV = substr($ciphertext, -$offset, $this->block_size); + } + break; + case self::MODE_CTR: + $plaintext = $this->_openssl_ctr_process($ciphertext, $this->decryptIV, $this->debuffer); + break; + case self::MODE_CFB: + // cfb loosely routines inspired by openssl's: + // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} + $plaintext = ''; + if ($this->continuousBuffer) { + $iv = &$this->decryptIV; + $pos = &$this->debuffer['pos']; + } else { + $iv = $this->decryptIV; + $pos = 0; + } + $len = strlen($ciphertext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $this->block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $this->blocksize + $plaintext = substr($iv, $orig_pos) ^ $ciphertext; + $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); + $ciphertext = substr($ciphertext, $i); + } + $overflow = $len % $this->block_size; + if ($overflow) { + $plaintext.= openssl_decrypt(substr($ciphertext, 0, -$overflow), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); + if ($len - $overflow) { + $iv = substr($ciphertext, -$overflow - $this->block_size, -$overflow); + } + $iv = openssl_encrypt(str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); + $plaintext.= $iv ^ substr($ciphertext, -$overflow); + $iv = substr_replace($iv, substr($ciphertext, -$overflow), 0, $overflow); + $pos = $overflow; + } elseif ($len) { + $plaintext.= openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); + $iv = substr($ciphertext, -$this->block_size); + } + break; + case self::MODE_CFB8: + $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV); + if ($this->continuousBuffer) { + if (($len = strlen($ciphertext)) >= $this->block_size) { + $this->decryptIV = substr($ciphertext, -$this->block_size); + } else { + $this->decryptIV = substr($this->decryptIV, $len - $this->block_size) . substr($ciphertext, -$len); + } + } + break; + case self::MODE_OFB8: + $plaintext = ''; + $len = strlen($ciphertext); + $iv = $this->decryptIV; + + for ($i = 0; $i < $len; ++$i) { + $xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV); + $plaintext.= $ciphertext[$i] ^ $xor; + $iv = substr($iv, 1) . $xor[0]; + } + + if ($this->continuousBuffer) { + $this->decryptIV = $iv; + } + break; + case self::MODE_OFB: + $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer); + } + + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + } + + if ($this->engine === self::ENGINE_MCRYPT) { + set_error_handler(array($this, 'do_nothing')); + $block_size = $this->block_size; + if ($this->changed) { + $this->_setupMcrypt(); + $this->changed = false; + } + if ($this->dechanged) { + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + $this->dechanged = false; + } + + if ($this->mode == self::MODE_CFB && $this->continuousBuffer) { + $iv = &$this->decryptIV; + $pos = &$this->debuffer['pos']; + $len = strlen($ciphertext); + $plaintext = ''; + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $plaintext = substr($iv, $orig_pos) ^ $ciphertext; + $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); + } + if ($len >= $block_size) { + $cb = substr($ciphertext, $i, $len - $len % $block_size); + $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; + $iv = substr($cb, -$block_size); + $len%= $block_size; + } + if ($len) { + $iv = mcrypt_generic($this->ecb, $iv); + $plaintext.= $iv ^ substr($ciphertext, -$len); + $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len); + $pos = $len; + } + + restore_error_handler(); + + return $plaintext; + } + + $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); + + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + } + + restore_error_handler(); + + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + } + + if ($this->changed) { + $this->_setup(); + $this->changed = false; + } + if ($this->use_inline_crypt) { + $inline = $this->inline_crypt; + return $inline('decrypt', $this, $ciphertext); + } + + $block_size = $this->block_size; + + $buffer = &$this->debuffer; + $plaintext = ''; + switch ($this->mode) { + case self::MODE_ECB: + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size)); + } + break; + case self::MODE_CBC: + $xor = $this->decryptIV; + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $block = substr($ciphertext, $i, $block_size); + $plaintext.= $this->_decryptBlock($block) ^ $xor; + $xor = $block; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + } + break; + case self::MODE_CTR: + $xor = $this->decryptIV; + if (strlen($buffer['ciphertext'])) { + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $block = substr($ciphertext, $i, $block_size); + if (strlen($block) > strlen($buffer['ciphertext'])) { + $buffer['ciphertext'].= $this->_encryptBlock($xor); + $this->_increment_str($xor); + } + $key = $this->_string_shift($buffer['ciphertext'], $block_size); + $plaintext.= $block ^ $key; + } + } else { + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $block = substr($ciphertext, $i, $block_size); + $key = $this->_encryptBlock($xor); + $this->_increment_str($xor); + $plaintext.= $block ^ $key; + } + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + if ($start = strlen($ciphertext) % $block_size) { + $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; + } + } + break; + case self::MODE_CFB: + if ($this->continuousBuffer) { + $iv = &$this->decryptIV; + $pos = &$buffer['pos']; + } else { + $iv = $this->decryptIV; + $pos = 0; + } + $len = strlen($ciphertext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $plaintext = substr($iv, $orig_pos) ^ $ciphertext; + $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); + } + while ($len >= $block_size) { + $iv = $this->_encryptBlock($iv); + $cb = substr($ciphertext, $i, $block_size); + $plaintext.= $iv ^ $cb; + $iv = $cb; + $len-= $block_size; + $i+= $block_size; + } + if ($len) { + $iv = $this->_encryptBlock($iv); + $plaintext.= $iv ^ substr($ciphertext, $i); + $iv = substr_replace($iv, substr($ciphertext, $i), 0, $len); + $pos = $len; + } + break; + case self::MODE_CFB8: + $plaintext = ''; + $len = strlen($ciphertext); + $iv = $this->decryptIV; + + for ($i = 0; $i < $len; ++$i) { + $plaintext.= $ciphertext[$i] ^ $this->_encryptBlock($iv); + $iv = substr($iv, 1) . $ciphertext[$i]; + } + + if ($this->continuousBuffer) { + if ($len >= $block_size) { + $this->decryptIV = substr($ciphertext, -$block_size); + } else { + $this->decryptIV = substr($this->decryptIV, $len - $block_size) . substr($ciphertext, -$len); + } + } + break; + case self::MODE_OFB8: + $plaintext = ''; + $len = strlen($ciphertext); + $iv = $this->decryptIV; + + for ($i = 0; $i < $len; ++$i) { + $xor = $this->_encryptBlock($iv); + $plaintext.= $ciphertext[$i] ^ $xor; + $iv = substr($iv, 1) . $xor[0]; + } + + if ($this->continuousBuffer) { + $this->decryptIV = $iv; + } + break; + case self::MODE_OFB: + $xor = $this->decryptIV; + if (strlen($buffer['xor'])) { + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $block = substr($ciphertext, $i, $block_size); + if (strlen($block) > strlen($buffer['xor'])) { + $xor = $this->_encryptBlock($xor); + $buffer['xor'].= $xor; + } + $key = $this->_string_shift($buffer['xor'], $block_size); + $plaintext.= $block ^ $key; + } + } else { + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $xor = $this->_encryptBlock($xor); + $plaintext.= substr($ciphertext, $i, $block_size) ^ $xor; + } + $key = $xor; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + if ($start = strlen($ciphertext) % $block_size) { + $buffer['xor'] = substr($key, $start) . $buffer['xor']; + } + } + break; + case self::MODE_STREAM: + $plaintext = $this->_decryptBlock($ciphertext); + break; + } + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + } + + /** + * OpenSSL CTR Processor + * + * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream + * for CTR is the same for both encrypting and decrypting this function is re-used by both Base::encrypt() + * and Base::decrypt(). Also, OpenSSL doesn't implement CTR for all of it's symmetric ciphers so this + * function will emulate CTR with ECB when necessary. + * + * @see self::encrypt() + * @see self::decrypt() + * @param string $plaintext + * @param string $encryptIV + * @param array $buffer + * @return string + * @access private + */ + function _openssl_ctr_process($plaintext, &$encryptIV, &$buffer) + { + $ciphertext = ''; + + $block_size = $this->block_size; + $key = $this->key; + + if ($this->openssl_emulate_ctr) { + $xor = $encryptIV; + if (strlen($buffer['ciphertext'])) { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + if (strlen($block) > strlen($buffer['ciphertext'])) { + $result = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + $result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result; + $buffer['ciphertext'].= $result; + } + $this->_increment_str($xor); + $otp = $this->_string_shift($buffer['ciphertext'], $block_size); + $ciphertext.= $block ^ $otp; + } + } else { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + $otp = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + $otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp; + $this->_increment_str($xor); + $ciphertext.= $block ^ $otp; + } + } + if ($this->continuousBuffer) { + $encryptIV = $xor; + if ($start = strlen($plaintext) % $block_size) { + $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; + } + } + + return $ciphertext; + } + + if (strlen($buffer['ciphertext'])) { + $ciphertext = $plaintext ^ $this->_string_shift($buffer['ciphertext'], strlen($plaintext)); + $plaintext = substr($plaintext, strlen($ciphertext)); + + if (!strlen($plaintext)) { + return $ciphertext; + } + } + + $overflow = strlen($plaintext) % $block_size; + if ($overflow) { + $plaintext2 = $this->_string_pop($plaintext, $overflow); // ie. trim $plaintext to a multiple of $block_size and put rest of $plaintext in $plaintext2 + $encrypted = openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); + $temp = $this->_string_pop($encrypted, $block_size); + $ciphertext.= $encrypted . ($plaintext2 ^ $temp); + if ($this->continuousBuffer) { + $buffer['ciphertext'] = substr($temp, $overflow); + $encryptIV = $temp; + } + } elseif (!strlen($buffer['ciphertext'])) { + $ciphertext.= openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); + $temp = $this->_string_pop($ciphertext, $block_size); + if ($this->continuousBuffer) { + $encryptIV = $temp; + } + } + if ($this->continuousBuffer) { + if (!defined('OPENSSL_RAW_DATA')) { + $encryptIV.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + } + $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + if ($overflow) { + $this->_increment_str($encryptIV); + } + } + + return $ciphertext; + } + + /** + * OpenSSL OFB Processor + * + * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream + * for OFB is the same for both encrypting and decrypting this function is re-used by both Base::encrypt() + * and Base::decrypt(). + * + * @see self::encrypt() + * @see self::decrypt() + * @param string $plaintext + * @param string $encryptIV + * @param array $buffer + * @return string + * @access private + */ + function _openssl_ofb_process($plaintext, &$encryptIV, &$buffer) + { + if (strlen($buffer['xor'])) { + $ciphertext = $plaintext ^ $buffer['xor']; + $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext)); + $plaintext = substr($plaintext, strlen($ciphertext)); + } else { + $ciphertext = ''; + } + + $block_size = $this->block_size; + + $len = strlen($plaintext); + $key = $this->key; + $overflow = $len % $block_size; + + if (strlen($plaintext)) { + if ($overflow) { + $ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); + $xor = $this->_string_pop($ciphertext, $block_size); + if ($this->continuousBuffer) { + $encryptIV = $xor; + } + $ciphertext.= $this->_string_shift($xor, $overflow) ^ substr($plaintext, -$overflow); + if ($this->continuousBuffer) { + $buffer['xor'] = $xor; + } + } else { + $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); + if ($this->continuousBuffer) { + $encryptIV = substr($ciphertext, -$block_size) ^ substr($plaintext, -$block_size); + } + } + } + + return $ciphertext; + } + + /** + * phpseclib <-> OpenSSL Mode Mapper + * + * May need to be overwritten by classes extending this one in some cases + * + * @return int + * @access private + */ + function _openssl_translate_mode() + { + switch ($this->mode) { + case self::MODE_ECB: + return 'ecb'; + case self::MODE_CBC: + return 'cbc'; + case self::MODE_CTR: + return 'ctr'; + case self::MODE_CFB: + return 'cfb'; + case self::MODE_CFB8: + return 'cfb8'; + case self::MODE_OFB: + return 'ofb'; + } + } + + /** + * Pad "packets". + * + * Block ciphers working by encrypting between their specified [$this->]block_size at a time + * If you ever need to encrypt or decrypt something that isn't of the proper length, it becomes necessary to + * pad the input so that it is of the proper length. + * + * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH, + * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping + * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is + * transmitted separately) + * + * @see self::disablePadding() + * @access public + */ + function enablePadding() + { + $this->padding = true; + } + + /** + * Do not pad packets. + * + * @see self::enablePadding() + * @access public + */ + function disablePadding() + { + $this->padding = false; + } + + /** + * Treat consecutive "packets" as if they are a continuous buffer. + * + * Say you have a 32-byte plaintext $plaintext. Using the default behavior, the two following code snippets + * will yield different outputs: + * + * + * echo $rijndael->encrypt(substr($plaintext, 0, 16)); + * echo $rijndael->encrypt(substr($plaintext, 16, 16)); + * + * + * echo $rijndael->encrypt($plaintext); + * + * + * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates + * another, as demonstrated with the following: + * + * + * $rijndael->encrypt(substr($plaintext, 0, 16)); + * echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16))); + * + * + * echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16))); + * + * + * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different + * outputs. The reason is due to the fact that the initialization vector's change after every encryption / + * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. + * + * Put another way, when the continuous buffer is enabled, the state of the \phpseclib\Crypt\*() object changes after each + * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that + * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), + * however, they are also less intuitive and more likely to cause you problems. + * + * @see self::disableContinuousBuffer() + * @access public + * @internal Could, but not must, extend by the child Crypt_* class + */ + function enableContinuousBuffer() + { + if ($this->mode == self::MODE_ECB) { + return; + } + + $this->continuousBuffer = true; + + $this->_setEngine(); + } + + /** + * Treat consecutive packets as if they are a discontinuous buffer. + * + * The default behavior. + * + * @see self::enableContinuousBuffer() + * @access public + * @internal Could, but not must, extend by the child Crypt_* class + */ + function disableContinuousBuffer() + { + if ($this->mode == self::MODE_ECB) { + return; + } + if (!$this->continuousBuffer) { + return; + } + + $this->continuousBuffer = false; + $this->changed = true; + + $this->_setEngine(); + } + + /** + * Test for engine validity + * + * @see self::__construct() + * @param int $engine + * @access public + * @return bool + */ + function isValidEngine($engine) + { + switch ($engine) { + case self::ENGINE_OPENSSL: + if ($this->mode == self::MODE_STREAM && $this->continuousBuffer) { + return false; + } + $this->openssl_emulate_ctr = false; + $result = $this->cipher_name_openssl && + extension_loaded('openssl') && + // PHP 5.3.0 - 5.3.2 did not let you set IV's + version_compare(PHP_VERSION, '5.3.3', '>='); + if (!$result) { + return false; + } + + // prior to PHP 5.4.0 OPENSSL_RAW_DATA and OPENSSL_ZERO_PADDING were not defined. instead of expecting an integer + // $options openssl_encrypt expected a boolean $raw_data. + if (!defined('OPENSSL_RAW_DATA')) { + $this->openssl_options = true; + } else { + $this->openssl_options = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING; + } + + $methods = openssl_get_cipher_methods(); + if (in_array($this->cipher_name_openssl, $methods)) { + return true; + } + // not all of openssl's symmetric cipher's support ctr. for those + // that don't we'll emulate it + switch ($this->mode) { + case self::MODE_CTR: + if (in_array($this->cipher_name_openssl_ecb, $methods)) { + $this->openssl_emulate_ctr = true; + return true; + } + } + return false; + case self::ENGINE_MCRYPT: + set_error_handler(array($this, 'do_nothing')); + $result = $this->cipher_name_mcrypt && + extension_loaded('mcrypt') && + in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms()); + restore_error_handler(); + return $result; + case self::ENGINE_INTERNAL: + return true; + } + + return false; + } + + /** + * Sets the preferred crypt engine + * + * Currently, $engine could be: + * + * - \phpseclib\Crypt\Base::ENGINE_OPENSSL [very fast] + * + * - \phpseclib\Crypt\Base::ENGINE_MCRYPT [fast] + * + * - \phpseclib\Crypt\Base::ENGINE_INTERNAL [slow] + * + * If the preferred crypt engine is not available the fastest available one will be used + * + * @see self::__construct() + * @param int $engine + * @access public + */ + function setPreferredEngine($engine) + { + switch ($engine) { + //case self::ENGINE_OPENSSL; + case self::ENGINE_MCRYPT: + case self::ENGINE_INTERNAL: + $this->preferredEngine = $engine; + break; + default: + $this->preferredEngine = self::ENGINE_OPENSSL; + } + + $this->_setEngine(); + } + + /** + * Returns the engine currently being utilized + * + * @see self::_setEngine() + * @access public + */ + function getEngine() + { + return $this->engine; + } + + /** + * Sets the engine as appropriate + * + * @see self::__construct() + * @access private + */ + function _setEngine() + { + $this->engine = null; + + $candidateEngines = array( + $this->preferredEngine, + self::ENGINE_OPENSSL, + self::ENGINE_MCRYPT + ); + foreach ($candidateEngines as $engine) { + if ($this->isValidEngine($engine)) { + $this->engine = $engine; + break; + } + } + if (!$this->engine) { + $this->engine = self::ENGINE_INTERNAL; + } + + if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) { + set_error_handler(array($this, 'do_nothing')); + // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed, + // (re)open them with the module named in $this->cipher_name_mcrypt + mcrypt_module_close($this->enmcrypt); + mcrypt_module_close($this->demcrypt); + $this->enmcrypt = null; + $this->demcrypt = null; + + if ($this->ecb) { + mcrypt_module_close($this->ecb); + $this->ecb = null; + } + restore_error_handler(); + } + + $this->changed = true; + } + + /** + * Encrypts a block + * + * Note: Must be extended by the child \phpseclib\Crypt\* class + * + * @access private + * @param string $in + * @return string + */ + abstract function _encryptBlock($in); + + /** + * Decrypts a block + * + * Note: Must be extended by the child \phpseclib\Crypt\* class + * + * @access private + * @param string $in + * @return string + */ + abstract function _decryptBlock($in); + + /** + * Setup the key (expansion) + * + * Only used if $engine == self::ENGINE_INTERNAL + * + * Note: Must extend by the child \phpseclib\Crypt\* class + * + * @see self::_setup() + * @access private + */ + abstract function _setupKey(); + + /** + * Setup the self::ENGINE_INTERNAL $engine + * + * (re)init, if necessary, the internal cipher $engine and flush all $buffers + * Used (only) if $engine == self::ENGINE_INTERNAL + * + * _setup() will be called each time if $changed === true + * typically this happens when using one or more of following public methods: + * + * - setKey() + * + * - setIV() + * + * - disableContinuousBuffer() + * + * - First run of encrypt() / decrypt() with no init-settings + * + * @see self::setKey() + * @see self::setIV() + * @see self::disableContinuousBuffer() + * @access private + * @internal _setup() is always called before en/decryption. + * @internal Could, but not must, extend by the child Crypt_* class + */ + function _setup() + { + $this->_clearBuffers(); + $this->_setupKey(); + + if ($this->use_inline_crypt) { + $this->_setupInlineCrypt(); + } + } + + /** + * Setup the self::ENGINE_MCRYPT $engine + * + * (re)init, if necessary, the (ext)mcrypt resources and flush all $buffers + * Used (only) if $engine = self::ENGINE_MCRYPT + * + * _setupMcrypt() will be called each time if $changed === true + * typically this happens when using one or more of following public methods: + * + * - setKey() + * + * - setIV() + * + * - disableContinuousBuffer() + * + * - First run of encrypt() / decrypt() + * + * @see self::setKey() + * @see self::setIV() + * @see self::disableContinuousBuffer() + * @access private + * @internal Could, but not must, extend by the child Crypt_* class + */ + function _setupMcrypt() + { + $this->_clearBuffers(); + $this->enchanged = $this->dechanged = true; + + if (!isset($this->enmcrypt)) { + static $mcrypt_modes = array( + self::MODE_CTR => 'ctr', + self::MODE_ECB => MCRYPT_MODE_ECB, + self::MODE_CBC => MCRYPT_MODE_CBC, + self::MODE_CFB => 'ncfb', + self::MODE_CFB8 => MCRYPT_MODE_CFB, + self::MODE_OFB => MCRYPT_MODE_NOFB, + self::MODE_OFB8 => MCRYPT_MODE_OFB, + self::MODE_STREAM => MCRYPT_MODE_STREAM, + ); + + $this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); + $this->enmcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); + + // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer() + // to workaround mcrypt's broken ncfb implementation in buffered mode + // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} + if ($this->mode == self::MODE_CFB) { + $this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, ''); + } + } // else should mcrypt_generic_deinit be called? + + if ($this->mode == self::MODE_CFB) { + mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); + } + } + + /** + * Pads a string + * + * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize. + * $this->block_size - (strlen($text) % $this->block_size) bytes are added, each of which is equal to + * chr($this->block_size - (strlen($text) % $this->block_size) + * + * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless + * and padding will, hence forth, be enabled. + * + * @see self::_unpad() + * @param string $text + * @access private + * @return string + */ + function _pad($text) + { + $length = strlen($text); + + if (!$this->padding) { + if ($length % $this->block_size == 0) { + return $text; + } else { + user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})"); + $this->padding = true; + } + } + + $pad = $this->block_size - ($length % $this->block_size); + + return str_pad($text, $length + $pad, chr($pad)); + } + + /** + * Unpads a string. + * + * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong + * and false will be returned. + * + * @see self::_pad() + * @param string $text + * @access private + * @return string + */ + function _unpad($text) + { + if (!$this->padding) { + return $text; + } + + $length = ord($text[strlen($text) - 1]); + + if (!$length || $length > $this->block_size) { + return false; + } + + return substr($text, 0, -$length); + } + + /** + * Clears internal buffers + * + * Clearing/resetting the internal buffers is done everytime + * after disableContinuousBuffer() or on cipher $engine (re)init + * ie after setKey() or setIV() + * + * @access public + * @internal Could, but not must, extend by the child Crypt_* class + */ + function _clearBuffers() + { + $this->enbuffer = $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); + + // mcrypt's handling of invalid's $iv: + // $this->encryptIV = $this->decryptIV = strlen($this->iv) == $this->block_size ? $this->iv : str_repeat("\0", $this->block_size); + $this->encryptIV = $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0"); + + if (!$this->skip_key_adjustment) { + $this->key = str_pad(substr($this->key, 0, $this->key_length), $this->key_length, "\0"); + } + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @access private + * @return string + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * String Pop + * + * Inspired by array_pop + * + * @param string $string + * @param int $index + * @access private + * @return string + */ + function _string_pop(&$string, $index = 1) + { + $substr = substr($string, -$index); + $string = substr($string, 0, -$index); + return $substr; + } + + /** + * Increment the current string + * + * @see self::decrypt() + * @see self::encrypt() + * @param string $var + * @access private + */ + function _increment_str(&$var) + { + if (function_exists('sodium_increment')) { + $var = strrev($var); + sodium_increment($var); + $var = strrev($var); + return; + } + + for ($i = 4; $i <= strlen($var); $i+= 4) { + $temp = substr($var, -$i, 4); + switch ($temp) { + case "\xFF\xFF\xFF\xFF": + $var = substr_replace($var, "\x00\x00\x00\x00", -$i, 4); + break; + case "\x7F\xFF\xFF\xFF": + $var = substr_replace($var, "\x80\x00\x00\x00", -$i, 4); + return; + default: + $temp = unpack('Nnum', $temp); + $var = substr_replace($var, pack('N', $temp['num'] + 1), -$i, 4); + return; + } + } + + $remainder = strlen($var) % 4; + + if ($remainder == 0) { + return; + } + + $temp = unpack('Nnum', str_pad(substr($var, 0, $remainder), 4, "\0", STR_PAD_LEFT)); + $temp = substr(pack('N', $temp['num'] + 1), -$remainder); + $var = substr_replace($var, $temp, 0, $remainder); + } + + /** + * Setup the performance-optimized function for de/encrypt() + * + * Stores the created (or existing) callback function-name + * in $this->inline_crypt + * + * Internally for phpseclib developers: + * + * _setupInlineCrypt() would be called only if: + * + * - $engine == self::ENGINE_INTERNAL and + * + * - $use_inline_crypt === true + * + * - each time on _setup(), after(!) _setupKey() + * + * + * This ensures that _setupInlineCrypt() has always a + * full ready2go initializated internal cipher $engine state + * where, for example, the keys allready expanded, + * keys/block_size calculated and such. + * + * It is, each time if called, the responsibility of _setupInlineCrypt(): + * + * - to set $this->inline_crypt to a valid and fully working callback function + * as a (faster) replacement for encrypt() / decrypt() + * + * - NOT to create unlimited callback functions (for memory reasons!) + * no matter how often _setupInlineCrypt() would be called. At some + * point of amount they must be generic re-useable. + * + * - the code of _setupInlineCrypt() it self, + * and the generated callback code, + * must be, in following order: + * - 100% safe + * - 100% compatible to encrypt()/decrypt() + * - using only php5+ features/lang-constructs/php-extensions if + * compatibility (down to php4) or fallback is provided + * - readable/maintainable/understandable/commented and... not-cryptic-styled-code :-) + * - >= 10% faster than encrypt()/decrypt() [which is, by the way, + * the reason for the existence of _setupInlineCrypt() :-)] + * - memory-nice + * - short (as good as possible) + * + * Note: - _setupInlineCrypt() is using _createInlineCryptFunction() to create the full callback function code. + * - In case of using inline crypting, _setupInlineCrypt() must extend by the child \phpseclib\Crypt\* class. + * - The following variable names are reserved: + * - $_* (all variable names prefixed with an underscore) + * - $self (object reference to it self. Do not use $this, but $self instead) + * - $in (the content of $in has to en/decrypt by the generated code) + * - The callback function should not use the 'return' statement, but en/decrypt'ing the content of $in only + * + * + * @see self::_setup() + * @see self::_createInlineCryptFunction() + * @see self::encrypt() + * @see self::decrypt() + * @access private + * @internal If a Crypt_* class providing inline crypting it must extend _setupInlineCrypt() + */ + function _setupInlineCrypt() + { + // If, for any reason, an extending \phpseclib\Crypt\Base() \phpseclib\Crypt\* class + // not using inline crypting then it must be ensured that: $this->use_inline_crypt = false + // ie in the class var declaration of $use_inline_crypt in general for the \phpseclib\Crypt\* class, + // in the constructor at object instance-time + // or, if it's runtime-specific, at runtime + + $this->use_inline_crypt = false; + } + + /** + * Creates the performance-optimized function for en/decrypt() + * + * Internally for phpseclib developers: + * + * _createInlineCryptFunction(): + * + * - merge the $cipher_code [setup'ed by _setupInlineCrypt()] + * with the current [$this->]mode of operation code + * + * - create the $inline function, which called by encrypt() / decrypt() + * as its replacement to speed up the en/decryption operations. + * + * - return the name of the created $inline callback function + * + * - used to speed up en/decryption + * + * + * + * The main reason why can speed up things [up to 50%] this way are: + * + * - using variables more effective then regular. + * (ie no use of expensive arrays but integers $k_0, $k_1 ... + * or even, for example, the pure $key[] values hardcoded) + * + * - avoiding 1000's of function calls of ie _encryptBlock() + * but inlining the crypt operations. + * in the mode of operation for() loop. + * + * - full loop unroll the (sometimes key-dependent) rounds + * avoiding this way ++$i counters and runtime-if's etc... + * + * The basic code architectur of the generated $inline en/decrypt() + * lambda function, in pseudo php, is: + * + * + * +----------------------------------------------------------------------------------------------+ + * | callback $inline = create_function: | + * | lambda_function_0001_crypt_ECB($action, $text) | + * | { | + * | INSERT PHP CODE OF: | + * | $cipher_code['init_crypt']; // general init code. | + * | // ie: $sbox'es declarations used for | + * | // encrypt and decrypt'ing. | + * | | + * | switch ($action) { | + * | case 'encrypt': | + * | INSERT PHP CODE OF: | + * | $cipher_code['init_encrypt']; // encrypt sepcific init code. | + * | ie: specified $key or $box | + * | declarations for encrypt'ing. | + * | | + * | foreach ($ciphertext) { | + * | $in = $block_size of $ciphertext; | + * | | + * | INSERT PHP CODE OF: | + * | $cipher_code['encrypt_block']; // encrypt's (string) $in, which is always: | + * | // strlen($in) == $this->block_size | + * | // here comes the cipher algorithm in action | + * | // for encryption. | + * | // $cipher_code['encrypt_block'] has to | + * | // encrypt the content of the $in variable | + * | | + * | $plaintext .= $in; | + * | } | + * | return $plaintext; | + * | | + * | case 'decrypt': | + * | INSERT PHP CODE OF: | + * | $cipher_code['init_decrypt']; // decrypt sepcific init code | + * | ie: specified $key or $box | + * | declarations for decrypt'ing. | + * | foreach ($plaintext) { | + * | $in = $block_size of $plaintext; | + * | | + * | INSERT PHP CODE OF: | + * | $cipher_code['decrypt_block']; // decrypt's (string) $in, which is always | + * | // strlen($in) == $this->block_size | + * | // here comes the cipher algorithm in action | + * | // for decryption. | + * | // $cipher_code['decrypt_block'] has to | + * | // decrypt the content of the $in variable | + * | $ciphertext .= $in; | + * | } | + * | return $ciphertext; | + * | } | + * | } | + * +----------------------------------------------------------------------------------------------+ + * + * + * See also the \phpseclib\Crypt\*::_setupInlineCrypt()'s for + * productive inline $cipher_code's how they works. + * + * Structure of: + * + * $cipher_code = array( + * 'init_crypt' => (string) '', // optional + * 'init_encrypt' => (string) '', // optional + * 'init_decrypt' => (string) '', // optional + * 'encrypt_block' => (string) '', // required + * 'decrypt_block' => (string) '' // required + * ); + * + * + * @see self::_setupInlineCrypt() + * @see self::encrypt() + * @see self::decrypt() + * @param array $cipher_code + * @access private + * @return string (the name of the created callback function) + */ + function _createInlineCryptFunction($cipher_code) + { + $block_size = $this->block_size; + + // optional + $init_crypt = isset($cipher_code['init_crypt']) ? $cipher_code['init_crypt'] : ''; + $init_encrypt = isset($cipher_code['init_encrypt']) ? $cipher_code['init_encrypt'] : ''; + $init_decrypt = isset($cipher_code['init_decrypt']) ? $cipher_code['init_decrypt'] : ''; + // required + $encrypt_block = $cipher_code['encrypt_block']; + $decrypt_block = $cipher_code['decrypt_block']; + + // Generating mode of operation inline code, + // merged with the $cipher_code algorithm + // for encrypt- and decryption. + switch ($this->mode) { + case self::MODE_ECB: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_plaintext_len = strlen($_text); + + for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { + $in = substr($_text, $_i, '.$block_size.'); + '.$encrypt_block.' + $_ciphertext.= $in; + } + + return $_ciphertext; + '; + + $decrypt = $init_decrypt . ' + $_plaintext = ""; + $_text = str_pad($_text, strlen($_text) + ('.$block_size.' - strlen($_text) % '.$block_size.') % '.$block_size.', chr(0)); + $_ciphertext_len = strlen($_text); + + for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { + $in = substr($_text, $_i, '.$block_size.'); + '.$decrypt_block.' + $_plaintext.= $in; + } + + return $self->_unpad($_plaintext); + '; + break; + case self::MODE_CTR: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_plaintext_len = strlen($_text); + $_xor = $self->encryptIV; + $_buffer = &$self->enbuffer; + if (strlen($_buffer["ciphertext"])) { + for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { + $_block = substr($_text, $_i, '.$block_size.'); + if (strlen($_block) > strlen($_buffer["ciphertext"])) { + $in = $_xor; + '.$encrypt_block.' + $self->_increment_str($_xor); + $_buffer["ciphertext"].= $in; + } + $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.'); + $_ciphertext.= $_block ^ $_key; + } + } else { + for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { + $_block = substr($_text, $_i, '.$block_size.'); + $in = $_xor; + '.$encrypt_block.' + $self->_increment_str($_xor); + $_key = $in; + $_ciphertext.= $_block ^ $_key; + } + } + if ($self->continuousBuffer) { + $self->encryptIV = $_xor; + if ($_start = $_plaintext_len % '.$block_size.') { + $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"]; + } + } + + return $_ciphertext; + '; + + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_ciphertext_len = strlen($_text); + $_xor = $self->decryptIV; + $_buffer = &$self->debuffer; + + if (strlen($_buffer["ciphertext"])) { + for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { + $_block = substr($_text, $_i, '.$block_size.'); + if (strlen($_block) > strlen($_buffer["ciphertext"])) { + $in = $_xor; + '.$encrypt_block.' + $self->_increment_str($_xor); + $_buffer["ciphertext"].= $in; + } + $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.'); + $_plaintext.= $_block ^ $_key; + } + } else { + for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { + $_block = substr($_text, $_i, '.$block_size.'); + $in = $_xor; + '.$encrypt_block.' + $self->_increment_str($_xor); + $_key = $in; + $_plaintext.= $_block ^ $_key; + } + } + if ($self->continuousBuffer) { + $self->decryptIV = $_xor; + if ($_start = $_ciphertext_len % '.$block_size.') { + $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"]; + } + } + + return $_plaintext; + '; + break; + case self::MODE_CFB: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_buffer = &$self->enbuffer; + + if ($self->continuousBuffer) { + $_iv = &$self->encryptIV; + $_pos = &$_buffer["pos"]; + } else { + $_iv = $self->encryptIV; + $_pos = 0; + } + $_len = strlen($_text); + $_i = 0; + if ($_pos) { + $_orig_pos = $_pos; + $_max = '.$block_size.' - $_pos; + if ($_len >= $_max) { + $_i = $_max; + $_len-= $_max; + $_pos = 0; + } else { + $_i = $_len; + $_pos+= $_len; + $_len = 0; + } + $_ciphertext = substr($_iv, $_orig_pos) ^ $_text; + $_iv = substr_replace($_iv, $_ciphertext, $_orig_pos, $_i); + } + while ($_len >= '.$block_size.') { + $in = $_iv; + '.$encrypt_block.'; + $_iv = $in ^ substr($_text, $_i, '.$block_size.'); + $_ciphertext.= $_iv; + $_len-= '.$block_size.'; + $_i+= '.$block_size.'; + } + if ($_len) { + $in = $_iv; + '.$encrypt_block.' + $_iv = $in; + $_block = $_iv ^ substr($_text, $_i); + $_iv = substr_replace($_iv, $_block, 0, $_len); + $_ciphertext.= $_block; + $_pos = $_len; + } + return $_ciphertext; + '; + + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_buffer = &$self->debuffer; + + if ($self->continuousBuffer) { + $_iv = &$self->decryptIV; + $_pos = &$_buffer["pos"]; + } else { + $_iv = $self->decryptIV; + $_pos = 0; + } + $_len = strlen($_text); + $_i = 0; + if ($_pos) { + $_orig_pos = $_pos; + $_max = '.$block_size.' - $_pos; + if ($_len >= $_max) { + $_i = $_max; + $_len-= $_max; + $_pos = 0; + } else { + $_i = $_len; + $_pos+= $_len; + $_len = 0; + } + $_plaintext = substr($_iv, $_orig_pos) ^ $_text; + $_iv = substr_replace($_iv, substr($_text, 0, $_i), $_orig_pos, $_i); + } + while ($_len >= '.$block_size.') { + $in = $_iv; + '.$encrypt_block.' + $_iv = $in; + $cb = substr($_text, $_i, '.$block_size.'); + $_plaintext.= $_iv ^ $cb; + $_iv = $cb; + $_len-= '.$block_size.'; + $_i+= '.$block_size.'; + } + if ($_len) { + $in = $_iv; + '.$encrypt_block.' + $_iv = $in; + $_plaintext.= $_iv ^ substr($_text, $_i); + $_iv = substr_replace($_iv, substr($_text, $_i), 0, $_len); + $_pos = $_len; + } + + return $_plaintext; + '; + break; + case self::MODE_CFB8: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_len = strlen($_text); + $_iv = $self->encryptIV; + + for ($_i = 0; $_i < $_len; ++$_i) { + $in = $_iv; + '.$encrypt_block.' + $_ciphertext.= ($_c = $_text[$_i] ^ $in); + $_iv = substr($_iv, 1) . $_c; + } + + if ($self->continuousBuffer) { + if ($_len >= '.$block_size.') { + $self->encryptIV = substr($_ciphertext, -'.$block_size.'); + } else { + $self->encryptIV = substr($self->encryptIV, $_len - '.$block_size.') . substr($_ciphertext, -$_len); + } + } + + return $_ciphertext; + '; + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_len = strlen($_text); + $_iv = $self->decryptIV; + + for ($_i = 0; $_i < $_len; ++$_i) { + $in = $_iv; + '.$encrypt_block.' + $_plaintext.= $_text[$_i] ^ $in; + $_iv = substr($_iv, 1) . $_text[$_i]; + } + + if ($self->continuousBuffer) { + if ($_len >= '.$block_size.') { + $self->decryptIV = substr($_text, -'.$block_size.'); + } else { + $self->decryptIV = substr($self->decryptIV, $_len - '.$block_size.') . substr($_text, -$_len); + } + } + + return $_plaintext; + '; + break; + case self::MODE_OFB8: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_len = strlen($_text); + $_iv = $self->encryptIV; + + for ($_i = 0; $_i < $_len; ++$_i) { + $in = $_iv; + '.$encrypt_block.' + $_ciphertext.= $_text[$_i] ^ $in; + $_iv = substr($_iv, 1) . $in[0]; + } + + if ($self->continuousBuffer) { + $self->encryptIV = $_iv; + } + + return $_ciphertext; + '; + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_len = strlen($_text); + $_iv = $self->decryptIV; + + for ($_i = 0; $_i < $_len; ++$_i) { + $in = $_iv; + '.$encrypt_block.' + $_plaintext.= $_text[$_i] ^ $in; + $_iv = substr($_iv, 1) . $in[0]; + } + + if ($self->continuousBuffer) { + $self->decryptIV = $_iv; + } + + return $_plaintext; + '; + break; + case self::MODE_OFB: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_plaintext_len = strlen($_text); + $_xor = $self->encryptIV; + $_buffer = &$self->enbuffer; + + if (strlen($_buffer["xor"])) { + for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { + $_block = substr($_text, $_i, '.$block_size.'); + if (strlen($_block) > strlen($_buffer["xor"])) { + $in = $_xor; + '.$encrypt_block.' + $_xor = $in; + $_buffer["xor"].= $_xor; + } + $_key = $self->_string_shift($_buffer["xor"], '.$block_size.'); + $_ciphertext.= $_block ^ $_key; + } + } else { + for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { + $in = $_xor; + '.$encrypt_block.' + $_xor = $in; + $_ciphertext.= substr($_text, $_i, '.$block_size.') ^ $_xor; + } + $_key = $_xor; + } + if ($self->continuousBuffer) { + $self->encryptIV = $_xor; + if ($_start = $_plaintext_len % '.$block_size.') { + $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"]; + } + } + return $_ciphertext; + '; + + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_ciphertext_len = strlen($_text); + $_xor = $self->decryptIV; + $_buffer = &$self->debuffer; + + if (strlen($_buffer["xor"])) { + for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { + $_block = substr($_text, $_i, '.$block_size.'); + if (strlen($_block) > strlen($_buffer["xor"])) { + $in = $_xor; + '.$encrypt_block.' + $_xor = $in; + $_buffer["xor"].= $_xor; + } + $_key = $self->_string_shift($_buffer["xor"], '.$block_size.'); + $_plaintext.= $_block ^ $_key; + } + } else { + for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { + $in = $_xor; + '.$encrypt_block.' + $_xor = $in; + $_plaintext.= substr($_text, $_i, '.$block_size.') ^ $_xor; + } + $_key = $_xor; + } + if ($self->continuousBuffer) { + $self->decryptIV = $_xor; + if ($_start = $_ciphertext_len % '.$block_size.') { + $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"]; + } + } + return $_plaintext; + '; + break; + case self::MODE_STREAM: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + '.$encrypt_block.' + return $_ciphertext; + '; + $decrypt = $init_decrypt . ' + $_plaintext = ""; + '.$decrypt_block.' + return $_plaintext; + '; + break; + // case self::MODE_CBC: + default: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_plaintext_len = strlen($_text); + + $in = $self->encryptIV; + + for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { + $in = substr($_text, $_i, '.$block_size.') ^ $in; + '.$encrypt_block.' + $_ciphertext.= $in; + } + + if ($self->continuousBuffer) { + $self->encryptIV = $in; + } + + return $_ciphertext; + '; + + $decrypt = $init_decrypt . ' + $_plaintext = ""; + $_text = str_pad($_text, strlen($_text) + ('.$block_size.' - strlen($_text) % '.$block_size.') % '.$block_size.', chr(0)); + $_ciphertext_len = strlen($_text); + + $_iv = $self->decryptIV; + + for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { + $in = $_block = substr($_text, $_i, '.$block_size.'); + '.$decrypt_block.' + $_plaintext.= $in ^ $_iv; + $_iv = $_block; + } + + if ($self->continuousBuffer) { + $self->decryptIV = $_iv; + } + + return $self->_unpad($_plaintext); + '; + break; + } + + // Create the $inline function and return its name as string. Ready to run! + eval('$func = function ($_action, &$self, $_text) { ' . $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' } };'); + return $func; + } + + /** + * Holds the lambda_functions table (classwide) + * + * Each name of the lambda function, created from + * _setupInlineCrypt() && _createInlineCryptFunction() + * is stored, classwide (!), here for reusing. + * + * The string-based index of $function is a classwide + * unique value representing, at least, the $mode of + * operation (or more... depends of the optimizing level) + * for which $mode the lambda function was created. + * + * @access private + * @return array &$functions + */ + function &_getLambdaFunctions() + { + static $functions = array(); + return $functions; + } + + /** + * Generates a digest from $bytes + * + * @see self::_setupInlineCrypt() + * @access private + * @param string $bytes + * @return string + */ + function _hashInlineCryptFunction($bytes) + { + if (!isset(self::$WHIRLPOOL_AVAILABLE)) { + self::$WHIRLPOOL_AVAILABLE = extension_loaded('hash') && in_array('whirlpool', hash_algos()); + } + + $result = ''; + $hash = $bytes; + + switch (true) { + case self::$WHIRLPOOL_AVAILABLE: + foreach (str_split($bytes, 64) as $t) { + $hash = hash('whirlpool', $hash, true); + $result .= $t ^ $hash; + } + return $result . hash('whirlpool', $hash, true); + default: + $len = strlen($bytes); + for ($i = 0; $i < $len; $i+=20) { + $t = substr($bytes, $i, 20); + $hash = pack('H*', sha1($hash)); + $result .= $t ^ $hash; + } + return $result . pack('H*', sha1($hash)); + } + } + + /** + * Convert float to int + * + * On ARM CPUs converting floats to ints doesn't always work + * + * @access private + * @param string $x + * @return int + */ + function safe_intval($x) + { + if (is_int($x)) { + return $x; + } + return (fmod($x, 0x80000000) & 0x7FFFFFFF) | + ((fmod(floor($x / 0x80000000), 2) & 1) << 31); + } + + /** + * eval()'able string for in-line float to int + * + * @access private + * @return string + */ + function safe_intval_inline() + { + if (CRYPT_BASE_USE_REG_INTVAL) { + return PHP_INT_SIZE == 4 ? 'intval(%s)' : '%s'; + } + + $safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | '; + return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))'; + } + + /** + * Dummy error handler to suppress mcrypt errors + * + * @access private + */ + function do_nothing() + { + } + + /** + * Is the continuous buffer enabled? + * + * @access public + * @return boolean + */ + function continuousBufferEnabled() + { + return $this->continuousBuffer; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php new file mode 100644 index 0000000..4e3414d --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php @@ -0,0 +1,992 @@ + unpack('N*', $x), $blocks); it jumps up by an additional + * ~90MB, yielding a 106x increase in memory usage. Consequently, it bcrypt calls a different + * _encryptBlock() then the regular Blowfish does. That said, the Blowfish _encryptBlock() is + * basically just a thin wrapper around the bcrypt _encryptBlock(), so there's that. + * + * This explains 3 of the 4 _encryptBlock() implementations. the last _encryptBlock() + * implementation can best be understood by doing Ctrl + F and searching for where + * CRYPT_BASE_USE_REG_INTVAL is defined. + * + * # phpseclib's three different _setupKey() implementations + * + * Every bcrypt round is the equivalent of encrypting 512KB of data. Since OpenSSH uses 16 + * rounds by default that's ~8MB of data that's essentially being encrypted whenever + * you use bcrypt. That's a lot of data, however, bcrypt operates within tighter constraints + * than regular Blowfish, so we can use that to our advantage. In particular, whereas Blowfish + * supports variable length keys, in bcrypt, the initial "key" is the sha512 hash of the + * password. sha512 hashes are 512 bits or 64 bytes long and thus the bcrypt keys are of a + * fixed length whereas Blowfish keys are not of a fixed length. + * + * bcrypt actually has two different key expansion steps. The first one (expandstate) is + * constantly XOR'ing every _encryptBlock() parameter against the salt prior _encryptBlock()'s + * being called. The second one (expand0state) is more similar to Blowfish's _setupKey() + * but it can still use the fixed length key optimization discussed above and can do away with + * the pack() / unpack() calls. + * + * I suppose _setupKey() could be made to be a thin wrapper around expandstate() but idk it's + * just a lot of work for very marginal benefits as _setupKey() is only called once for + * regular Blowfish vs the 128 times it's called --per round-- with bcrypt. + * + * # blowfish + bcrypt in the same class + * + * Altho there's a lot of Blowfish code that bcrypt doesn't re-use, bcrypt does re-use the + * initial S-boxes, the initial P-array and the int-only _encryptBlock() implementation. + * + * # Credit + * + * phpseclib's bcrypt implementation is based losely off of OpenSSH's implementation: + * + * https://github.com/openssh/openssh-portable/blob/master/openbsd-compat/bcrypt_pbkdf.c + * + * Here's a short example of how to use this library: + * + * setKey('12345678901234567890123456789012'); + * + * $plaintext = str_repeat('a', 1024); + * + * echo $blowfish->decrypt($blowfish->encrypt($plaintext)); + * ?> + * + * + * @category Crypt + * @package Blowfish + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +/** + * Pure-PHP implementation of Blowfish. + * + * @package Blowfish + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @access public + */ +class Blowfish extends Base +{ + /** + * Block Length of the cipher + * + * @see \phpseclib\Crypt\Base::block_size + * @var int + * @access private + */ + var $block_size = 8; + + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib\Crypt\Base::cipher_name_mcrypt + * @var string + * @access private + */ + var $cipher_name_mcrypt = 'blowfish'; + + /** + * Optimizing value while CFB-encrypting + * + * @see \phpseclib\Crypt\Base::cfb_init_len + * @var int + * @access private + */ + var $cfb_init_len = 500; + + /** + * SHA512 Object + * + * @see self::bcrypt_pbkdf + * @var object + * @access private + */ + var $sha512; + + /** + * The fixed subkeys boxes ($sbox0 - $sbox3) with 256 entries each + * + * S-Box 0 + * + * @access private + * @var array + */ + var $sbox0 = array( + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a + ); + + /** + * S-Box 1 + * + * @access private + * @var array + */ + var $sbox1 = array( + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 + ); + + /** + * S-Box 2 + * + * @access private + * @var array + */ + var $sbox2 = array( + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 + ); + + /** + * S-Box 3 + * + * @access private + * @var array + */ + var $sbox3 = array( + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 + ); + + /** + * P-Array consists of 18 32-bit subkeys + * + * @var array + * @access private + */ + var $parray = array( + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, + 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b + ); + + /** + * The BCTX-working Array + * + * Holds the expanded key [p] and the key-depended s-boxes [sb] + * + * @var array + * @access private + */ + var $bctx; + + /** + * Holds the last used key + * + * @var array + * @access private + */ + var $kl; + + /** + * The Key Length (in bytes) + * + * @see \phpseclib\Crypt\Base::setKeyLength() + * @var int + * @access private + * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk + * because the encryption / decryption / key schedule creation requires this number and not $key_length. We could + * derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu + * of that, we'll just precompute it once. + */ + var $key_length = 16; + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. + * + * $mode could be: + * + * - CRYPT_MODE_ECB + * + * - CRYPT_MODE_CBC + * + * - CRYPT_MODE_CTR + * + * - CRYPT_MODE_CFB + * + * - CRYPT_MODE_OFB + * + * (or the alias constants of the chosen cipher, for example for AES: CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC ...) + * + * If not explicitly set, CRYPT_MODE_CBC will be used. + * + * @param int $mode + * @access public + */ + function __construct($mode = self::MODE_CBC) + { + parent::__construct($mode); + + $this->sbox0 = array_map('intval', $this->sbox0); + $this->sbox1 = array_map('intval', $this->sbox1); + $this->sbox2 = array_map('intval', $this->sbox2); + $this->sbox3 = array_map('intval', $this->sbox3); + $this->parray = array_map('intval', $this->parray); + } + + /** + * Sets the key length. + * + * Key lengths can be between 32 and 448 bits. + * + * @access public + * @param int $length + */ + function setKeyLength($length) + { + if ($length < 32) { + $this->key_length = 4; + } elseif ($length > 448) { + $this->key_length = 56; + } else { + $this->key_length = $length >> 3; + } + + parent::setKeyLength($length); + } + + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() + * + * @see \phpseclib\Crypt\Base::isValidEngine() + * @param int $engine + * @access public + * @return bool + */ + function isValidEngine($engine) + { + if ($engine == self::ENGINE_OPENSSL) { + // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1 + // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider" + // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not + if (version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) { + return false; + } + if (version_compare(PHP_VERSION, '5.3.7') < 0 && $this->key_length != 16) { + return false; + } + if ($this->key_length < 16) { + return false; + } + $this->cipher_name_openssl_ecb = 'bf-ecb'; + $this->cipher_name_openssl = 'bf-' . $this->_openssl_translate_mode(); + } + + return parent::isValidEngine($engine); + } + + /** + * Setup the key (expansion) + * + * @see \phpseclib\Crypt\Base::_setupKey() + * @access private + */ + function _setupKey() + { + if (isset($this->kl['key']) && $this->key === $this->kl['key']) { + // already expanded + return; + } + $this->kl = array('key' => $this->key); + + /* key-expanding p[] and S-Box building sb[] */ + $this->bctx = array( + 'p' => array(), + 'sb' => array( + $this->sbox0, + $this->sbox1, + $this->sbox2, + $this->sbox3 + ) + ); + + // unpack binary string in unsigned chars + $key = array_values(unpack('C*', $this->key)); + $keyl = count($key); + // with bcrypt $keyl will always be 16 (because the key is the sha512 of the key you provide) + for ($j = 0, $i = 0; $i < 18; ++$i) { + // xor P1 with the first 32-bits of the key, xor P2 with the second 32-bits ... + for ($data = 0, $k = 0; $k < 4; ++$k) { + $data = ($data << 8) | $key[$j]; + if (++$j >= $keyl) { + $j = 0; + } + } + $this->bctx['p'][] = $this->parray[$i] ^ intval($data); + } + + // encrypt the zero-string, replace P1 and P2 with the encrypted data, + // encrypt P3 and P4 with the new P1 and P2, do it with all P-array and subkeys + $data = "\0\0\0\0\0\0\0\0"; + for ($i = 0; $i < 18; $i += 2) { + list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data))); + $this->bctx['p'][$i ] = $l; + $this->bctx['p'][$i + 1] = $r; + } + for ($i = 0; $i < 4; ++$i) { + for ($j = 0; $j < 256; $j += 2) { + list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data))); + $this->bctx['sb'][$i][$j ] = $l; + $this->bctx['sb'][$i][$j + 1] = $r; + } + } + } + + /** + * bcrypt + * + * @param string $sha2pass + * @param string $sha2salt + * @access private + * @return string + */ + function _bcrypt_hash($sha2pass, $sha2salt) + { + $p = $this->parray; + $sbox0 = $this->sbox0; + $sbox1 = $this->sbox1; + $sbox2 = $this->sbox2; + $sbox3 = $this->sbox3; + + $cdata = array_values(unpack('N*', 'OxychromaticBlowfishSwatDynamite')); + $sha2pass = array_values(unpack('N*', $sha2pass)); + $sha2salt = array_values(unpack('N*', $sha2salt)); + + $this->_expandstate($sha2salt, $sha2pass, $sbox0, $sbox1, $sbox2, $sbox3, $p); + for ($i = 0; $i < 64; $i++) { + $this->_expand0state($sha2salt, $sbox0, $sbox1, $sbox2, $sbox3, $p); + $this->_expand0state($sha2pass, $sbox0, $sbox1, $sbox2, $sbox3, $p); + } + + for ($i = 0; $i < 64; $i++) { + for ($j = 0; $j < 8; $j+= 2) { // count($cdata) == 8 + list($cdata[$j], $cdata[$j + 1]) = $this->_encryptBlockHelperFast($cdata[$j], $cdata[$j + 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); + } + } + + $output = ''; + for ($i = 0; $i < count($cdata); $i++) { + $output.= pack('L*', $cdata[$i]); + } + return $output; + } + + /** + * Performs OpenSSH-style bcrypt + * + * @param string $pass + * @param string $salt + * @param int $keylen + * @param int $rounds + * @access public + * @return false|string + */ + function bcrypt_pbkdf($pass, $salt, $keylen, $rounds) + { + if (PHP_INT_SIZE == 4) { + user_error('bcrypt is far too slow to be practical on 32-bit versions of PHP'); + return false; + } + + if (!isset($this->sha512)) { + $this->sha512 = new Hash('sha512'); + } + + $sha2pass = $this->sha512->hash($pass); + $results = array(); + $count = 1; + while (32 * count($results) < $keylen) { + $countsalt = $salt . pack('N', $count++); + $sha2salt = $this->sha512->hash($countsalt); + $out = $tmpout = $this->_bcrypt_hash($sha2pass, $sha2salt); + for ($i = 1; $i < $rounds; $i++) { + $sha2salt = $this->sha512->hash($tmpout); + $tmpout = $this->_bcrypt_hash($sha2pass, $sha2salt); + $out^= $tmpout; + } + $results[] = $out; + } + $output = ''; + for ($i = 0; $i < 32; $i++) { + foreach ($results as $result) { + $output.= $result[$i]; + } + } + return substr($output, 0, $keylen); + } + + /** + * Key expansion without salt + * + * @access private + * @param int[] $key + * @param int[] $sbox0 + * @param int[] $sbox1 + * @param int[] $sbox2 + * @param int[] $sbox3 + * @param int[] $p + * @see self::_bcrypt_hash() + */ + function _expand0state($key, &$sbox0, &$sbox1, &$sbox2, &$sbox3, &$p) + { + // expand0state is basically the same thing as this: + //return $this->_expandstate(array_fill(0, 16, 0), $key); + // but this separate function eliminates a bunch of XORs and array lookups + + $p = array( + $p[0] ^ $key[0], + $p[1] ^ $key[1], + $p[2] ^ $key[2], + $p[3] ^ $key[3], + $p[4] ^ $key[4], + $p[5] ^ $key[5], + $p[6] ^ $key[6], + $p[7] ^ $key[7], + $p[8] ^ $key[8], + $p[9] ^ $key[9], + $p[10] ^ $key[10], + $p[11] ^ $key[11], + $p[12] ^ $key[12], + $p[13] ^ $key[13], + $p[14] ^ $key[14], + $p[15] ^ $key[15], + $p[16] ^ $key[0], + $p[17] ^ $key[1] + ); + + // @codingStandardsIgnoreStart + list( $p[0], $p[1]) = $this->_encryptBlockHelperFast( 0, 0, $sbox0, $sbox1, $sbox2, $sbox3, $p); + list( $p[2], $p[3]) = $this->_encryptBlockHelperFast($p[ 0], $p[ 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); + list( $p[4], $p[5]) = $this->_encryptBlockHelperFast($p[ 2], $p[ 3], $sbox0, $sbox1, $sbox2, $sbox3, $p); + list( $p[6], $p[7]) = $this->_encryptBlockHelperFast($p[ 4], $p[ 5], $sbox0, $sbox1, $sbox2, $sbox3, $p); + list( $p[8], $p[9]) = $this->_encryptBlockHelperFast($p[ 6], $p[ 7], $sbox0, $sbox1, $sbox2, $sbox3, $p); + list($p[10], $p[11]) = $this->_encryptBlockHelperFast($p[ 8], $p[ 9], $sbox0, $sbox1, $sbox2, $sbox3, $p); + list($p[12], $p[13]) = $this->_encryptBlockHelperFast($p[10], $p[11], $sbox0, $sbox1, $sbox2, $sbox3, $p); + list($p[14], $p[15]) = $this->_encryptBlockHelperFast($p[12], $p[13], $sbox0, $sbox1, $sbox2, $sbox3, $p); + list($p[16], $p[17]) = $this->_encryptBlockHelperFast($p[14], $p[15], $sbox0, $sbox1, $sbox2, $sbox3, $p); + // @codingStandardsIgnoreEnd + + list($sbox0[0], $sbox0[1]) = $this->_encryptBlockHelperFast($p[16], $p[17], $sbox0, $sbox1, $sbox2, $sbox3, $p); + for ($i = 2; $i < 256; $i+= 2) { + list($sbox0[$i], $sbox0[$i + 1]) = $this->_encryptBlockHelperFast($sbox0[$i - 2], $sbox0[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); + } + + list($sbox1[0], $sbox1[1]) = $this->_encryptBlockHelperFast($sbox0[254], $sbox0[255], $sbox0, $sbox1, $sbox2, $sbox3, $p); + for ($i = 2; $i < 256; $i+= 2) { + list($sbox1[$i], $sbox1[$i + 1]) = $this->_encryptBlockHelperFast($sbox1[$i - 2], $sbox1[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); + } + + list($sbox2[0], $sbox2[1]) = $this->_encryptBlockHelperFast($sbox1[254], $sbox1[255], $sbox0, $sbox1, $sbox2, $sbox3, $p); + for ($i = 2; $i < 256; $i+= 2) { + list($sbox2[$i], $sbox2[$i + 1]) = $this->_encryptBlockHelperFast($sbox2[$i - 2], $sbox2[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); + } + + list($sbox3[0], $sbox3[1]) = $this->_encryptBlockHelperFast($sbox2[254], $sbox2[255], $sbox0, $sbox1, $sbox2, $sbox3, $p); + for ($i = 2; $i < 256; $i+= 2) { + list($sbox3[$i], $sbox3[$i + 1]) = $this->_encryptBlockHelperFast($sbox3[$i - 2], $sbox3[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); + } + } + + /** + * Key expansion with salt + * + * @access private + * @param int[] $data + * @param int[] $key + * @param int[] $sbox0 + * @param int[] $sbox1 + * @param int[] $sbox2 + * @param int[] $sbox3 + * @param int[] $p + * @see self::_bcrypt_hash() + */ + function _expandstate($data, $key, &$sbox0, &$sbox1, &$sbox2, &$sbox3, &$p) + { + $p = array( + $p[0] ^ $key[0], + $p[1] ^ $key[1], + $p[2] ^ $key[2], + $p[3] ^ $key[3], + $p[4] ^ $key[4], + $p[5] ^ $key[5], + $p[6] ^ $key[6], + $p[7] ^ $key[7], + $p[8] ^ $key[8], + $p[9] ^ $key[9], + $p[10] ^ $key[10], + $p[11] ^ $key[11], + $p[12] ^ $key[12], + $p[13] ^ $key[13], + $p[14] ^ $key[14], + $p[15] ^ $key[15], + $p[16] ^ $key[0], + $p[17] ^ $key[1] + ); + + // @codingStandardsIgnoreStart + list( $p[0], $p[1]) = $this->_encryptBlockHelperFast($data[ 0] , $data[ 1] , $sbox0, $sbox1, $sbox2, $sbox3, $p); + list( $p[2], $p[3]) = $this->_encryptBlockHelperFast($data[ 2] ^ $p[ 0], $data[ 3] ^ $p[ 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); + list( $p[4], $p[5]) = $this->_encryptBlockHelperFast($data[ 4] ^ $p[ 2], $data[ 5] ^ $p[ 3], $sbox0, $sbox1, $sbox2, $sbox3, $p); + list( $p[6], $p[7]) = $this->_encryptBlockHelperFast($data[ 6] ^ $p[ 4], $data[ 7] ^ $p[ 5], $sbox0, $sbox1, $sbox2, $sbox3, $p); + list( $p[8], $p[9]) = $this->_encryptBlockHelperFast($data[ 8] ^ $p[ 6], $data[ 9] ^ $p[ 7], $sbox0, $sbox1, $sbox2, $sbox3, $p); + list($p[10], $p[11]) = $this->_encryptBlockHelperFast($data[10] ^ $p[ 8], $data[11] ^ $p[ 9], $sbox0, $sbox1, $sbox2, $sbox3, $p); + list($p[12], $p[13]) = $this->_encryptBlockHelperFast($data[12] ^ $p[10], $data[13] ^ $p[11], $sbox0, $sbox1, $sbox2, $sbox3, $p); + list($p[14], $p[15]) = $this->_encryptBlockHelperFast($data[14] ^ $p[12], $data[15] ^ $p[13], $sbox0, $sbox1, $sbox2, $sbox3, $p); + list($p[16], $p[17]) = $this->_encryptBlockHelperFast($data[ 0] ^ $p[14], $data[ 1] ^ $p[15], $sbox0, $sbox1, $sbox2, $sbox3, $p); + // @codingStandardsIgnoreEnd + + list($sbox0[0], $sbox0[1]) = $this->_encryptBlockHelperFast($data[2] ^ $p[16], $data[3] ^ $p[17], $sbox0, $sbox1, $sbox2, $sbox3, $p); + for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) { // instead of 16 maybe count($data) would be better? + list($sbox0[$i], $sbox0[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox0[$i - 2], $data[$j + 1] ^ $sbox0[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); + } + + list($sbox1[0], $sbox1[1]) = $this->_encryptBlockHelperFast($data[2] ^ $sbox0[254], $data[3] ^ $sbox0[255], $sbox0, $sbox1, $sbox2, $sbox3, $p); + for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) { + list($sbox1[$i], $sbox1[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox1[$i - 2], $data[$j + 1] ^ $sbox1[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); + } + + list($sbox2[0], $sbox2[1]) = $this->_encryptBlockHelperFast($data[2] ^ $sbox1[254], $data[3] ^ $sbox1[255], $sbox0, $sbox1, $sbox2, $sbox3, $p); + for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) { + list($sbox2[$i], $sbox2[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox2[$i - 2], $data[$j + 1] ^ $sbox2[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); + } + + list($sbox3[0], $sbox3[1]) = $this->_encryptBlockHelperFast($data[2] ^ $sbox2[254], $data[3] ^ $sbox2[255], $sbox0, $sbox1, $sbox2, $sbox3, $p); + for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) { + list($sbox3[$i], $sbox3[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox3[$i - 2], $data[$j + 1] ^ $sbox3[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p); + } + } + + /** + * Encrypts a block + * + * @access private + * @param string $in + * @return string + */ + function _encryptBlock($in) + { + $p = $this->bctx["p"]; + // extract($this->bctx["sb"], EXTR_PREFIX_ALL, "sb"); // slower + $sb_0 = $this->bctx["sb"][0]; + $sb_1 = $this->bctx["sb"][1]; + $sb_2 = $this->bctx["sb"][2]; + $sb_3 = $this->bctx["sb"][3]; + + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + + list($r, $l) = CRYPT_BASE_USE_REG_INTVAL ? + $this->_encryptBlockHelperFast($l, $r, $sb_0, $sb_1, $sb_2, $sb_3, $p) : + $this->_encryptBlockHelperSlow($l, $r, $sb_0, $sb_1, $sb_2, $sb_3, $p); + + return pack("N*", $r, $l); + } + + /** + * Fast helper function for block encryption + * + * @access private + * @param int $x0 + * @param int $x1 + * @param int[] $sbox0 + * @param int[] $sbox1 + * @param int[] $sbox2 + * @param int[] $sbox3 + * @param int[] $p + * @return int[] + */ + function _encryptBlockHelperFast($x0, $x1, $sbox0, $sbox1, $sbox2, $sbox3, $p) + { + $x0 ^= $p[0]; + $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[1]; + $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[2]; + $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[3]; + $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[4]; + $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[5]; + $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[6]; + $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[7]; + $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[8]; + $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[9]; + $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[10]; + $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[11]; + $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[12]; + $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[13]; + $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[14]; + $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[15]; + $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[16]; + + return array($x1 & 0xFFFFFFFF ^ $p[17], $x0 & 0xFFFFFFFF); + } + + /** + * Slow helper function for block encryption + * + * @access private + * @param int $x0 + * @param int $x1 + * @param int[] $sbox0 + * @param int[] $sbox1 + * @param int[] $sbox2 + * @param int[] $sbox3 + * @param int[] $p + * @return int[] + */ + function _encryptBlockHelperSlow($x0, $x1, $sbox0, $sbox1, $sbox2, $sbox3, $p) + { + $x0^= $p[0]; + $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[1]; + $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[2]; + $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[3]; + $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[4]; + $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[5]; + $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[6]; + $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[7]; + $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[8]; + $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[9]; + $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[10]; + $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[11]; + $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[12]; + $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[13]; + $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[14]; + $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[15]; + $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[16]; + + return array($x1 & 0xFFFFFFFF ^ $p[17], $x0 & 0xFFFFFFFF); + } + + /** + * Decrypts a block + * + * @access private + * @param string $in + * @return string + */ + function _decryptBlock($in) + { + $p = $this->bctx["p"]; + $sb_0 = $this->bctx["sb"][0]; + $sb_1 = $this->bctx["sb"][1]; + $sb_2 = $this->bctx["sb"][2]; + $sb_3 = $this->bctx["sb"][3]; + + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + + for ($i = 17; $i > 2; $i-= 2) { + $l^= $p[$i]; + $r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^ + $sb_2[$l >> 8 & 0xff]) + + $sb_3[$l & 0xff]); + + $r^= $p[$i - 1]; + $l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^ + $sb_2[$r >> 8 & 0xff]) + + $sb_3[$r & 0xff]); + } + return pack("N*", $r ^ $p[0], $l ^ $p[1]); + } + + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see \phpseclib\Crypt\Base::_setupInlineCrypt() + * @access private + */ + function _setupInlineCrypt() + { + $lambda_functions =& self::_getLambdaFunctions(); + + // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function. + // (Currently, for Blowfish, one generated $lambda_function cost on php5.5@32bit ~100kb unfreeable mem and ~180kb on php5.5@64bit) + // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one. + $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); + + // Generation of a unique hash for our generated code + $code_hash = "Crypt_Blowfish, {$this->mode}"; + if ($gen_hi_opt_code) { + $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); + } + + $safeint = $this->safe_intval_inline(); + + if (!isset($lambda_functions[$code_hash])) { + switch (true) { + case $gen_hi_opt_code: + $p = $this->bctx['p']; + $init_crypt = ' + static $sb_0, $sb_1, $sb_2, $sb_3; + if (!$sb_0) { + $sb_0 = $self->bctx["sb"][0]; + $sb_1 = $self->bctx["sb"][1]; + $sb_2 = $self->bctx["sb"][2]; + $sb_3 = $self->bctx["sb"][3]; + } + '; + break; + default: + $p = array(); + for ($i = 0; $i < 18; ++$i) { + $p[] = '$p_' . $i; + } + $init_crypt = ' + list($sb_0, $sb_1, $sb_2, $sb_3) = $self->bctx["sb"]; + list(' . implode(',', $p) . ') = $self->bctx["p"]; + + '; + } + + // Generating encrypt code: + $encrypt_block = ' + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + '; + for ($i = 0; $i < 16; $i+= 2) { + $encrypt_block.= ' + $l^= ' . $p[$i] . '; + $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^ + $sb_2[$l >> 8 & 0xff]) + + $sb_3[$l & 0xff]') . '; + + $r^= ' . $p[$i + 1] . '; + $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^ + $sb_2[$r >> 8 & 0xff]) + + $sb_3[$r & 0xff]') . '; + '; + } + $encrypt_block.= ' + $in = pack("N*", + $r ^ ' . $p[17] . ', + $l ^ ' . $p[16] . ' + ); + '; + + // Generating decrypt code: + $decrypt_block = ' + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + '; + + for ($i = 17; $i > 2; $i-= 2) { + $decrypt_block.= ' + $l^= ' . $p[$i] . '; + $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^ + $sb_2[$l >> 8 & 0xff]) + + $sb_3[$l & 0xff]') . '; + + $r^= ' . $p[$i - 1] . '; + $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^ + $sb_2[$r >> 8 & 0xff]) + + $sb_3[$r & 0xff]') . '; + '; + } + + $decrypt_block.= ' + $in = pack("N*", + $r ^ ' . $p[0] . ', + $l ^ ' . $p[1] . ' + ); + '; + + $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( + array( + 'init_crypt' => $init_crypt, + 'init_encrypt' => '', + 'init_decrypt' => '', + 'encrypt_block' => $encrypt_block, + 'decrypt_block' => $decrypt_block + ) + ); + } + $this->inline_crypt = $lambda_functions[$code_hash]; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php new file mode 100644 index 0000000..5c77917 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php @@ -0,0 +1,1449 @@ + + * setKey('abcdefgh'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $des->decrypt($des->encrypt($plaintext)); + * ?> + * + * + * @category Crypt + * @package DES + * @author Jim Wigginton + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +/** + * Pure-PHP implementation of DES. + * + * @package DES + * @author Jim Wigginton + * @access public + */ +class DES extends Base +{ + /**#@+ + * @access private + * @see \phpseclib\Crypt\DES::_setupKey() + * @see \phpseclib\Crypt\DES::_processBlock() + */ + /** + * Contains $keys[self::ENCRYPT] + */ + const ENCRYPT = 0; + /** + * Contains $keys[self::DECRYPT] + */ + const DECRYPT = 1; + /**#@-*/ + + /** + * Block Length of the cipher + * + * @see \phpseclib\Crypt\Base::block_size + * @var int + * @access private + */ + var $block_size = 8; + + /** + * Key Length (in bytes) + * + * @see \phpseclib\Crypt\Base::setKeyLength() + * @var int + * @access private + */ + var $key_length = 8; + + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib\Crypt\Base::cipher_name_mcrypt + * @var string + * @access private + */ + var $cipher_name_mcrypt = 'des'; + + /** + * The OpenSSL names of the cipher / modes + * + * @see \phpseclib\Crypt\Base::openssl_mode_names + * @var array + * @access private + */ + var $openssl_mode_names = array( + self::MODE_ECB => 'des-ecb', + self::MODE_CBC => 'des-cbc', + self::MODE_CFB => 'des-cfb', + self::MODE_OFB => 'des-ofb' + // self::MODE_CTR is undefined for DES + ); + + /** + * Optimizing value while CFB-encrypting + * + * @see \phpseclib\Crypt\Base::cfb_init_len + * @var int + * @access private + */ + var $cfb_init_len = 500; + + /** + * Switch for DES/3DES encryption + * + * Used only if $engine == self::ENGINE_INTERNAL + * + * @see self::_setupKey() + * @see self::_processBlock() + * @var int + * @access private + */ + var $des_rounds = 1; + + /** + * max possible size of $key + * + * @see self::setKey() + * @var string + * @access private + */ + var $key_length_max = 8; + + /** + * The Key Schedule + * + * @see self::_setupKey() + * @var array + * @access private + */ + var $keys; + + /** + * Shuffle table. + * + * For each byte value index, the entry holds an 8-byte string + * with each byte containing all bits in the same state as the + * corresponding bit in the index value. + * + * @see self::_processBlock() + * @see self::_setupKey() + * @var array + * @access private + */ + var $shuffle = array( + "\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\xFF", + "\x00\x00\x00\x00\x00\x00\xFF\x00", "\x00\x00\x00\x00\x00\x00\xFF\xFF", + "\x00\x00\x00\x00\x00\xFF\x00\x00", "\x00\x00\x00\x00\x00\xFF\x00\xFF", + "\x00\x00\x00\x00\x00\xFF\xFF\x00", "\x00\x00\x00\x00\x00\xFF\xFF\xFF", + "\x00\x00\x00\x00\xFF\x00\x00\x00", "\x00\x00\x00\x00\xFF\x00\x00\xFF", + "\x00\x00\x00\x00\xFF\x00\xFF\x00", "\x00\x00\x00\x00\xFF\x00\xFF\xFF", + "\x00\x00\x00\x00\xFF\xFF\x00\x00", "\x00\x00\x00\x00\xFF\xFF\x00\xFF", + "\x00\x00\x00\x00\xFF\xFF\xFF\x00", "\x00\x00\x00\x00\xFF\xFF\xFF\xFF", + "\x00\x00\x00\xFF\x00\x00\x00\x00", "\x00\x00\x00\xFF\x00\x00\x00\xFF", + "\x00\x00\x00\xFF\x00\x00\xFF\x00", "\x00\x00\x00\xFF\x00\x00\xFF\xFF", + "\x00\x00\x00\xFF\x00\xFF\x00\x00", "\x00\x00\x00\xFF\x00\xFF\x00\xFF", + "\x00\x00\x00\xFF\x00\xFF\xFF\x00", "\x00\x00\x00\xFF\x00\xFF\xFF\xFF", + "\x00\x00\x00\xFF\xFF\x00\x00\x00", "\x00\x00\x00\xFF\xFF\x00\x00\xFF", + "\x00\x00\x00\xFF\xFF\x00\xFF\x00", "\x00\x00\x00\xFF\xFF\x00\xFF\xFF", + "\x00\x00\x00\xFF\xFF\xFF\x00\x00", "\x00\x00\x00\xFF\xFF\xFF\x00\xFF", + "\x00\x00\x00\xFF\xFF\xFF\xFF\x00", "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF", + "\x00\x00\xFF\x00\x00\x00\x00\x00", "\x00\x00\xFF\x00\x00\x00\x00\xFF", + "\x00\x00\xFF\x00\x00\x00\xFF\x00", "\x00\x00\xFF\x00\x00\x00\xFF\xFF", + "\x00\x00\xFF\x00\x00\xFF\x00\x00", "\x00\x00\xFF\x00\x00\xFF\x00\xFF", + "\x00\x00\xFF\x00\x00\xFF\xFF\x00", "\x00\x00\xFF\x00\x00\xFF\xFF\xFF", + "\x00\x00\xFF\x00\xFF\x00\x00\x00", "\x00\x00\xFF\x00\xFF\x00\x00\xFF", + "\x00\x00\xFF\x00\xFF\x00\xFF\x00", "\x00\x00\xFF\x00\xFF\x00\xFF\xFF", + "\x00\x00\xFF\x00\xFF\xFF\x00\x00", "\x00\x00\xFF\x00\xFF\xFF\x00\xFF", + "\x00\x00\xFF\x00\xFF\xFF\xFF\x00", "\x00\x00\xFF\x00\xFF\xFF\xFF\xFF", + "\x00\x00\xFF\xFF\x00\x00\x00\x00", "\x00\x00\xFF\xFF\x00\x00\x00\xFF", + "\x00\x00\xFF\xFF\x00\x00\xFF\x00", "\x00\x00\xFF\xFF\x00\x00\xFF\xFF", + "\x00\x00\xFF\xFF\x00\xFF\x00\x00", "\x00\x00\xFF\xFF\x00\xFF\x00\xFF", + "\x00\x00\xFF\xFF\x00\xFF\xFF\x00", "\x00\x00\xFF\xFF\x00\xFF\xFF\xFF", + "\x00\x00\xFF\xFF\xFF\x00\x00\x00", "\x00\x00\xFF\xFF\xFF\x00\x00\xFF", + "\x00\x00\xFF\xFF\xFF\x00\xFF\x00", "\x00\x00\xFF\xFF\xFF\x00\xFF\xFF", + "\x00\x00\xFF\xFF\xFF\xFF\x00\x00", "\x00\x00\xFF\xFF\xFF\xFF\x00\xFF", + "\x00\x00\xFF\xFF\xFF\xFF\xFF\x00", "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF", + "\x00\xFF\x00\x00\x00\x00\x00\x00", "\x00\xFF\x00\x00\x00\x00\x00\xFF", + "\x00\xFF\x00\x00\x00\x00\xFF\x00", "\x00\xFF\x00\x00\x00\x00\xFF\xFF", + "\x00\xFF\x00\x00\x00\xFF\x00\x00", "\x00\xFF\x00\x00\x00\xFF\x00\xFF", + "\x00\xFF\x00\x00\x00\xFF\xFF\x00", "\x00\xFF\x00\x00\x00\xFF\xFF\xFF", + "\x00\xFF\x00\x00\xFF\x00\x00\x00", "\x00\xFF\x00\x00\xFF\x00\x00\xFF", + "\x00\xFF\x00\x00\xFF\x00\xFF\x00", "\x00\xFF\x00\x00\xFF\x00\xFF\xFF", + "\x00\xFF\x00\x00\xFF\xFF\x00\x00", "\x00\xFF\x00\x00\xFF\xFF\x00\xFF", + "\x00\xFF\x00\x00\xFF\xFF\xFF\x00", "\x00\xFF\x00\x00\xFF\xFF\xFF\xFF", + "\x00\xFF\x00\xFF\x00\x00\x00\x00", "\x00\xFF\x00\xFF\x00\x00\x00\xFF", + "\x00\xFF\x00\xFF\x00\x00\xFF\x00", "\x00\xFF\x00\xFF\x00\x00\xFF\xFF", + "\x00\xFF\x00\xFF\x00\xFF\x00\x00", "\x00\xFF\x00\xFF\x00\xFF\x00\xFF", + "\x00\xFF\x00\xFF\x00\xFF\xFF\x00", "\x00\xFF\x00\xFF\x00\xFF\xFF\xFF", + "\x00\xFF\x00\xFF\xFF\x00\x00\x00", "\x00\xFF\x00\xFF\xFF\x00\x00\xFF", + "\x00\xFF\x00\xFF\xFF\x00\xFF\x00", "\x00\xFF\x00\xFF\xFF\x00\xFF\xFF", + "\x00\xFF\x00\xFF\xFF\xFF\x00\x00", "\x00\xFF\x00\xFF\xFF\xFF\x00\xFF", + "\x00\xFF\x00\xFF\xFF\xFF\xFF\x00", "\x00\xFF\x00\xFF\xFF\xFF\xFF\xFF", + "\x00\xFF\xFF\x00\x00\x00\x00\x00", "\x00\xFF\xFF\x00\x00\x00\x00\xFF", + "\x00\xFF\xFF\x00\x00\x00\xFF\x00", "\x00\xFF\xFF\x00\x00\x00\xFF\xFF", + "\x00\xFF\xFF\x00\x00\xFF\x00\x00", "\x00\xFF\xFF\x00\x00\xFF\x00\xFF", + "\x00\xFF\xFF\x00\x00\xFF\xFF\x00", "\x00\xFF\xFF\x00\x00\xFF\xFF\xFF", + "\x00\xFF\xFF\x00\xFF\x00\x00\x00", "\x00\xFF\xFF\x00\xFF\x00\x00\xFF", + "\x00\xFF\xFF\x00\xFF\x00\xFF\x00", "\x00\xFF\xFF\x00\xFF\x00\xFF\xFF", + "\x00\xFF\xFF\x00\xFF\xFF\x00\x00", "\x00\xFF\xFF\x00\xFF\xFF\x00\xFF", + "\x00\xFF\xFF\x00\xFF\xFF\xFF\x00", "\x00\xFF\xFF\x00\xFF\xFF\xFF\xFF", + "\x00\xFF\xFF\xFF\x00\x00\x00\x00", "\x00\xFF\xFF\xFF\x00\x00\x00\xFF", + "\x00\xFF\xFF\xFF\x00\x00\xFF\x00", "\x00\xFF\xFF\xFF\x00\x00\xFF\xFF", + "\x00\xFF\xFF\xFF\x00\xFF\x00\x00", "\x00\xFF\xFF\xFF\x00\xFF\x00\xFF", + "\x00\xFF\xFF\xFF\x00\xFF\xFF\x00", "\x00\xFF\xFF\xFF\x00\xFF\xFF\xFF", + "\x00\xFF\xFF\xFF\xFF\x00\x00\x00", "\x00\xFF\xFF\xFF\xFF\x00\x00\xFF", + "\x00\xFF\xFF\xFF\xFF\x00\xFF\x00", "\x00\xFF\xFF\xFF\xFF\x00\xFF\xFF", + "\x00\xFF\xFF\xFF\xFF\xFF\x00\x00", "\x00\xFF\xFF\xFF\xFF\xFF\x00\xFF", + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF", + "\xFF\x00\x00\x00\x00\x00\x00\x00", "\xFF\x00\x00\x00\x00\x00\x00\xFF", + "\xFF\x00\x00\x00\x00\x00\xFF\x00", "\xFF\x00\x00\x00\x00\x00\xFF\xFF", + "\xFF\x00\x00\x00\x00\xFF\x00\x00", "\xFF\x00\x00\x00\x00\xFF\x00\xFF", + "\xFF\x00\x00\x00\x00\xFF\xFF\x00", "\xFF\x00\x00\x00\x00\xFF\xFF\xFF", + "\xFF\x00\x00\x00\xFF\x00\x00\x00", "\xFF\x00\x00\x00\xFF\x00\x00\xFF", + "\xFF\x00\x00\x00\xFF\x00\xFF\x00", "\xFF\x00\x00\x00\xFF\x00\xFF\xFF", + "\xFF\x00\x00\x00\xFF\xFF\x00\x00", "\xFF\x00\x00\x00\xFF\xFF\x00\xFF", + "\xFF\x00\x00\x00\xFF\xFF\xFF\x00", "\xFF\x00\x00\x00\xFF\xFF\xFF\xFF", + "\xFF\x00\x00\xFF\x00\x00\x00\x00", "\xFF\x00\x00\xFF\x00\x00\x00\xFF", + "\xFF\x00\x00\xFF\x00\x00\xFF\x00", "\xFF\x00\x00\xFF\x00\x00\xFF\xFF", + "\xFF\x00\x00\xFF\x00\xFF\x00\x00", "\xFF\x00\x00\xFF\x00\xFF\x00\xFF", + "\xFF\x00\x00\xFF\x00\xFF\xFF\x00", "\xFF\x00\x00\xFF\x00\xFF\xFF\xFF", + "\xFF\x00\x00\xFF\xFF\x00\x00\x00", "\xFF\x00\x00\xFF\xFF\x00\x00\xFF", + "\xFF\x00\x00\xFF\xFF\x00\xFF\x00", "\xFF\x00\x00\xFF\xFF\x00\xFF\xFF", + "\xFF\x00\x00\xFF\xFF\xFF\x00\x00", "\xFF\x00\x00\xFF\xFF\xFF\x00\xFF", + "\xFF\x00\x00\xFF\xFF\xFF\xFF\x00", "\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF", + "\xFF\x00\xFF\x00\x00\x00\x00\x00", "\xFF\x00\xFF\x00\x00\x00\x00\xFF", + "\xFF\x00\xFF\x00\x00\x00\xFF\x00", "\xFF\x00\xFF\x00\x00\x00\xFF\xFF", + "\xFF\x00\xFF\x00\x00\xFF\x00\x00", "\xFF\x00\xFF\x00\x00\xFF\x00\xFF", + "\xFF\x00\xFF\x00\x00\xFF\xFF\x00", "\xFF\x00\xFF\x00\x00\xFF\xFF\xFF", + "\xFF\x00\xFF\x00\xFF\x00\x00\x00", "\xFF\x00\xFF\x00\xFF\x00\x00\xFF", + "\xFF\x00\xFF\x00\xFF\x00\xFF\x00", "\xFF\x00\xFF\x00\xFF\x00\xFF\xFF", + "\xFF\x00\xFF\x00\xFF\xFF\x00\x00", "\xFF\x00\xFF\x00\xFF\xFF\x00\xFF", + "\xFF\x00\xFF\x00\xFF\xFF\xFF\x00", "\xFF\x00\xFF\x00\xFF\xFF\xFF\xFF", + "\xFF\x00\xFF\xFF\x00\x00\x00\x00", "\xFF\x00\xFF\xFF\x00\x00\x00\xFF", + "\xFF\x00\xFF\xFF\x00\x00\xFF\x00", "\xFF\x00\xFF\xFF\x00\x00\xFF\xFF", + "\xFF\x00\xFF\xFF\x00\xFF\x00\x00", "\xFF\x00\xFF\xFF\x00\xFF\x00\xFF", + "\xFF\x00\xFF\xFF\x00\xFF\xFF\x00", "\xFF\x00\xFF\xFF\x00\xFF\xFF\xFF", + "\xFF\x00\xFF\xFF\xFF\x00\x00\x00", "\xFF\x00\xFF\xFF\xFF\x00\x00\xFF", + "\xFF\x00\xFF\xFF\xFF\x00\xFF\x00", "\xFF\x00\xFF\xFF\xFF\x00\xFF\xFF", + "\xFF\x00\xFF\xFF\xFF\xFF\x00\x00", "\xFF\x00\xFF\xFF\xFF\xFF\x00\xFF", + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF", + "\xFF\xFF\x00\x00\x00\x00\x00\x00", "\xFF\xFF\x00\x00\x00\x00\x00\xFF", + "\xFF\xFF\x00\x00\x00\x00\xFF\x00", "\xFF\xFF\x00\x00\x00\x00\xFF\xFF", + "\xFF\xFF\x00\x00\x00\xFF\x00\x00", "\xFF\xFF\x00\x00\x00\xFF\x00\xFF", + "\xFF\xFF\x00\x00\x00\xFF\xFF\x00", "\xFF\xFF\x00\x00\x00\xFF\xFF\xFF", + "\xFF\xFF\x00\x00\xFF\x00\x00\x00", "\xFF\xFF\x00\x00\xFF\x00\x00\xFF", + "\xFF\xFF\x00\x00\xFF\x00\xFF\x00", "\xFF\xFF\x00\x00\xFF\x00\xFF\xFF", + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00", "\xFF\xFF\x00\x00\xFF\xFF\x00\xFF", + "\xFF\xFF\x00\x00\xFF\xFF\xFF\x00", "\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF", + "\xFF\xFF\x00\xFF\x00\x00\x00\x00", "\xFF\xFF\x00\xFF\x00\x00\x00\xFF", + "\xFF\xFF\x00\xFF\x00\x00\xFF\x00", "\xFF\xFF\x00\xFF\x00\x00\xFF\xFF", + "\xFF\xFF\x00\xFF\x00\xFF\x00\x00", "\xFF\xFF\x00\xFF\x00\xFF\x00\xFF", + "\xFF\xFF\x00\xFF\x00\xFF\xFF\x00", "\xFF\xFF\x00\xFF\x00\xFF\xFF\xFF", + "\xFF\xFF\x00\xFF\xFF\x00\x00\x00", "\xFF\xFF\x00\xFF\xFF\x00\x00\xFF", + "\xFF\xFF\x00\xFF\xFF\x00\xFF\x00", "\xFF\xFF\x00\xFF\xFF\x00\xFF\xFF", + "\xFF\xFF\x00\xFF\xFF\xFF\x00\x00", "\xFF\xFF\x00\xFF\xFF\xFF\x00\xFF", + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF", + "\xFF\xFF\xFF\x00\x00\x00\x00\x00", "\xFF\xFF\xFF\x00\x00\x00\x00\xFF", + "\xFF\xFF\xFF\x00\x00\x00\xFF\x00", "\xFF\xFF\xFF\x00\x00\x00\xFF\xFF", + "\xFF\xFF\xFF\x00\x00\xFF\x00\x00", "\xFF\xFF\xFF\x00\x00\xFF\x00\xFF", + "\xFF\xFF\xFF\x00\x00\xFF\xFF\x00", "\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF", + "\xFF\xFF\xFF\x00\xFF\x00\x00\x00", "\xFF\xFF\xFF\x00\xFF\x00\x00\xFF", + "\xFF\xFF\xFF\x00\xFF\x00\xFF\x00", "\xFF\xFF\xFF\x00\xFF\x00\xFF\xFF", + "\xFF\xFF\xFF\x00\xFF\xFF\x00\x00", "\xFF\xFF\xFF\x00\xFF\xFF\x00\xFF", + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF", + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00", "\xFF\xFF\xFF\xFF\x00\x00\x00\xFF", + "\xFF\xFF\xFF\xFF\x00\x00\xFF\x00", "\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF", + "\xFF\xFF\xFF\xFF\x00\xFF\x00\x00", "\xFF\xFF\xFF\xFF\x00\xFF\x00\xFF", + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF", + "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00", "\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF", + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF", + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF", + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + ); + + /** + * IP mapping helper table. + * + * Indexing this table with each source byte performs the initial bit permutation. + * + * @var array + * @access private + */ + var $ipmap = array( + 0x00, 0x10, 0x01, 0x11, 0x20, 0x30, 0x21, 0x31, + 0x02, 0x12, 0x03, 0x13, 0x22, 0x32, 0x23, 0x33, + 0x40, 0x50, 0x41, 0x51, 0x60, 0x70, 0x61, 0x71, + 0x42, 0x52, 0x43, 0x53, 0x62, 0x72, 0x63, 0x73, + 0x04, 0x14, 0x05, 0x15, 0x24, 0x34, 0x25, 0x35, + 0x06, 0x16, 0x07, 0x17, 0x26, 0x36, 0x27, 0x37, + 0x44, 0x54, 0x45, 0x55, 0x64, 0x74, 0x65, 0x75, + 0x46, 0x56, 0x47, 0x57, 0x66, 0x76, 0x67, 0x77, + 0x80, 0x90, 0x81, 0x91, 0xA0, 0xB0, 0xA1, 0xB1, + 0x82, 0x92, 0x83, 0x93, 0xA2, 0xB2, 0xA3, 0xB3, + 0xC0, 0xD0, 0xC1, 0xD1, 0xE0, 0xF0, 0xE1, 0xF1, + 0xC2, 0xD2, 0xC3, 0xD3, 0xE2, 0xF2, 0xE3, 0xF3, + 0x84, 0x94, 0x85, 0x95, 0xA4, 0xB4, 0xA5, 0xB5, + 0x86, 0x96, 0x87, 0x97, 0xA6, 0xB6, 0xA7, 0xB7, + 0xC4, 0xD4, 0xC5, 0xD5, 0xE4, 0xF4, 0xE5, 0xF5, + 0xC6, 0xD6, 0xC7, 0xD7, 0xE6, 0xF6, 0xE7, 0xF7, + 0x08, 0x18, 0x09, 0x19, 0x28, 0x38, 0x29, 0x39, + 0x0A, 0x1A, 0x0B, 0x1B, 0x2A, 0x3A, 0x2B, 0x3B, + 0x48, 0x58, 0x49, 0x59, 0x68, 0x78, 0x69, 0x79, + 0x4A, 0x5A, 0x4B, 0x5B, 0x6A, 0x7A, 0x6B, 0x7B, + 0x0C, 0x1C, 0x0D, 0x1D, 0x2C, 0x3C, 0x2D, 0x3D, + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4C, 0x5C, 0x4D, 0x5D, 0x6C, 0x7C, 0x6D, 0x7D, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x88, 0x98, 0x89, 0x99, 0xA8, 0xB8, 0xA9, 0xB9, + 0x8A, 0x9A, 0x8B, 0x9B, 0xAA, 0xBA, 0xAB, 0xBB, + 0xC8, 0xD8, 0xC9, 0xD9, 0xE8, 0xF8, 0xE9, 0xF9, + 0xCA, 0xDA, 0xCB, 0xDB, 0xEA, 0xFA, 0xEB, 0xFB, + 0x8C, 0x9C, 0x8D, 0x9D, 0xAC, 0xBC, 0xAD, 0xBD, + 0x8E, 0x9E, 0x8F, 0x9F, 0xAE, 0xBE, 0xAF, 0xBF, + 0xCC, 0xDC, 0xCD, 0xDD, 0xEC, 0xFC, 0xED, 0xFD, + 0xCE, 0xDE, 0xCF, 0xDF, 0xEE, 0xFE, 0xEF, 0xFF + ); + + /** + * Inverse IP mapping helper table. + * Indexing this table with a byte value reverses the bit order. + * + * @var array + * @access private + */ + var $invipmap = array( + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF + ); + + /** + * Pre-permuted S-box1 + * + * Each box ($sbox1-$sbox8) has been vectorized, then each value pre-permuted using the + * P table: concatenation can then be replaced by exclusive ORs. + * + * @var array + * @access private + */ + var $sbox1 = array( + 0x00808200, 0x00000000, 0x00008000, 0x00808202, + 0x00808002, 0x00008202, 0x00000002, 0x00008000, + 0x00000200, 0x00808200, 0x00808202, 0x00000200, + 0x00800202, 0x00808002, 0x00800000, 0x00000002, + 0x00000202, 0x00800200, 0x00800200, 0x00008200, + 0x00008200, 0x00808000, 0x00808000, 0x00800202, + 0x00008002, 0x00800002, 0x00800002, 0x00008002, + 0x00000000, 0x00000202, 0x00008202, 0x00800000, + 0x00008000, 0x00808202, 0x00000002, 0x00808000, + 0x00808200, 0x00800000, 0x00800000, 0x00000200, + 0x00808002, 0x00008000, 0x00008200, 0x00800002, + 0x00000200, 0x00000002, 0x00800202, 0x00008202, + 0x00808202, 0x00008002, 0x00808000, 0x00800202, + 0x00800002, 0x00000202, 0x00008202, 0x00808200, + 0x00000202, 0x00800200, 0x00800200, 0x00000000, + 0x00008002, 0x00008200, 0x00000000, 0x00808002 + ); + + /** + * Pre-permuted S-box2 + * + * @var array + * @access private + */ + var $sbox2 = array( + 0x40084010, 0x40004000, 0x00004000, 0x00084010, + 0x00080000, 0x00000010, 0x40080010, 0x40004010, + 0x40000010, 0x40084010, 0x40084000, 0x40000000, + 0x40004000, 0x00080000, 0x00000010, 0x40080010, + 0x00084000, 0x00080010, 0x40004010, 0x00000000, + 0x40000000, 0x00004000, 0x00084010, 0x40080000, + 0x00080010, 0x40000010, 0x00000000, 0x00084000, + 0x00004010, 0x40084000, 0x40080000, 0x00004010, + 0x00000000, 0x00084010, 0x40080010, 0x00080000, + 0x40004010, 0x40080000, 0x40084000, 0x00004000, + 0x40080000, 0x40004000, 0x00000010, 0x40084010, + 0x00084010, 0x00000010, 0x00004000, 0x40000000, + 0x00004010, 0x40084000, 0x00080000, 0x40000010, + 0x00080010, 0x40004010, 0x40000010, 0x00080010, + 0x00084000, 0x00000000, 0x40004000, 0x00004010, + 0x40000000, 0x40080010, 0x40084010, 0x00084000 + ); + + /** + * Pre-permuted S-box3 + * + * @var array + * @access private + */ + var $sbox3 = array( + 0x00000104, 0x04010100, 0x00000000, 0x04010004, + 0x04000100, 0x00000000, 0x00010104, 0x04000100, + 0x00010004, 0x04000004, 0x04000004, 0x00010000, + 0x04010104, 0x00010004, 0x04010000, 0x00000104, + 0x04000000, 0x00000004, 0x04010100, 0x00000100, + 0x00010100, 0x04010000, 0x04010004, 0x00010104, + 0x04000104, 0x00010100, 0x00010000, 0x04000104, + 0x00000004, 0x04010104, 0x00000100, 0x04000000, + 0x04010100, 0x04000000, 0x00010004, 0x00000104, + 0x00010000, 0x04010100, 0x04000100, 0x00000000, + 0x00000100, 0x00010004, 0x04010104, 0x04000100, + 0x04000004, 0x00000100, 0x00000000, 0x04010004, + 0x04000104, 0x00010000, 0x04000000, 0x04010104, + 0x00000004, 0x00010104, 0x00010100, 0x04000004, + 0x04010000, 0x04000104, 0x00000104, 0x04010000, + 0x00010104, 0x00000004, 0x04010004, 0x00010100 + ); + + /** + * Pre-permuted S-box4 + * + * @var array + * @access private + */ + var $sbox4 = array( + 0x80401000, 0x80001040, 0x80001040, 0x00000040, + 0x00401040, 0x80400040, 0x80400000, 0x80001000, + 0x00000000, 0x00401000, 0x00401000, 0x80401040, + 0x80000040, 0x00000000, 0x00400040, 0x80400000, + 0x80000000, 0x00001000, 0x00400000, 0x80401000, + 0x00000040, 0x00400000, 0x80001000, 0x00001040, + 0x80400040, 0x80000000, 0x00001040, 0x00400040, + 0x00001000, 0x00401040, 0x80401040, 0x80000040, + 0x00400040, 0x80400000, 0x00401000, 0x80401040, + 0x80000040, 0x00000000, 0x00000000, 0x00401000, + 0x00001040, 0x00400040, 0x80400040, 0x80000000, + 0x80401000, 0x80001040, 0x80001040, 0x00000040, + 0x80401040, 0x80000040, 0x80000000, 0x00001000, + 0x80400000, 0x80001000, 0x00401040, 0x80400040, + 0x80001000, 0x00001040, 0x00400000, 0x80401000, + 0x00000040, 0x00400000, 0x00001000, 0x00401040 + ); + + /** + * Pre-permuted S-box5 + * + * @var array + * @access private + */ + var $sbox5 = array( + 0x00000080, 0x01040080, 0x01040000, 0x21000080, + 0x00040000, 0x00000080, 0x20000000, 0x01040000, + 0x20040080, 0x00040000, 0x01000080, 0x20040080, + 0x21000080, 0x21040000, 0x00040080, 0x20000000, + 0x01000000, 0x20040000, 0x20040000, 0x00000000, + 0x20000080, 0x21040080, 0x21040080, 0x01000080, + 0x21040000, 0x20000080, 0x00000000, 0x21000000, + 0x01040080, 0x01000000, 0x21000000, 0x00040080, + 0x00040000, 0x21000080, 0x00000080, 0x01000000, + 0x20000000, 0x01040000, 0x21000080, 0x20040080, + 0x01000080, 0x20000000, 0x21040000, 0x01040080, + 0x20040080, 0x00000080, 0x01000000, 0x21040000, + 0x21040080, 0x00040080, 0x21000000, 0x21040080, + 0x01040000, 0x00000000, 0x20040000, 0x21000000, + 0x00040080, 0x01000080, 0x20000080, 0x00040000, + 0x00000000, 0x20040000, 0x01040080, 0x20000080 + ); + + /** + * Pre-permuted S-box6 + * + * @var array + * @access private + */ + var $sbox6 = array( + 0x10000008, 0x10200000, 0x00002000, 0x10202008, + 0x10200000, 0x00000008, 0x10202008, 0x00200000, + 0x10002000, 0x00202008, 0x00200000, 0x10000008, + 0x00200008, 0x10002000, 0x10000000, 0x00002008, + 0x00000000, 0x00200008, 0x10002008, 0x00002000, + 0x00202000, 0x10002008, 0x00000008, 0x10200008, + 0x10200008, 0x00000000, 0x00202008, 0x10202000, + 0x00002008, 0x00202000, 0x10202000, 0x10000000, + 0x10002000, 0x00000008, 0x10200008, 0x00202000, + 0x10202008, 0x00200000, 0x00002008, 0x10000008, + 0x00200000, 0x10002000, 0x10000000, 0x00002008, + 0x10000008, 0x10202008, 0x00202000, 0x10200000, + 0x00202008, 0x10202000, 0x00000000, 0x10200008, + 0x00000008, 0x00002000, 0x10200000, 0x00202008, + 0x00002000, 0x00200008, 0x10002008, 0x00000000, + 0x10202000, 0x10000000, 0x00200008, 0x10002008 + ); + + /** + * Pre-permuted S-box7 + * + * @var array + * @access private + */ + var $sbox7 = array( + 0x00100000, 0x02100001, 0x02000401, 0x00000000, + 0x00000400, 0x02000401, 0x00100401, 0x02100400, + 0x02100401, 0x00100000, 0x00000000, 0x02000001, + 0x00000001, 0x02000000, 0x02100001, 0x00000401, + 0x02000400, 0x00100401, 0x00100001, 0x02000400, + 0x02000001, 0x02100000, 0x02100400, 0x00100001, + 0x02100000, 0x00000400, 0x00000401, 0x02100401, + 0x00100400, 0x00000001, 0x02000000, 0x00100400, + 0x02000000, 0x00100400, 0x00100000, 0x02000401, + 0x02000401, 0x02100001, 0x02100001, 0x00000001, + 0x00100001, 0x02000000, 0x02000400, 0x00100000, + 0x02100400, 0x00000401, 0x00100401, 0x02100400, + 0x00000401, 0x02000001, 0x02100401, 0x02100000, + 0x00100400, 0x00000000, 0x00000001, 0x02100401, + 0x00000000, 0x00100401, 0x02100000, 0x00000400, + 0x02000001, 0x02000400, 0x00000400, 0x00100001 + ); + + /** + * Pre-permuted S-box8 + * + * @var array + * @access private + */ + var $sbox8 = array( + 0x08000820, 0x00000800, 0x00020000, 0x08020820, + 0x08000000, 0x08000820, 0x00000020, 0x08000000, + 0x00020020, 0x08020000, 0x08020820, 0x00020800, + 0x08020800, 0x00020820, 0x00000800, 0x00000020, + 0x08020000, 0x08000020, 0x08000800, 0x00000820, + 0x00020800, 0x00020020, 0x08020020, 0x08020800, + 0x00000820, 0x00000000, 0x00000000, 0x08020020, + 0x08000020, 0x08000800, 0x00020820, 0x00020000, + 0x00020820, 0x00020000, 0x08020800, 0x00000800, + 0x00000020, 0x08020020, 0x00000800, 0x00020820, + 0x08000800, 0x00000020, 0x08000020, 0x08020000, + 0x08020020, 0x08000000, 0x00020000, 0x08000820, + 0x00000000, 0x08020820, 0x00020020, 0x08000020, + 0x08020000, 0x08000800, 0x08000820, 0x00000000, + 0x08020820, 0x00020800, 0x00020800, 0x00000820, + 0x00000820, 0x00020020, 0x08000000, 0x08020800 + ); + + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() + * + * @see \phpseclib\Crypt\Base::isValidEngine() + * @param int $engine + * @access public + * @return bool + */ + function isValidEngine($engine) + { + if ($this->key_length_max == 8) { + if ($engine == self::ENGINE_OPENSSL) { + // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1 + // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider" + // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not + if (version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) { + return false; + } + $this->cipher_name_openssl_ecb = 'des-ecb'; + $this->cipher_name_openssl = 'des-' . $this->_openssl_translate_mode(); + } + } + + return parent::isValidEngine($engine); + } + + /** + * Sets the key. + * + * Keys can be of any length. DES, itself, uses 64-bit keys (eg. strlen($key) == 8), however, we + * only use the first eight, if $key has more then eight characters in it, and pad $key with the + * null byte if it is less then eight characters long. + * + * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. + * + * If the key is not explicitly set, it'll be assumed to be all zero's. + * + * @see \phpseclib\Crypt\Base::setKey() + * @access public + * @param string $key + */ + function setKey($key) + { + // We check/cut here only up to max length of the key. + // Key padding to the proper length will be done in _setupKey() + if (strlen($key) > $this->key_length_max) { + $key = substr($key, 0, $this->key_length_max); + } + + // Sets the key + parent::setKey($key); + } + + /** + * Encrypts a block + * + * @see \phpseclib\Crypt\Base::_encryptBlock() + * @see \phpseclib\Crypt\Base::encrypt() + * @see self::encrypt() + * @access private + * @param string $in + * @return string + */ + function _encryptBlock($in) + { + return $this->_processBlock($in, self::ENCRYPT); + } + + /** + * Decrypts a block + * + * @see \phpseclib\Crypt\Base::_decryptBlock() + * @see \phpseclib\Crypt\Base::decrypt() + * @see self::decrypt() + * @access private + * @param string $in + * @return string + */ + function _decryptBlock($in) + { + return $this->_processBlock($in, self::DECRYPT); + } + + /** + * Encrypts or decrypts a 64-bit block + * + * $mode should be either self::ENCRYPT or self::DECRYPT. See + * {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general + * idea of what this function does. + * + * @see self::_encryptBlock() + * @see self::_decryptBlock() + * @access private + * @param string $block + * @param int $mode + * @return string + */ + function _processBlock($block, $mode) + { + static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip; + if (!$sbox1) { + $sbox1 = array_map("intval", $this->sbox1); + $sbox2 = array_map("intval", $this->sbox2); + $sbox3 = array_map("intval", $this->sbox3); + $sbox4 = array_map("intval", $this->sbox4); + $sbox5 = array_map("intval", $this->sbox5); + $sbox6 = array_map("intval", $this->sbox6); + $sbox7 = array_map("intval", $this->sbox7); + $sbox8 = array_map("intval", $this->sbox8); + /* Merge $shuffle with $[inv]ipmap */ + for ($i = 0; $i < 256; ++$i) { + $shuffleip[] = $this->shuffle[$this->ipmap[$i]]; + $shuffleinvip[] = $this->shuffle[$this->invipmap[$i]]; + } + } + + $keys = $this->keys[$mode]; + $ki = -1; + + // Do the initial IP permutation. + $t = unpack('Nl/Nr', $block); + list($l, $r) = array($t['l'], $t['r']); + $block = ($shuffleip[ $r & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | + ($shuffleip[($r >> 8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | + ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | + ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | + ($shuffleip[ $l & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | + ($shuffleip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | + ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | + ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01"); + + // Extract L0 and R0. + $t = unpack('Nl/Nr', $block); + list($l, $r) = array($t['l'], $t['r']); + + for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) { + // Perform the 16 steps. + for ($i = 0; $i < 16; $i++) { + // start of "the Feistel (F) function" - see the following URL: + // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png + // Merge key schedule. + $b1 = (($r >> 3) & 0x1FFFFFFF) ^ ($r << 29) ^ $keys[++$ki]; + $b2 = (($r >> 31) & 0x00000001) ^ ($r << 1) ^ $keys[++$ki]; + + // S-box indexing. + $t = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^ + $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^ + $sbox5[($b1 >> 8) & 0x3F] ^ $sbox6[($b2 >> 8) & 0x3F] ^ + $sbox7[ $b1 & 0x3F] ^ $sbox8[ $b2 & 0x3F] ^ $l; + // end of "the Feistel (F) function" + + $l = $r; + $r = $t; + } + + // Last step should not permute L & R. + $t = $l; + $l = $r; + $r = $t; + } + + // Perform the inverse IP permutation. + return ($shuffleinvip[($r >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | + ($shuffleinvip[($l >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | + ($shuffleinvip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | + ($shuffleinvip[($l >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | + ($shuffleinvip[($r >> 8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | + ($shuffleinvip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | + ($shuffleinvip[ $r & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | + ($shuffleinvip[ $l & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01"); + } + + /** + * Creates the key schedule + * + * @see \phpseclib\Crypt\Base::_setupKey() + * @access private + */ + function _setupKey() + { + if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->des_rounds === $this->kl['des_rounds']) { + // already expanded + return; + } + $this->kl = array('key' => $this->key, 'des_rounds' => $this->des_rounds); + + static $shifts = array( // number of key bits shifted per round + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 + ); + + static $pc1map = array( + 0x00, 0x00, 0x08, 0x08, 0x04, 0x04, 0x0C, 0x0C, + 0x02, 0x02, 0x0A, 0x0A, 0x06, 0x06, 0x0E, 0x0E, + 0x10, 0x10, 0x18, 0x18, 0x14, 0x14, 0x1C, 0x1C, + 0x12, 0x12, 0x1A, 0x1A, 0x16, 0x16, 0x1E, 0x1E, + 0x20, 0x20, 0x28, 0x28, 0x24, 0x24, 0x2C, 0x2C, + 0x22, 0x22, 0x2A, 0x2A, 0x26, 0x26, 0x2E, 0x2E, + 0x30, 0x30, 0x38, 0x38, 0x34, 0x34, 0x3C, 0x3C, + 0x32, 0x32, 0x3A, 0x3A, 0x36, 0x36, 0x3E, 0x3E, + 0x40, 0x40, 0x48, 0x48, 0x44, 0x44, 0x4C, 0x4C, + 0x42, 0x42, 0x4A, 0x4A, 0x46, 0x46, 0x4E, 0x4E, + 0x50, 0x50, 0x58, 0x58, 0x54, 0x54, 0x5C, 0x5C, + 0x52, 0x52, 0x5A, 0x5A, 0x56, 0x56, 0x5E, 0x5E, + 0x60, 0x60, 0x68, 0x68, 0x64, 0x64, 0x6C, 0x6C, + 0x62, 0x62, 0x6A, 0x6A, 0x66, 0x66, 0x6E, 0x6E, + 0x70, 0x70, 0x78, 0x78, 0x74, 0x74, 0x7C, 0x7C, + 0x72, 0x72, 0x7A, 0x7A, 0x76, 0x76, 0x7E, 0x7E, + 0x80, 0x80, 0x88, 0x88, 0x84, 0x84, 0x8C, 0x8C, + 0x82, 0x82, 0x8A, 0x8A, 0x86, 0x86, 0x8E, 0x8E, + 0x90, 0x90, 0x98, 0x98, 0x94, 0x94, 0x9C, 0x9C, + 0x92, 0x92, 0x9A, 0x9A, 0x96, 0x96, 0x9E, 0x9E, + 0xA0, 0xA0, 0xA8, 0xA8, 0xA4, 0xA4, 0xAC, 0xAC, + 0xA2, 0xA2, 0xAA, 0xAA, 0xA6, 0xA6, 0xAE, 0xAE, + 0xB0, 0xB0, 0xB8, 0xB8, 0xB4, 0xB4, 0xBC, 0xBC, + 0xB2, 0xB2, 0xBA, 0xBA, 0xB6, 0xB6, 0xBE, 0xBE, + 0xC0, 0xC0, 0xC8, 0xC8, 0xC4, 0xC4, 0xCC, 0xCC, + 0xC2, 0xC2, 0xCA, 0xCA, 0xC6, 0xC6, 0xCE, 0xCE, + 0xD0, 0xD0, 0xD8, 0xD8, 0xD4, 0xD4, 0xDC, 0xDC, + 0xD2, 0xD2, 0xDA, 0xDA, 0xD6, 0xD6, 0xDE, 0xDE, + 0xE0, 0xE0, 0xE8, 0xE8, 0xE4, 0xE4, 0xEC, 0xEC, + 0xE2, 0xE2, 0xEA, 0xEA, 0xE6, 0xE6, 0xEE, 0xEE, + 0xF0, 0xF0, 0xF8, 0xF8, 0xF4, 0xF4, 0xFC, 0xFC, + 0xF2, 0xF2, 0xFA, 0xFA, 0xF6, 0xF6, 0xFE, 0xFE + ); + + // Mapping tables for the PC-2 transformation. + static $pc2mapc1 = array( + 0x00000000, 0x00000400, 0x00200000, 0x00200400, + 0x00000001, 0x00000401, 0x00200001, 0x00200401, + 0x02000000, 0x02000400, 0x02200000, 0x02200400, + 0x02000001, 0x02000401, 0x02200001, 0x02200401 + ); + static $pc2mapc2 = array( + 0x00000000, 0x00000800, 0x08000000, 0x08000800, + 0x00010000, 0x00010800, 0x08010000, 0x08010800, + 0x00000000, 0x00000800, 0x08000000, 0x08000800, + 0x00010000, 0x00010800, 0x08010000, 0x08010800, + 0x00000100, 0x00000900, 0x08000100, 0x08000900, + 0x00010100, 0x00010900, 0x08010100, 0x08010900, + 0x00000100, 0x00000900, 0x08000100, 0x08000900, + 0x00010100, 0x00010900, 0x08010100, 0x08010900, + 0x00000010, 0x00000810, 0x08000010, 0x08000810, + 0x00010010, 0x00010810, 0x08010010, 0x08010810, + 0x00000010, 0x00000810, 0x08000010, 0x08000810, + 0x00010010, 0x00010810, 0x08010010, 0x08010810, + 0x00000110, 0x00000910, 0x08000110, 0x08000910, + 0x00010110, 0x00010910, 0x08010110, 0x08010910, + 0x00000110, 0x00000910, 0x08000110, 0x08000910, + 0x00010110, 0x00010910, 0x08010110, 0x08010910, + 0x00040000, 0x00040800, 0x08040000, 0x08040800, + 0x00050000, 0x00050800, 0x08050000, 0x08050800, + 0x00040000, 0x00040800, 0x08040000, 0x08040800, + 0x00050000, 0x00050800, 0x08050000, 0x08050800, + 0x00040100, 0x00040900, 0x08040100, 0x08040900, + 0x00050100, 0x00050900, 0x08050100, 0x08050900, + 0x00040100, 0x00040900, 0x08040100, 0x08040900, + 0x00050100, 0x00050900, 0x08050100, 0x08050900, + 0x00040010, 0x00040810, 0x08040010, 0x08040810, + 0x00050010, 0x00050810, 0x08050010, 0x08050810, + 0x00040010, 0x00040810, 0x08040010, 0x08040810, + 0x00050010, 0x00050810, 0x08050010, 0x08050810, + 0x00040110, 0x00040910, 0x08040110, 0x08040910, + 0x00050110, 0x00050910, 0x08050110, 0x08050910, + 0x00040110, 0x00040910, 0x08040110, 0x08040910, + 0x00050110, 0x00050910, 0x08050110, 0x08050910, + 0x01000000, 0x01000800, 0x09000000, 0x09000800, + 0x01010000, 0x01010800, 0x09010000, 0x09010800, + 0x01000000, 0x01000800, 0x09000000, 0x09000800, + 0x01010000, 0x01010800, 0x09010000, 0x09010800, + 0x01000100, 0x01000900, 0x09000100, 0x09000900, + 0x01010100, 0x01010900, 0x09010100, 0x09010900, + 0x01000100, 0x01000900, 0x09000100, 0x09000900, + 0x01010100, 0x01010900, 0x09010100, 0x09010900, + 0x01000010, 0x01000810, 0x09000010, 0x09000810, + 0x01010010, 0x01010810, 0x09010010, 0x09010810, + 0x01000010, 0x01000810, 0x09000010, 0x09000810, + 0x01010010, 0x01010810, 0x09010010, 0x09010810, + 0x01000110, 0x01000910, 0x09000110, 0x09000910, + 0x01010110, 0x01010910, 0x09010110, 0x09010910, + 0x01000110, 0x01000910, 0x09000110, 0x09000910, + 0x01010110, 0x01010910, 0x09010110, 0x09010910, + 0x01040000, 0x01040800, 0x09040000, 0x09040800, + 0x01050000, 0x01050800, 0x09050000, 0x09050800, + 0x01040000, 0x01040800, 0x09040000, 0x09040800, + 0x01050000, 0x01050800, 0x09050000, 0x09050800, + 0x01040100, 0x01040900, 0x09040100, 0x09040900, + 0x01050100, 0x01050900, 0x09050100, 0x09050900, + 0x01040100, 0x01040900, 0x09040100, 0x09040900, + 0x01050100, 0x01050900, 0x09050100, 0x09050900, + 0x01040010, 0x01040810, 0x09040010, 0x09040810, + 0x01050010, 0x01050810, 0x09050010, 0x09050810, + 0x01040010, 0x01040810, 0x09040010, 0x09040810, + 0x01050010, 0x01050810, 0x09050010, 0x09050810, + 0x01040110, 0x01040910, 0x09040110, 0x09040910, + 0x01050110, 0x01050910, 0x09050110, 0x09050910, + 0x01040110, 0x01040910, 0x09040110, 0x09040910, + 0x01050110, 0x01050910, 0x09050110, 0x09050910 + ); + static $pc2mapc3 = array( + 0x00000000, 0x00000004, 0x00001000, 0x00001004, + 0x00000000, 0x00000004, 0x00001000, 0x00001004, + 0x10000000, 0x10000004, 0x10001000, 0x10001004, + 0x10000000, 0x10000004, 0x10001000, 0x10001004, + 0x00000020, 0x00000024, 0x00001020, 0x00001024, + 0x00000020, 0x00000024, 0x00001020, 0x00001024, + 0x10000020, 0x10000024, 0x10001020, 0x10001024, + 0x10000020, 0x10000024, 0x10001020, 0x10001024, + 0x00080000, 0x00080004, 0x00081000, 0x00081004, + 0x00080000, 0x00080004, 0x00081000, 0x00081004, + 0x10080000, 0x10080004, 0x10081000, 0x10081004, + 0x10080000, 0x10080004, 0x10081000, 0x10081004, + 0x00080020, 0x00080024, 0x00081020, 0x00081024, + 0x00080020, 0x00080024, 0x00081020, 0x00081024, + 0x10080020, 0x10080024, 0x10081020, 0x10081024, + 0x10080020, 0x10080024, 0x10081020, 0x10081024, + 0x20000000, 0x20000004, 0x20001000, 0x20001004, + 0x20000000, 0x20000004, 0x20001000, 0x20001004, + 0x30000000, 0x30000004, 0x30001000, 0x30001004, + 0x30000000, 0x30000004, 0x30001000, 0x30001004, + 0x20000020, 0x20000024, 0x20001020, 0x20001024, + 0x20000020, 0x20000024, 0x20001020, 0x20001024, + 0x30000020, 0x30000024, 0x30001020, 0x30001024, + 0x30000020, 0x30000024, 0x30001020, 0x30001024, + 0x20080000, 0x20080004, 0x20081000, 0x20081004, + 0x20080000, 0x20080004, 0x20081000, 0x20081004, + 0x30080000, 0x30080004, 0x30081000, 0x30081004, + 0x30080000, 0x30080004, 0x30081000, 0x30081004, + 0x20080020, 0x20080024, 0x20081020, 0x20081024, + 0x20080020, 0x20080024, 0x20081020, 0x20081024, + 0x30080020, 0x30080024, 0x30081020, 0x30081024, + 0x30080020, 0x30080024, 0x30081020, 0x30081024, + 0x00000002, 0x00000006, 0x00001002, 0x00001006, + 0x00000002, 0x00000006, 0x00001002, 0x00001006, + 0x10000002, 0x10000006, 0x10001002, 0x10001006, + 0x10000002, 0x10000006, 0x10001002, 0x10001006, + 0x00000022, 0x00000026, 0x00001022, 0x00001026, + 0x00000022, 0x00000026, 0x00001022, 0x00001026, + 0x10000022, 0x10000026, 0x10001022, 0x10001026, + 0x10000022, 0x10000026, 0x10001022, 0x10001026, + 0x00080002, 0x00080006, 0x00081002, 0x00081006, + 0x00080002, 0x00080006, 0x00081002, 0x00081006, + 0x10080002, 0x10080006, 0x10081002, 0x10081006, + 0x10080002, 0x10080006, 0x10081002, 0x10081006, + 0x00080022, 0x00080026, 0x00081022, 0x00081026, + 0x00080022, 0x00080026, 0x00081022, 0x00081026, + 0x10080022, 0x10080026, 0x10081022, 0x10081026, + 0x10080022, 0x10080026, 0x10081022, 0x10081026, + 0x20000002, 0x20000006, 0x20001002, 0x20001006, + 0x20000002, 0x20000006, 0x20001002, 0x20001006, + 0x30000002, 0x30000006, 0x30001002, 0x30001006, + 0x30000002, 0x30000006, 0x30001002, 0x30001006, + 0x20000022, 0x20000026, 0x20001022, 0x20001026, + 0x20000022, 0x20000026, 0x20001022, 0x20001026, + 0x30000022, 0x30000026, 0x30001022, 0x30001026, + 0x30000022, 0x30000026, 0x30001022, 0x30001026, + 0x20080002, 0x20080006, 0x20081002, 0x20081006, + 0x20080002, 0x20080006, 0x20081002, 0x20081006, + 0x30080002, 0x30080006, 0x30081002, 0x30081006, + 0x30080002, 0x30080006, 0x30081002, 0x30081006, + 0x20080022, 0x20080026, 0x20081022, 0x20081026, + 0x20080022, 0x20080026, 0x20081022, 0x20081026, + 0x30080022, 0x30080026, 0x30081022, 0x30081026, + 0x30080022, 0x30080026, 0x30081022, 0x30081026 + ); + static $pc2mapc4 = array( + 0x00000000, 0x00100000, 0x00000008, 0x00100008, + 0x00000200, 0x00100200, 0x00000208, 0x00100208, + 0x00000000, 0x00100000, 0x00000008, 0x00100008, + 0x00000200, 0x00100200, 0x00000208, 0x00100208, + 0x04000000, 0x04100000, 0x04000008, 0x04100008, + 0x04000200, 0x04100200, 0x04000208, 0x04100208, + 0x04000000, 0x04100000, 0x04000008, 0x04100008, + 0x04000200, 0x04100200, 0x04000208, 0x04100208, + 0x00002000, 0x00102000, 0x00002008, 0x00102008, + 0x00002200, 0x00102200, 0x00002208, 0x00102208, + 0x00002000, 0x00102000, 0x00002008, 0x00102008, + 0x00002200, 0x00102200, 0x00002208, 0x00102208, + 0x04002000, 0x04102000, 0x04002008, 0x04102008, + 0x04002200, 0x04102200, 0x04002208, 0x04102208, + 0x04002000, 0x04102000, 0x04002008, 0x04102008, + 0x04002200, 0x04102200, 0x04002208, 0x04102208, + 0x00000000, 0x00100000, 0x00000008, 0x00100008, + 0x00000200, 0x00100200, 0x00000208, 0x00100208, + 0x00000000, 0x00100000, 0x00000008, 0x00100008, + 0x00000200, 0x00100200, 0x00000208, 0x00100208, + 0x04000000, 0x04100000, 0x04000008, 0x04100008, + 0x04000200, 0x04100200, 0x04000208, 0x04100208, + 0x04000000, 0x04100000, 0x04000008, 0x04100008, + 0x04000200, 0x04100200, 0x04000208, 0x04100208, + 0x00002000, 0x00102000, 0x00002008, 0x00102008, + 0x00002200, 0x00102200, 0x00002208, 0x00102208, + 0x00002000, 0x00102000, 0x00002008, 0x00102008, + 0x00002200, 0x00102200, 0x00002208, 0x00102208, + 0x04002000, 0x04102000, 0x04002008, 0x04102008, + 0x04002200, 0x04102200, 0x04002208, 0x04102208, + 0x04002000, 0x04102000, 0x04002008, 0x04102008, + 0x04002200, 0x04102200, 0x04002208, 0x04102208, + 0x00020000, 0x00120000, 0x00020008, 0x00120008, + 0x00020200, 0x00120200, 0x00020208, 0x00120208, + 0x00020000, 0x00120000, 0x00020008, 0x00120008, + 0x00020200, 0x00120200, 0x00020208, 0x00120208, + 0x04020000, 0x04120000, 0x04020008, 0x04120008, + 0x04020200, 0x04120200, 0x04020208, 0x04120208, + 0x04020000, 0x04120000, 0x04020008, 0x04120008, + 0x04020200, 0x04120200, 0x04020208, 0x04120208, + 0x00022000, 0x00122000, 0x00022008, 0x00122008, + 0x00022200, 0x00122200, 0x00022208, 0x00122208, + 0x00022000, 0x00122000, 0x00022008, 0x00122008, + 0x00022200, 0x00122200, 0x00022208, 0x00122208, + 0x04022000, 0x04122000, 0x04022008, 0x04122008, + 0x04022200, 0x04122200, 0x04022208, 0x04122208, + 0x04022000, 0x04122000, 0x04022008, 0x04122008, + 0x04022200, 0x04122200, 0x04022208, 0x04122208, + 0x00020000, 0x00120000, 0x00020008, 0x00120008, + 0x00020200, 0x00120200, 0x00020208, 0x00120208, + 0x00020000, 0x00120000, 0x00020008, 0x00120008, + 0x00020200, 0x00120200, 0x00020208, 0x00120208, + 0x04020000, 0x04120000, 0x04020008, 0x04120008, + 0x04020200, 0x04120200, 0x04020208, 0x04120208, + 0x04020000, 0x04120000, 0x04020008, 0x04120008, + 0x04020200, 0x04120200, 0x04020208, 0x04120208, + 0x00022000, 0x00122000, 0x00022008, 0x00122008, + 0x00022200, 0x00122200, 0x00022208, 0x00122208, + 0x00022000, 0x00122000, 0x00022008, 0x00122008, + 0x00022200, 0x00122200, 0x00022208, 0x00122208, + 0x04022000, 0x04122000, 0x04022008, 0x04122008, + 0x04022200, 0x04122200, 0x04022208, 0x04122208, + 0x04022000, 0x04122000, 0x04022008, 0x04122008, + 0x04022200, 0x04122200, 0x04022208, 0x04122208 + ); + static $pc2mapd1 = array( + 0x00000000, 0x00000001, 0x08000000, 0x08000001, + 0x00200000, 0x00200001, 0x08200000, 0x08200001, + 0x00000002, 0x00000003, 0x08000002, 0x08000003, + 0x00200002, 0x00200003, 0x08200002, 0x08200003 + ); + static $pc2mapd2 = array( + 0x00000000, 0x00100000, 0x00000800, 0x00100800, + 0x00000000, 0x00100000, 0x00000800, 0x00100800, + 0x04000000, 0x04100000, 0x04000800, 0x04100800, + 0x04000000, 0x04100000, 0x04000800, 0x04100800, + 0x00000004, 0x00100004, 0x00000804, 0x00100804, + 0x00000004, 0x00100004, 0x00000804, 0x00100804, + 0x04000004, 0x04100004, 0x04000804, 0x04100804, + 0x04000004, 0x04100004, 0x04000804, 0x04100804, + 0x00000000, 0x00100000, 0x00000800, 0x00100800, + 0x00000000, 0x00100000, 0x00000800, 0x00100800, + 0x04000000, 0x04100000, 0x04000800, 0x04100800, + 0x04000000, 0x04100000, 0x04000800, 0x04100800, + 0x00000004, 0x00100004, 0x00000804, 0x00100804, + 0x00000004, 0x00100004, 0x00000804, 0x00100804, + 0x04000004, 0x04100004, 0x04000804, 0x04100804, + 0x04000004, 0x04100004, 0x04000804, 0x04100804, + 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, + 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, + 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, + 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, + 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, + 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, + 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, + 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, + 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, + 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, + 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, + 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, + 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, + 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, + 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, + 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, + 0x00020000, 0x00120000, 0x00020800, 0x00120800, + 0x00020000, 0x00120000, 0x00020800, 0x00120800, + 0x04020000, 0x04120000, 0x04020800, 0x04120800, + 0x04020000, 0x04120000, 0x04020800, 0x04120800, + 0x00020004, 0x00120004, 0x00020804, 0x00120804, + 0x00020004, 0x00120004, 0x00020804, 0x00120804, + 0x04020004, 0x04120004, 0x04020804, 0x04120804, + 0x04020004, 0x04120004, 0x04020804, 0x04120804, + 0x00020000, 0x00120000, 0x00020800, 0x00120800, + 0x00020000, 0x00120000, 0x00020800, 0x00120800, + 0x04020000, 0x04120000, 0x04020800, 0x04120800, + 0x04020000, 0x04120000, 0x04020800, 0x04120800, + 0x00020004, 0x00120004, 0x00020804, 0x00120804, + 0x00020004, 0x00120004, 0x00020804, 0x00120804, + 0x04020004, 0x04120004, 0x04020804, 0x04120804, + 0x04020004, 0x04120004, 0x04020804, 0x04120804, + 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, + 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, + 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, + 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, + 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, + 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, + 0x04020204, 0x04120204, 0x04020A04, 0x04120A04, + 0x04020204, 0x04120204, 0x04020A04, 0x04120A04, + 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, + 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, + 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, + 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, + 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, + 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, + 0x04020204, 0x04120204, 0x04020A04, 0x04120A04, + 0x04020204, 0x04120204, 0x04020A04, 0x04120A04 + ); + static $pc2mapd3 = array( + 0x00000000, 0x00010000, 0x02000000, 0x02010000, + 0x00000020, 0x00010020, 0x02000020, 0x02010020, + 0x00040000, 0x00050000, 0x02040000, 0x02050000, + 0x00040020, 0x00050020, 0x02040020, 0x02050020, + 0x00002000, 0x00012000, 0x02002000, 0x02012000, + 0x00002020, 0x00012020, 0x02002020, 0x02012020, + 0x00042000, 0x00052000, 0x02042000, 0x02052000, + 0x00042020, 0x00052020, 0x02042020, 0x02052020, + 0x00000000, 0x00010000, 0x02000000, 0x02010000, + 0x00000020, 0x00010020, 0x02000020, 0x02010020, + 0x00040000, 0x00050000, 0x02040000, 0x02050000, + 0x00040020, 0x00050020, 0x02040020, 0x02050020, + 0x00002000, 0x00012000, 0x02002000, 0x02012000, + 0x00002020, 0x00012020, 0x02002020, 0x02012020, + 0x00042000, 0x00052000, 0x02042000, 0x02052000, + 0x00042020, 0x00052020, 0x02042020, 0x02052020, + 0x00000010, 0x00010010, 0x02000010, 0x02010010, + 0x00000030, 0x00010030, 0x02000030, 0x02010030, + 0x00040010, 0x00050010, 0x02040010, 0x02050010, + 0x00040030, 0x00050030, 0x02040030, 0x02050030, + 0x00002010, 0x00012010, 0x02002010, 0x02012010, + 0x00002030, 0x00012030, 0x02002030, 0x02012030, + 0x00042010, 0x00052010, 0x02042010, 0x02052010, + 0x00042030, 0x00052030, 0x02042030, 0x02052030, + 0x00000010, 0x00010010, 0x02000010, 0x02010010, + 0x00000030, 0x00010030, 0x02000030, 0x02010030, + 0x00040010, 0x00050010, 0x02040010, 0x02050010, + 0x00040030, 0x00050030, 0x02040030, 0x02050030, + 0x00002010, 0x00012010, 0x02002010, 0x02012010, + 0x00002030, 0x00012030, 0x02002030, 0x02012030, + 0x00042010, 0x00052010, 0x02042010, 0x02052010, + 0x00042030, 0x00052030, 0x02042030, 0x02052030, + 0x20000000, 0x20010000, 0x22000000, 0x22010000, + 0x20000020, 0x20010020, 0x22000020, 0x22010020, + 0x20040000, 0x20050000, 0x22040000, 0x22050000, + 0x20040020, 0x20050020, 0x22040020, 0x22050020, + 0x20002000, 0x20012000, 0x22002000, 0x22012000, + 0x20002020, 0x20012020, 0x22002020, 0x22012020, + 0x20042000, 0x20052000, 0x22042000, 0x22052000, + 0x20042020, 0x20052020, 0x22042020, 0x22052020, + 0x20000000, 0x20010000, 0x22000000, 0x22010000, + 0x20000020, 0x20010020, 0x22000020, 0x22010020, + 0x20040000, 0x20050000, 0x22040000, 0x22050000, + 0x20040020, 0x20050020, 0x22040020, 0x22050020, + 0x20002000, 0x20012000, 0x22002000, 0x22012000, + 0x20002020, 0x20012020, 0x22002020, 0x22012020, + 0x20042000, 0x20052000, 0x22042000, 0x22052000, + 0x20042020, 0x20052020, 0x22042020, 0x22052020, + 0x20000010, 0x20010010, 0x22000010, 0x22010010, + 0x20000030, 0x20010030, 0x22000030, 0x22010030, + 0x20040010, 0x20050010, 0x22040010, 0x22050010, + 0x20040030, 0x20050030, 0x22040030, 0x22050030, + 0x20002010, 0x20012010, 0x22002010, 0x22012010, + 0x20002030, 0x20012030, 0x22002030, 0x22012030, + 0x20042010, 0x20052010, 0x22042010, 0x22052010, + 0x20042030, 0x20052030, 0x22042030, 0x22052030, + 0x20000010, 0x20010010, 0x22000010, 0x22010010, + 0x20000030, 0x20010030, 0x22000030, 0x22010030, + 0x20040010, 0x20050010, 0x22040010, 0x22050010, + 0x20040030, 0x20050030, 0x22040030, 0x22050030, + 0x20002010, 0x20012010, 0x22002010, 0x22012010, + 0x20002030, 0x20012030, 0x22002030, 0x22012030, + 0x20042010, 0x20052010, 0x22042010, 0x22052010, + 0x20042030, 0x20052030, 0x22042030, 0x22052030 + ); + static $pc2mapd4 = array( + 0x00000000, 0x00000400, 0x01000000, 0x01000400, + 0x00000000, 0x00000400, 0x01000000, 0x01000400, + 0x00000100, 0x00000500, 0x01000100, 0x01000500, + 0x00000100, 0x00000500, 0x01000100, 0x01000500, + 0x10000000, 0x10000400, 0x11000000, 0x11000400, + 0x10000000, 0x10000400, 0x11000000, 0x11000400, + 0x10000100, 0x10000500, 0x11000100, 0x11000500, + 0x10000100, 0x10000500, 0x11000100, 0x11000500, + 0x00080000, 0x00080400, 0x01080000, 0x01080400, + 0x00080000, 0x00080400, 0x01080000, 0x01080400, + 0x00080100, 0x00080500, 0x01080100, 0x01080500, + 0x00080100, 0x00080500, 0x01080100, 0x01080500, + 0x10080000, 0x10080400, 0x11080000, 0x11080400, + 0x10080000, 0x10080400, 0x11080000, 0x11080400, + 0x10080100, 0x10080500, 0x11080100, 0x11080500, + 0x10080100, 0x10080500, 0x11080100, 0x11080500, + 0x00000008, 0x00000408, 0x01000008, 0x01000408, + 0x00000008, 0x00000408, 0x01000008, 0x01000408, + 0x00000108, 0x00000508, 0x01000108, 0x01000508, + 0x00000108, 0x00000508, 0x01000108, 0x01000508, + 0x10000008, 0x10000408, 0x11000008, 0x11000408, + 0x10000008, 0x10000408, 0x11000008, 0x11000408, + 0x10000108, 0x10000508, 0x11000108, 0x11000508, + 0x10000108, 0x10000508, 0x11000108, 0x11000508, + 0x00080008, 0x00080408, 0x01080008, 0x01080408, + 0x00080008, 0x00080408, 0x01080008, 0x01080408, + 0x00080108, 0x00080508, 0x01080108, 0x01080508, + 0x00080108, 0x00080508, 0x01080108, 0x01080508, + 0x10080008, 0x10080408, 0x11080008, 0x11080408, + 0x10080008, 0x10080408, 0x11080008, 0x11080408, + 0x10080108, 0x10080508, 0x11080108, 0x11080508, + 0x10080108, 0x10080508, 0x11080108, 0x11080508, + 0x00001000, 0x00001400, 0x01001000, 0x01001400, + 0x00001000, 0x00001400, 0x01001000, 0x01001400, + 0x00001100, 0x00001500, 0x01001100, 0x01001500, + 0x00001100, 0x00001500, 0x01001100, 0x01001500, + 0x10001000, 0x10001400, 0x11001000, 0x11001400, + 0x10001000, 0x10001400, 0x11001000, 0x11001400, + 0x10001100, 0x10001500, 0x11001100, 0x11001500, + 0x10001100, 0x10001500, 0x11001100, 0x11001500, + 0x00081000, 0x00081400, 0x01081000, 0x01081400, + 0x00081000, 0x00081400, 0x01081000, 0x01081400, + 0x00081100, 0x00081500, 0x01081100, 0x01081500, + 0x00081100, 0x00081500, 0x01081100, 0x01081500, + 0x10081000, 0x10081400, 0x11081000, 0x11081400, + 0x10081000, 0x10081400, 0x11081000, 0x11081400, + 0x10081100, 0x10081500, 0x11081100, 0x11081500, + 0x10081100, 0x10081500, 0x11081100, 0x11081500, + 0x00001008, 0x00001408, 0x01001008, 0x01001408, + 0x00001008, 0x00001408, 0x01001008, 0x01001408, + 0x00001108, 0x00001508, 0x01001108, 0x01001508, + 0x00001108, 0x00001508, 0x01001108, 0x01001508, + 0x10001008, 0x10001408, 0x11001008, 0x11001408, + 0x10001008, 0x10001408, 0x11001008, 0x11001408, + 0x10001108, 0x10001508, 0x11001108, 0x11001508, + 0x10001108, 0x10001508, 0x11001108, 0x11001508, + 0x00081008, 0x00081408, 0x01081008, 0x01081408, + 0x00081008, 0x00081408, 0x01081008, 0x01081408, + 0x00081108, 0x00081508, 0x01081108, 0x01081508, + 0x00081108, 0x00081508, 0x01081108, 0x01081508, + 0x10081008, 0x10081408, 0x11081008, 0x11081408, + 0x10081008, 0x10081408, 0x11081008, 0x11081408, + 0x10081108, 0x10081508, 0x11081108, 0x11081508, + 0x10081108, 0x10081508, 0x11081108, 0x11081508 + ); + + $keys = array(); + for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) { + // pad the key and remove extra characters as appropriate. + $key = str_pad(substr($this->key, $des_round * 8, 8), 8, "\0"); + + // Perform the PC/1 transformation and compute C and D. + $t = unpack('Nl/Nr', $key); + list($l, $r) = array($t['l'], $t['r']); + $key = ($this->shuffle[$pc1map[ $r & 0xFF]] & "\x80\x80\x80\x80\x80\x80\x80\x00") | + ($this->shuffle[$pc1map[($r >> 8) & 0xFF]] & "\x40\x40\x40\x40\x40\x40\x40\x00") | + ($this->shuffle[$pc1map[($r >> 16) & 0xFF]] & "\x20\x20\x20\x20\x20\x20\x20\x00") | + ($this->shuffle[$pc1map[($r >> 24) & 0xFF]] & "\x10\x10\x10\x10\x10\x10\x10\x00") | + ($this->shuffle[$pc1map[ $l & 0xFF]] & "\x08\x08\x08\x08\x08\x08\x08\x00") | + ($this->shuffle[$pc1map[($l >> 8) & 0xFF]] & "\x04\x04\x04\x04\x04\x04\x04\x00") | + ($this->shuffle[$pc1map[($l >> 16) & 0xFF]] & "\x02\x02\x02\x02\x02\x02\x02\x00") | + ($this->shuffle[$pc1map[($l >> 24) & 0xFF]] & "\x01\x01\x01\x01\x01\x01\x01\x00"); + $key = unpack('Nc/Nd', $key); + $c = ( $key['c'] >> 4) & 0x0FFFFFFF; + $d = (($key['d'] >> 4) & 0x0FFFFFF0) | ($key['c'] & 0x0F); + + $keys[$des_round] = array( + self::ENCRYPT => array(), + self::DECRYPT => array_fill(0, 32, 0) + ); + for ($i = 0, $ki = 31; $i < 16; ++$i, $ki-= 2) { + $c <<= $shifts[$i]; + $c = ($c | ($c >> 28)) & 0x0FFFFFFF; + $d <<= $shifts[$i]; + $d = ($d | ($d >> 28)) & 0x0FFFFFFF; + + // Perform the PC-2 transformation. + $cp = $pc2mapc1[ $c >> 24 ] | $pc2mapc2[($c >> 16) & 0xFF] | + $pc2mapc3[($c >> 8) & 0xFF] | $pc2mapc4[ $c & 0xFF]; + $dp = $pc2mapd1[ $d >> 24 ] | $pc2mapd2[($d >> 16) & 0xFF] | + $pc2mapd3[($d >> 8) & 0xFF] | $pc2mapd4[ $d & 0xFF]; + + // Reorder: odd bytes/even bytes. Push the result in key schedule. + $val1 = ( $cp & intval(0xFF000000)) | (($cp << 8) & 0x00FF0000) | + (($dp >> 16) & 0x0000FF00) | (($dp >> 8) & 0x000000FF); + $val2 = (($cp << 8) & intval(0xFF000000)) | (($cp << 16) & 0x00FF0000) | + (($dp >> 8) & 0x0000FF00) | ( $dp & 0x000000FF); + $keys[$des_round][self::ENCRYPT][ ] = $val1; + $keys[$des_round][self::DECRYPT][$ki - 1] = $val1; + $keys[$des_round][self::ENCRYPT][ ] = $val2; + $keys[$des_round][self::DECRYPT][$ki ] = $val2; + } + } + + switch ($this->des_rounds) { + case 3: // 3DES keys + $this->keys = array( + self::ENCRYPT => array_merge( + $keys[0][self::ENCRYPT], + $keys[1][self::DECRYPT], + $keys[2][self::ENCRYPT] + ), + self::DECRYPT => array_merge( + $keys[2][self::DECRYPT], + $keys[1][self::ENCRYPT], + $keys[0][self::DECRYPT] + ) + ); + break; + // case 1: // DES keys + default: + $this->keys = array( + self::ENCRYPT => $keys[0][self::ENCRYPT], + self::DECRYPT => $keys[0][self::DECRYPT] + ); + } + } + + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see \phpseclib\Crypt\Base::_setupInlineCrypt() + * @access private + */ + function _setupInlineCrypt() + { + $lambda_functions =& self::_getLambdaFunctions(); + + // Engine configuration for: + // - DES ($des_rounds == 1) or + // - 3DES ($des_rounds == 3) + $des_rounds = $this->des_rounds; + + // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function. + // (Currently, for DES, one generated $lambda_function cost on php5.5@32bit ~135kb unfreeable mem and ~230kb on php5.5@64bit) + // (Currently, for TripleDES, one generated $lambda_function cost on php5.5@32bit ~240kb unfreeable mem and ~340kb on php5.5@64bit) + // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one + $gen_hi_opt_code = (bool)( count($lambda_functions) < 10 ); + + // Generation of a unique hash for our generated code + $code_hash = "Crypt_DES, $des_rounds, {$this->mode}"; + if ($gen_hi_opt_code) { + // For hi-optimized code, we create for each combination of + // $mode, $des_rounds and $this->key its own encrypt/decrypt function. + // After max 10 hi-optimized functions, we create generic + // (still very fast.. but not ultra) functions for each $mode/$des_rounds + // Currently 2 * 5 generic functions will be then max. possible. + $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); + } + + // Is there a re-usable $lambda_functions in there? If not, we have to create it. + if (!isset($lambda_functions[$code_hash])) { + // Init code for both, encrypt and decrypt. + $init_crypt = 'static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip; + if (!$sbox1) { + $sbox1 = array_map("intval", $self->sbox1); + $sbox2 = array_map("intval", $self->sbox2); + $sbox3 = array_map("intval", $self->sbox3); + $sbox4 = array_map("intval", $self->sbox4); + $sbox5 = array_map("intval", $self->sbox5); + $sbox6 = array_map("intval", $self->sbox6); + $sbox7 = array_map("intval", $self->sbox7); + $sbox8 = array_map("intval", $self->sbox8);' + /* Merge $shuffle with $[inv]ipmap */ . ' + for ($i = 0; $i < 256; ++$i) { + $shuffleip[] = $self->shuffle[$self->ipmap[$i]]; + $shuffleinvip[] = $self->shuffle[$self->invipmap[$i]]; + } + } + '; + + switch (true) { + case $gen_hi_opt_code: + // In Hi-optimized code mode, we use our [3]DES key schedule as hardcoded integers. + // No futher initialisation of the $keys schedule is necessary. + // That is the extra performance boost. + $k = array( + self::ENCRYPT => $this->keys[self::ENCRYPT], + self::DECRYPT => $this->keys[self::DECRYPT] + ); + $init_encrypt = ''; + $init_decrypt = ''; + break; + default: + // In generic optimized code mode, we have to use, as the best compromise [currently], + // our key schedule as $ke/$kd arrays. (with hardcoded indexes...) + $k = array( + self::ENCRYPT => array(), + self::DECRYPT => array() + ); + for ($i = 0, $c = count($this->keys[self::ENCRYPT]); $i < $c; ++$i) { + $k[self::ENCRYPT][$i] = '$ke[' . $i . ']'; + $k[self::DECRYPT][$i] = '$kd[' . $i . ']'; + } + $init_encrypt = '$ke = $self->keys[$self::ENCRYPT];'; + $init_decrypt = '$kd = $self->keys[$self::DECRYPT];'; + break; + } + + // Creating code for en- and decryption. + $crypt_block = array(); + foreach (array(self::ENCRYPT, self::DECRYPT) as $c) { + /* Do the initial IP permutation. */ + $crypt_block[$c] = ' + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + $in = unpack("N*", + ($shuffleip[ $r & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | + ($shuffleip[($r >> 8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | + ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | + ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | + ($shuffleip[ $l & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | + ($shuffleip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | + ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | + ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01") + ); + ' . /* Extract L0 and R0 */ ' + $l = $in[1]; + $r = $in[2]; + '; + + $l = '$l'; + $r = '$r'; + + // Perform DES or 3DES. + for ($ki = -1, $des_round = 0; $des_round < $des_rounds; ++$des_round) { + // Perform the 16 steps. + for ($i = 0; $i < 16; ++$i) { + // start of "the Feistel (F) function" - see the following URL: + // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png + // Merge key schedule. + $crypt_block[$c].= ' + $b1 = ((' . $r . ' >> 3) & 0x1FFFFFFF) ^ (' . $r . ' << 29) ^ ' . $k[$c][++$ki] . '; + $b2 = ((' . $r . ' >> 31) & 0x00000001) ^ (' . $r . ' << 1) ^ ' . $k[$c][++$ki] . ';' . + /* S-box indexing. */ + $l . ' = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^ + $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^ + $sbox5[($b1 >> 8) & 0x3F] ^ $sbox6[($b2 >> 8) & 0x3F] ^ + $sbox7[ $b1 & 0x3F] ^ $sbox8[ $b2 & 0x3F] ^ ' . $l . '; + '; + // end of "the Feistel (F) function" + + // swap L & R + list($l, $r) = array($r, $l); + } + list($l, $r) = array($r, $l); + } + + // Perform the inverse IP permutation. + $crypt_block[$c].= '$in = + ($shuffleinvip[($l >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | + ($shuffleinvip[($r >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | + ($shuffleinvip[($l >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | + ($shuffleinvip[($r >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | + ($shuffleinvip[($l >> 8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | + ($shuffleinvip[($r >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | + ($shuffleinvip[ $l & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | + ($shuffleinvip[ $r & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01"); + '; + } + + // Creates the inline-crypt function + $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( + array( + 'init_crypt' => $init_crypt, + 'init_encrypt' => $init_encrypt, + 'init_decrypt' => $init_decrypt, + 'encrypt_block' => $crypt_block[self::ENCRYPT], + 'decrypt_block' => $crypt_block[self::DECRYPT] + ) + ); + } + + // Set the inline-crypt function as callback in: $this->inline_crypt + $this->inline_crypt = $lambda_functions[$code_hash]; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php new file mode 100644 index 0000000..f7a47a2 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php @@ -0,0 +1,893 @@ + + * setKey('abcdefg'); + * + * echo base64_encode($hash->hash('abcdefg')); + * ?> + * + * + * @category Crypt + * @package Hash + * @author Jim Wigginton + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +use phpseclib\Math\BigInteger; + +/** + * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions. + * + * @package Hash + * @author Jim Wigginton + * @access public + */ +class Hash +{ + /**#@+ + * @access private + * @see \phpseclib\Crypt\Hash::__construct() + */ + /** + * Toggles the internal implementation + */ + const MODE_INTERNAL = 1; + /** + * Toggles the mhash() implementation, which has been deprecated on PHP 5.3.0+. + */ + const MODE_MHASH = 2; + /** + * Toggles the hash() implementation, which works on PHP 5.1.2+. + */ + const MODE_HASH = 3; + /**#@-*/ + + /** + * Hash Parameter + * + * @see self::setHash() + * @var int + * @access private + */ + var $hashParam; + + /** + * Byte-length of compression blocks / key (Internal HMAC) + * + * @see self::setAlgorithm() + * @var int + * @access private + */ + var $b; + + /** + * Byte-length of hash output (Internal HMAC) + * + * @see self::setHash() + * @var int + * @access private + */ + var $l = false; + + /** + * Hash Algorithm + * + * @see self::setHash() + * @var string + * @access private + */ + var $hash; + + /** + * Key + * + * @see self::setKey() + * @var string + * @access private + */ + var $key = false; + + /** + * Computed Key + * + * @see self::_computeKey() + * @var string + * @access private + */ + var $computedKey = false; + + /** + * Outer XOR (Internal HMAC) + * + * @see self::setKey() + * @var string + * @access private + */ + var $opad; + + /** + * Inner XOR (Internal HMAC) + * + * @see self::setKey() + * @var string + * @access private + */ + var $ipad; + + /** + * Engine + * + * @see self::setHash() + * @var string + * @access private + */ + var $engine; + + /** + * Default Constructor. + * + * @param string $hash + * @return \phpseclib\Crypt\Hash + * @access public + */ + function __construct($hash = 'sha1') + { + if (!defined('CRYPT_HASH_MODE')) { + switch (true) { + case extension_loaded('hash'): + define('CRYPT_HASH_MODE', self::MODE_HASH); + break; + case extension_loaded('mhash'): + define('CRYPT_HASH_MODE', self::MODE_MHASH); + break; + default: + define('CRYPT_HASH_MODE', self::MODE_INTERNAL); + } + } + + $this->setHash($hash); + } + + /** + * Sets the key for HMACs + * + * Keys can be of any length. + * + * @access public + * @param string $key + */ + function setKey($key = false) + { + $this->key = $key; + $this->_computeKey(); + } + + /** + * Pre-compute the key used by the HMAC + * + * Quoting http://tools.ietf.org/html/rfc2104#section-2, "Applications that use keys longer than B bytes + * will first hash the key using H and then use the resultant L byte string as the actual key to HMAC." + * + * As documented in https://www.reddit.com/r/PHP/comments/9nct2l/symfonypolyfill_hash_pbkdf2_correct_fix_for/ + * when doing an HMAC multiple times it's faster to compute the hash once instead of computing it during + * every call + * + * @access private + */ + function _computeKey() + { + if ($this->key === false) { + $this->computedKey = false; + return; + } + + if (strlen($this->key) <= $this->b) { + $this->computedKey = $this->key; + return; + } + + switch ($this->engine) { + case self::MODE_MHASH: + $this->computedKey = mhash($this->hash, $this->key); + break; + case self::MODE_HASH: + $this->computedKey = hash($this->hash, $this->key, true); + break; + case self::MODE_INTERNAL: + $this->computedKey = call_user_func($this->hash, $this->key); + } + } + + /** + * Gets the hash function. + * + * As set by the constructor or by the setHash() method. + * + * @access public + * @return string + */ + function getHash() + { + return $this->hashParam; + } + + /** + * Sets the hash function. + * + * @access public + * @param string $hash + */ + function setHash($hash) + { + $this->hashParam = $hash = strtolower($hash); + switch ($hash) { + case 'md5-96': + case 'sha1-96': + case 'sha256-96': + case 'sha512-96': + $hash = substr($hash, 0, -3); + $this->l = 12; // 96 / 8 = 12 + break; + case 'md2': + case 'md5': + $this->l = 16; + break; + case 'sha1': + $this->l = 20; + break; + case 'sha256': + $this->l = 32; + break; + case 'sha384': + $this->l = 48; + break; + case 'sha512': + $this->l = 64; + } + + switch ($hash) { + case 'md2-96': + case 'md2': + $this->b = 16; + case 'md5-96': + case 'sha1-96': + case 'sha224-96': + case 'sha256-96': + case 'md2': + case 'md5': + case 'sha1': + case 'sha224': + case 'sha256': + $this->b = 64; + break; + default: + $this->b = 128; + } + + switch ($hash) { + case 'md2': + $this->engine = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ? + self::MODE_HASH : self::MODE_INTERNAL; + break; + case 'sha384': + case 'sha512': + $this->engine = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE; + break; + default: + $this->engine = CRYPT_HASH_MODE; + } + + switch ($this->engine) { + case self::MODE_MHASH: + switch ($hash) { + case 'md5': + $this->hash = MHASH_MD5; + break; + case 'sha256': + $this->hash = MHASH_SHA256; + break; + case 'sha1': + default: + $this->hash = MHASH_SHA1; + } + $this->_computeKey(self::MODE_MHASH); + return; + case self::MODE_HASH: + switch ($hash) { + case 'md5': + $this->hash = 'md5'; + return; + case 'md2': + case 'sha256': + case 'sha384': + case 'sha512': + $this->hash = $hash; + return; + case 'sha1': + default: + $this->hash = 'sha1'; + } + $this->_computeKey(self::MODE_HASH); + return; + } + + switch ($hash) { + case 'md2': + $this->hash = array($this, '_md2'); + break; + case 'md5': + $this->hash = array($this, '_md5'); + break; + case 'sha256': + $this->hash = array($this, '_sha256'); + break; + case 'sha384': + case 'sha512': + $this->hash = array($this, '_sha512'); + break; + case 'sha1': + default: + $this->hash = array($this, '_sha1'); + } + + $this->ipad = str_repeat(chr(0x36), $this->b); + $this->opad = str_repeat(chr(0x5C), $this->b); + + $this->_computeKey(self::MODE_INTERNAL); + } + + /** + * Compute the HMAC. + * + * @access public + * @param string $text + * @return string + */ + function hash($text) + { + if (!empty($this->key) || is_string($this->key)) { + switch ($this->engine) { + case self::MODE_MHASH: + $output = mhash($this->hash, $text, $this->computedKey); + break; + case self::MODE_HASH: + $output = hash_hmac($this->hash, $text, $this->computedKey, true); + break; + case self::MODE_INTERNAL: + $key = str_pad($this->computedKey, $this->b, chr(0)); // step 1 + $temp = $this->ipad ^ $key; // step 2 + $temp .= $text; // step 3 + $temp = call_user_func($this->hash, $temp); // step 4 + $output = $this->opad ^ $key; // step 5 + $output.= $temp; // step 6 + $output = call_user_func($this->hash, $output); // step 7 + } + } else { + switch ($this->engine) { + case self::MODE_MHASH: + $output = mhash($this->hash, $text); + break; + case self::MODE_HASH: + $output = hash($this->hash, $text, true); + break; + case self::MODE_INTERNAL: + $output = call_user_func($this->hash, $text); + } + } + + return substr($output, 0, $this->l); + } + + /** + * Returns the hash length (in bytes) + * + * @access public + * @return int + */ + function getLength() + { + return $this->l; + } + + /** + * Wrapper for MD5 + * + * @access private + * @param string $m + */ + function _md5($m) + { + return pack('H*', md5($m)); + } + + /** + * Wrapper for SHA1 + * + * @access private + * @param string $m + */ + function _sha1($m) + { + return pack('H*', sha1($m)); + } + + /** + * Pure-PHP implementation of MD2 + * + * See {@link http://tools.ietf.org/html/rfc1319 RFC1319}. + * + * @access private + * @param string $m + */ + function _md2($m) + { + static $s = array( + 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, + 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, + 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, + 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, + 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, + 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, + 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, + 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, + 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, + 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, + 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, + 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, + 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, + 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, + 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, + 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, + 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, + 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 + ); + + // Step 1. Append Padding Bytes + $pad = 16 - (strlen($m) & 0xF); + $m.= str_repeat(chr($pad), $pad); + + $length = strlen($m); + + // Step 2. Append Checksum + $c = str_repeat(chr(0), 16); + $l = chr(0); + for ($i = 0; $i < $length; $i+= 16) { + for ($j = 0; $j < 16; $j++) { + // RFC1319 incorrectly states that C[j] should be set to S[c xor L] + //$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]); + // per , however, C[j] should be set to S[c xor L] xor C[j] + $c[$j] = chr($s[ord($m[$i + $j] ^ $l)] ^ ord($c[$j])); + $l = $c[$j]; + } + } + $m.= $c; + + $length+= 16; + + // Step 3. Initialize MD Buffer + $x = str_repeat(chr(0), 48); + + // Step 4. Process Message in 16-Byte Blocks + for ($i = 0; $i < $length; $i+= 16) { + for ($j = 0; $j < 16; $j++) { + $x[$j + 16] = $m[$i + $j]; + $x[$j + 32] = $x[$j + 16] ^ $x[$j]; + } + $t = chr(0); + for ($j = 0; $j < 18; $j++) { + for ($k = 0; $k < 48; $k++) { + $x[$k] = $t = $x[$k] ^ chr($s[ord($t)]); + //$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]); + } + $t = chr(ord($t) + $j); + } + } + + // Step 5. Output + return substr($x, 0, 16); + } + + /** + * Pure-PHP implementation of SHA256 + * + * See {@link http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}. + * + * @access private + * @param string $m + */ + function _sha256($m) + { + if (extension_loaded('suhosin')) { + return pack('H*', sha256($m)); + } + + // Initialize variables + $hash = array( + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 + ); + // Initialize table of round constants + // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311) + static $k = array( + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + ); + + // Pre-processing + $length = strlen($m); + // to round to nearest 56 mod 64, we'll add 64 - (length + (64 - 56)) % 64 + $m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F)); + $m[$length] = chr(0x80); + // we don't support hashing strings 512MB long + $m.= pack('N2', 0, $length << 3); + + // Process the message in successive 512-bit chunks + $chunks = str_split($m, 64); + foreach ($chunks as $chunk) { + $w = array(); + for ($i = 0; $i < 16; $i++) { + extract(unpack('Ntemp', $this->_string_shift($chunk, 4))); + $w[] = $temp; + } + + // Extend the sixteen 32-bit words into sixty-four 32-bit words + for ($i = 16; $i < 64; $i++) { + // @codingStandardsIgnoreStart + $s0 = $this->_rightRotate($w[$i - 15], 7) ^ + $this->_rightRotate($w[$i - 15], 18) ^ + $this->_rightShift( $w[$i - 15], 3); + $s1 = $this->_rightRotate($w[$i - 2], 17) ^ + $this->_rightRotate($w[$i - 2], 19) ^ + $this->_rightShift( $w[$i - 2], 10); + // @codingStandardsIgnoreEnd + $w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1); + } + + // Initialize hash value for this chunk + list($a, $b, $c, $d, $e, $f, $g, $h) = $hash; + + // Main loop + for ($i = 0; $i < 64; $i++) { + $s0 = $this->_rightRotate($a, 2) ^ + $this->_rightRotate($a, 13) ^ + $this->_rightRotate($a, 22); + $maj = ($a & $b) ^ + ($a & $c) ^ + ($b & $c); + $t2 = $this->_add($s0, $maj); + + $s1 = $this->_rightRotate($e, 6) ^ + $this->_rightRotate($e, 11) ^ + $this->_rightRotate($e, 25); + $ch = ($e & $f) ^ + ($this->_not($e) & $g); + $t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]); + + $h = $g; + $g = $f; + $f = $e; + $e = $this->_add($d, $t1); + $d = $c; + $c = $b; + $b = $a; + $a = $this->_add($t1, $t2); + } + + // Add this chunk's hash to result so far + $hash = array( + $this->_add($hash[0], $a), + $this->_add($hash[1], $b), + $this->_add($hash[2], $c), + $this->_add($hash[3], $d), + $this->_add($hash[4], $e), + $this->_add($hash[5], $f), + $this->_add($hash[6], $g), + $this->_add($hash[7], $h) + ); + } + + // Produce the final hash value (big-endian) + return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]); + } + + /** + * Pure-PHP implementation of SHA384 and SHA512 + * + * @access private + * @param string $m + */ + function _sha512($m) + { + static $init384, $init512, $k; + + if (!isset($k)) { + // Initialize variables + $init384 = array( // initial values for SHA384 + 'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939', + '67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4' + ); + $init512 = array( // initial values for SHA512 + '6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1', + '510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179' + ); + + for ($i = 0; $i < 8; $i++) { + $init384[$i] = new BigInteger($init384[$i], 16); + $init384[$i]->setPrecision(64); + $init512[$i] = new BigInteger($init512[$i], 16); + $init512[$i]->setPrecision(64); + } + + // Initialize table of round constants + // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409) + $k = array( + '428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc', + '3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118', + 'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2', + '72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694', + 'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65', + '2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5', + '983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4', + 'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70', + '27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df', + '650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b', + 'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30', + 'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8', + '19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8', + '391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3', + '748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec', + '90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b', + 'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178', + '06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b', + '28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c', + '4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817' + ); + + for ($i = 0; $i < 80; $i++) { + $k[$i] = new BigInteger($k[$i], 16); + } + } + + $hash = $this->l == 48 ? $init384 : $init512; + + // Pre-processing + $length = strlen($m); + // to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128 + $m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F)); + $m[$length] = chr(0x80); + // we don't support hashing strings 512MB long + $m.= pack('N4', 0, 0, 0, $length << 3); + + // Process the message in successive 1024-bit chunks + $chunks = str_split($m, 128); + foreach ($chunks as $chunk) { + $w = array(); + for ($i = 0; $i < 16; $i++) { + $temp = new BigInteger($this->_string_shift($chunk, 8), 256); + $temp->setPrecision(64); + $w[] = $temp; + } + + // Extend the sixteen 32-bit words into eighty 32-bit words + for ($i = 16; $i < 80; $i++) { + $temp = array( + $w[$i - 15]->bitwise_rightRotate(1), + $w[$i - 15]->bitwise_rightRotate(8), + $w[$i - 15]->bitwise_rightShift(7) + ); + $s0 = $temp[0]->bitwise_xor($temp[1]); + $s0 = $s0->bitwise_xor($temp[2]); + $temp = array( + $w[$i - 2]->bitwise_rightRotate(19), + $w[$i - 2]->bitwise_rightRotate(61), + $w[$i - 2]->bitwise_rightShift(6) + ); + $s1 = $temp[0]->bitwise_xor($temp[1]); + $s1 = $s1->bitwise_xor($temp[2]); + $w[$i] = $w[$i - 16]->copy(); + $w[$i] = $w[$i]->add($s0); + $w[$i] = $w[$i]->add($w[$i - 7]); + $w[$i] = $w[$i]->add($s1); + } + + // Initialize hash value for this chunk + $a = $hash[0]->copy(); + $b = $hash[1]->copy(); + $c = $hash[2]->copy(); + $d = $hash[3]->copy(); + $e = $hash[4]->copy(); + $f = $hash[5]->copy(); + $g = $hash[6]->copy(); + $h = $hash[7]->copy(); + + // Main loop + for ($i = 0; $i < 80; $i++) { + $temp = array( + $a->bitwise_rightRotate(28), + $a->bitwise_rightRotate(34), + $a->bitwise_rightRotate(39) + ); + $s0 = $temp[0]->bitwise_xor($temp[1]); + $s0 = $s0->bitwise_xor($temp[2]); + $temp = array( + $a->bitwise_and($b), + $a->bitwise_and($c), + $b->bitwise_and($c) + ); + $maj = $temp[0]->bitwise_xor($temp[1]); + $maj = $maj->bitwise_xor($temp[2]); + $t2 = $s0->add($maj); + + $temp = array( + $e->bitwise_rightRotate(14), + $e->bitwise_rightRotate(18), + $e->bitwise_rightRotate(41) + ); + $s1 = $temp[0]->bitwise_xor($temp[1]); + $s1 = $s1->bitwise_xor($temp[2]); + $temp = array( + $e->bitwise_and($f), + $g->bitwise_and($e->bitwise_not()) + ); + $ch = $temp[0]->bitwise_xor($temp[1]); + $t1 = $h->add($s1); + $t1 = $t1->add($ch); + $t1 = $t1->add($k[$i]); + $t1 = $t1->add($w[$i]); + + $h = $g->copy(); + $g = $f->copy(); + $f = $e->copy(); + $e = $d->add($t1); + $d = $c->copy(); + $c = $b->copy(); + $b = $a->copy(); + $a = $t1->add($t2); + } + + // Add this chunk's hash to result so far + $hash = array( + $hash[0]->add($a), + $hash[1]->add($b), + $hash[2]->add($c), + $hash[3]->add($d), + $hash[4]->add($e), + $hash[5]->add($f), + $hash[6]->add($g), + $hash[7]->add($h) + ); + } + + // Produce the final hash value (big-endian) + // (\phpseclib\Crypt\Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here) + $temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() . + $hash[4]->toBytes() . $hash[5]->toBytes(); + if ($this->l != 48) { + $temp.= $hash[6]->toBytes() . $hash[7]->toBytes(); + } + + return $temp; + } + + /** + * Right Rotate + * + * @access private + * @param int $int + * @param int $amt + * @see self::_sha256() + * @return int + */ + function _rightRotate($int, $amt) + { + $invamt = 32 - $amt; + $mask = (1 << $invamt) - 1; + return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask); + } + + /** + * Right Shift + * + * @access private + * @param int $int + * @param int $amt + * @see self::_sha256() + * @return int + */ + function _rightShift($int, $amt) + { + $mask = (1 << (32 - $amt)) - 1; + return ($int >> $amt) & $mask; + } + + /** + * Not + * + * @access private + * @param int $int + * @see self::_sha256() + * @return int + */ + function _not($int) + { + return ~$int & 0xFFFFFFFF; + } + + /** + * Add + * + * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the + * possibility of overflow exists, care has to be taken. BigInteger could be used but this should be faster. + * + * @return int + * @see self::_sha256() + * @access private + */ + function _add() + { + static $mod; + if (!isset($mod)) { + $mod = pow(2, 32); + } + + $result = 0; + $arguments = func_get_args(); + foreach ($arguments as $argument) { + $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument; + } + + if ((php_uname('m') & "\xDF\xDF\xDF") != 'ARM') { + return fmod($result, $mod); + } + + return (fmod($result, 0x80000000) & 0x7FFFFFFF) | + ((fmod(floor($result / 0x80000000), 2) & 1) << 31); + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php new file mode 100644 index 0000000..6891413 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php @@ -0,0 +1,694 @@ + + * setKey('abcdefgh'); + * + * $plaintext = str_repeat('a', 1024); + * + * echo $rc2->decrypt($rc2->encrypt($plaintext)); + * ?> + * + * + * @category Crypt + * @package RC2 + * @author Patrick Monnerat + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +/** + * Pure-PHP implementation of RC2. + * + * @package RC2 + * @access public + */ +class RC2 extends Base +{ + /** + * Block Length of the cipher + * + * @see \phpseclib\Crypt\Base::block_size + * @var int + * @access private + */ + var $block_size = 8; + + /** + * The Key + * + * @see \phpseclib\Crypt\Base::key + * @see self::setKey() + * @var string + * @access private + */ + var $key; + + /** + * The Original (unpadded) Key + * + * @see \phpseclib\Crypt\Base::key + * @see self::setKey() + * @see self::encrypt() + * @see self::decrypt() + * @var string + * @access private + */ + var $orig_key = ''; + + /** + * Don't truncate / null pad key + * + * @see \phpseclib\Crypt\Base::_clearBuffers() + * @var bool + * @access private + */ + var $skip_key_adjustment = true; + + /** + * Key Length (in bytes) + * + * @see \phpseclib\Crypt\RC2::setKeyLength() + * @var int + * @access private + */ + var $key_length = 16; // = 128 bits + + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib\Crypt\Base::cipher_name_mcrypt + * @var string + * @access private + */ + var $cipher_name_mcrypt = 'rc2'; + + /** + * Optimizing value while CFB-encrypting + * + * @see \phpseclib\Crypt\Base::cfb_init_len + * @var int + * @access private + */ + var $cfb_init_len = 500; + + /** + * The key length in bits. + * + * @see self::setKeyLength() + * @see self::setKey() + * @var int + * @access private + * @internal Should be in range [1..1024]. + * @internal Changing this value after setting the key has no effect. + */ + var $default_key_length = 1024; + + /** + * The key length in bits. + * + * @see self::isValidEnine() + * @see self::setKey() + * @var int + * @access private + * @internal Should be in range [1..1024]. + */ + var $current_key_length; + + /** + * The Key Schedule + * + * @see self::_setupKey() + * @var array + * @access private + */ + var $keys; + + /** + * Key expansion randomization table. + * Twice the same 256-value sequence to save a modulus in key expansion. + * + * @see self::setKey() + * @var array + * @access private + */ + var $pitable = array( + 0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED, + 0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D, + 0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E, + 0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2, + 0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13, + 0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32, + 0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B, + 0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82, + 0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C, + 0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC, + 0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1, + 0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26, + 0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57, + 0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03, + 0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7, + 0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7, + 0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7, + 0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A, + 0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74, + 0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC, + 0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC, + 0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39, + 0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A, + 0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31, + 0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE, + 0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9, + 0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C, + 0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9, + 0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0, + 0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E, + 0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77, + 0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD, + 0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED, + 0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D, + 0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E, + 0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2, + 0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13, + 0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32, + 0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B, + 0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82, + 0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C, + 0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC, + 0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1, + 0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26, + 0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57, + 0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03, + 0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7, + 0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7, + 0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7, + 0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A, + 0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74, + 0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC, + 0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC, + 0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39, + 0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A, + 0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31, + 0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE, + 0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9, + 0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C, + 0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9, + 0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0, + 0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E, + 0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77, + 0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD + ); + + /** + * Inverse key expansion randomization table. + * + * @see self::setKey() + * @var array + * @access private + */ + var $invpitable = array( + 0xD1, 0xDA, 0xB9, 0x6F, 0x9C, 0xC8, 0x78, 0x66, + 0x80, 0x2C, 0xF8, 0x37, 0xEA, 0xE0, 0x62, 0xA4, + 0xCB, 0x71, 0x50, 0x27, 0x4B, 0x95, 0xD9, 0x20, + 0x9D, 0x04, 0x91, 0xE3, 0x47, 0x6A, 0x7E, 0x53, + 0xFA, 0x3A, 0x3B, 0xB4, 0xA8, 0xBC, 0x5F, 0x68, + 0x08, 0xCA, 0x8F, 0x14, 0xD7, 0xC0, 0xEF, 0x7B, + 0x5B, 0xBF, 0x2F, 0xE5, 0xE2, 0x8C, 0xBA, 0x12, + 0xE1, 0xAF, 0xB2, 0x54, 0x5D, 0x59, 0x76, 0xDB, + 0x32, 0xA2, 0x58, 0x6E, 0x1C, 0x29, 0x64, 0xF3, + 0xE9, 0x96, 0x0C, 0x98, 0x19, 0x8D, 0x3E, 0x26, + 0xAB, 0xA5, 0x85, 0x16, 0x40, 0xBD, 0x49, 0x67, + 0xDC, 0x22, 0x94, 0xBB, 0x3C, 0xC1, 0x9B, 0xEB, + 0x45, 0x28, 0x18, 0xD8, 0x1A, 0x42, 0x7D, 0xCC, + 0xFB, 0x65, 0x8E, 0x3D, 0xCD, 0x2A, 0xA3, 0x60, + 0xAE, 0x93, 0x8A, 0x48, 0x97, 0x51, 0x15, 0xF7, + 0x01, 0x0B, 0xB7, 0x36, 0xB1, 0x2E, 0x11, 0xFD, + 0x84, 0x2D, 0x3F, 0x13, 0x88, 0xB3, 0x34, 0x24, + 0x1B, 0xDE, 0xC5, 0x1D, 0x4D, 0x2B, 0x17, 0x31, + 0x74, 0xA9, 0xC6, 0x43, 0x6D, 0x39, 0x90, 0xBE, + 0xC3, 0xB0, 0x21, 0x6B, 0xF6, 0x0F, 0xD5, 0x99, + 0x0D, 0xAC, 0x1F, 0x5C, 0x9E, 0xF5, 0xF9, 0x4C, + 0xD6, 0xDF, 0x89, 0xE4, 0x8B, 0xFF, 0xC7, 0xAA, + 0xE7, 0xED, 0x46, 0x25, 0xB6, 0x06, 0x5E, 0x35, + 0xB5, 0xEC, 0xCE, 0xE8, 0x6C, 0x30, 0x55, 0x61, + 0x4A, 0xFE, 0xA0, 0x79, 0x03, 0xF0, 0x10, 0x72, + 0x7C, 0xCF, 0x52, 0xA6, 0xA7, 0xEE, 0x44, 0xD3, + 0x9A, 0x57, 0x92, 0xD0, 0x5A, 0x7A, 0x41, 0x7F, + 0x0E, 0x00, 0x63, 0xF2, 0x4F, 0x05, 0x83, 0xC9, + 0xA1, 0xD4, 0xDD, 0xC4, 0x56, 0xF4, 0xD2, 0x77, + 0x81, 0x09, 0x82, 0x33, 0x9F, 0x07, 0x86, 0x75, + 0x38, 0x4E, 0x69, 0xF1, 0xAD, 0x23, 0x73, 0x87, + 0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6 + ); + + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() + * + * @see \phpseclib\Crypt\Base::__construct() + * @param int $engine + * @access public + * @return bool + */ + function isValidEngine($engine) + { + switch ($engine) { + case self::ENGINE_OPENSSL: + // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1 + // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider" + // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not + if (version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) { + return false; + } + if ($this->current_key_length != 128 || strlen($this->orig_key) < 16) { + return false; + } + $this->cipher_name_openssl_ecb = 'rc2-ecb'; + $this->cipher_name_openssl = 'rc2-' . $this->_openssl_translate_mode(); + } + + return parent::isValidEngine($engine); + } + + /** + * Sets the key length. + * + * Valid key lengths are 8 to 1024. + * Calling this function after setting the key has no effect until the next + * \phpseclib\Crypt\RC2::setKey() call. + * + * @access public + * @param int $length in bits + */ + function setKeyLength($length) + { + if ($length < 8) { + $this->default_key_length = 1; + } elseif ($length > 1024) { + $this->default_key_length = 128; + } else { + $this->default_key_length = $length; + } + $this->current_key_length = $this->default_key_length; + + parent::setKeyLength($length); + } + + /** + * Returns the current key length + * + * @access public + * @return int + */ + function getKeyLength() + { + return $this->current_key_length; + } + + /** + * Sets the key. + * + * Keys can be of any length. RC2, itself, uses 8 to 1024 bit keys (eg. + * strlen($key) <= 128), however, we only use the first 128 bytes if $key + * has more then 128 bytes in it, and set $key to a single null byte if + * it is empty. + * + * If the key is not explicitly set, it'll be assumed to be a single + * null byte. + * + * @see \phpseclib\Crypt\Base::setKey() + * @access public + * @param string $key + * @param int $t1 optional Effective key length in bits. + */ + function setKey($key, $t1 = 0) + { + $this->orig_key = $key; + + if ($t1 <= 0) { + $t1 = $this->default_key_length; + } elseif ($t1 > 1024) { + $t1 = 1024; + } + $this->current_key_length = $t1; + // Key byte count should be 1..128. + $key = strlen($key) ? substr($key, 0, 128) : "\x00"; + $t = strlen($key); + + // The mcrypt RC2 implementation only supports effective key length + // of 1024 bits. It is however possible to handle effective key + // lengths in range 1..1024 by expanding the key and applying + // inverse pitable mapping to the first byte before submitting it + // to mcrypt. + + // Key expansion. + $l = array_values(unpack('C*', $key)); + $t8 = ($t1 + 7) >> 3; + $tm = 0xFF >> (8 * $t8 - $t1); + + // Expand key. + $pitable = $this->pitable; + for ($i = $t; $i < 128; $i++) { + $l[$i] = $pitable[$l[$i - 1] + $l[$i - $t]]; + } + $i = 128 - $t8; + $l[$i] = $pitable[$l[$i] & $tm]; + while ($i--) { + $l[$i] = $pitable[$l[$i + 1] ^ $l[$i + $t8]]; + } + + // Prepare the key for mcrypt. + $l[0] = $this->invpitable[$l[0]]; + array_unshift($l, 'C*'); + + parent::setKey(call_user_func_array('pack', $l)); + } + + /** + * Encrypts a message. + * + * Mostly a wrapper for \phpseclib\Crypt\Base::encrypt, with some additional OpenSSL handling code + * + * @see self::decrypt() + * @access public + * @param string $plaintext + * @return string $ciphertext + */ + function encrypt($plaintext) + { + if ($this->engine == self::ENGINE_OPENSSL) { + $temp = $this->key; + $this->key = $this->orig_key; + $result = parent::encrypt($plaintext); + $this->key = $temp; + return $result; + } + + return parent::encrypt($plaintext); + } + + /** + * Decrypts a message. + * + * Mostly a wrapper for \phpseclib\Crypt\Base::decrypt, with some additional OpenSSL handling code + * + * @see self::encrypt() + * @access public + * @param string $ciphertext + * @return string $plaintext + */ + function decrypt($ciphertext) + { + if ($this->engine == self::ENGINE_OPENSSL) { + $temp = $this->key; + $this->key = $this->orig_key; + $result = parent::decrypt($ciphertext); + $this->key = $temp; + return $result; + } + + return parent::decrypt($ciphertext); + } + + /** + * Encrypts a block + * + * @see \phpseclib\Crypt\Base::_encryptBlock() + * @see \phpseclib\Crypt\Base::encrypt() + * @access private + * @param string $in + * @return string + */ + function _encryptBlock($in) + { + list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in)); + $keys = $this->keys; + $limit = 20; + $actions = array($limit => 44, 44 => 64); + $j = 0; + + for (;;) { + // Mixing round. + $r0 = (($r0 + $keys[$j++] + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1; + $r0 |= $r0 >> 16; + $r1 = (($r1 + $keys[$j++] + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2; + $r1 |= $r1 >> 16; + $r2 = (($r2 + $keys[$j++] + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3; + $r2 |= $r2 >> 16; + $r3 = (($r3 + $keys[$j++] + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5; + $r3 |= $r3 >> 16; + + if ($j === $limit) { + if ($limit === 64) { + break; + } + + // Mashing round. + $r0 += $keys[$r3 & 0x3F]; + $r1 += $keys[$r0 & 0x3F]; + $r2 += $keys[$r1 & 0x3F]; + $r3 += $keys[$r2 & 0x3F]; + $limit = $actions[$limit]; + } + } + + return pack('vvvv', $r0, $r1, $r2, $r3); + } + + /** + * Decrypts a block + * + * @see \phpseclib\Crypt\Base::_decryptBlock() + * @see \phpseclib\Crypt\Base::decrypt() + * @access private + * @param string $in + * @return string + */ + function _decryptBlock($in) + { + list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in)); + $keys = $this->keys; + $limit = 44; + $actions = array($limit => 20, 20 => 0); + $j = 64; + + for (;;) { + // R-mixing round. + $r3 = ($r3 | ($r3 << 16)) >> 5; + $r3 = ($r3 - $keys[--$j] - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF; + $r2 = ($r2 | ($r2 << 16)) >> 3; + $r2 = ($r2 - $keys[--$j] - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF; + $r1 = ($r1 | ($r1 << 16)) >> 2; + $r1 = ($r1 - $keys[--$j] - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF; + $r0 = ($r0 | ($r0 << 16)) >> 1; + $r0 = ($r0 - $keys[--$j] - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF; + + if ($j === $limit) { + if ($limit === 0) { + break; + } + + // R-mashing round. + $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF; + $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF; + $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF; + $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF; + $limit = $actions[$limit]; + } + } + + return pack('vvvv', $r0, $r1, $r2, $r3); + } + + /** + * Setup the \phpseclib\Crypt\Base::ENGINE_MCRYPT $engine + * + * @see \phpseclib\Crypt\Base::_setupMcrypt() + * @access private + */ + function _setupMcrypt() + { + if (!isset($this->key)) { + $this->setKey(''); + } + + parent::_setupMcrypt(); + } + + /** + * Creates the key schedule + * + * @see \phpseclib\Crypt\Base::_setupKey() + * @access private + */ + function _setupKey() + { + if (!isset($this->key)) { + $this->setKey(''); + } + + // Key has already been expanded in \phpseclib\Crypt\RC2::setKey(): + // Only the first value must be altered. + $l = unpack('Ca/Cb/v*', $this->key); + array_unshift($l, $this->pitable[$l['a']] | ($l['b'] << 8)); + unset($l['a']); + unset($l['b']); + $this->keys = $l; + } + + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see \phpseclib\Crypt\Base::_setupInlineCrypt() + * @access private + */ + function _setupInlineCrypt() + { + $lambda_functions =& self::_getLambdaFunctions(); + + // The first 10 generated $lambda_functions will use the $keys hardcoded as integers + // for the mixing rounds, for better inline crypt performance [~20% faster]. + // But for memory reason we have to limit those ultra-optimized $lambda_functions to an amount of 10. + // (Currently, for Crypt_RC2, one generated $lambda_function cost on php5.5@32bit ~60kb unfreeable mem and ~100kb on php5.5@64bit) + $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); + + // Generation of a unique hash for our generated code + $code_hash = "Crypt_RC2, {$this->mode}"; + if ($gen_hi_opt_code) { + $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); + } + + // Is there a re-usable $lambda_functions in there? + // If not, we have to create it. + if (!isset($lambda_functions[$code_hash])) { + // Init code for both, encrypt and decrypt. + $init_crypt = '$keys = $self->keys;'; + + switch (true) { + case $gen_hi_opt_code: + $keys = $this->keys; + default: + $keys = array(); + foreach ($this->keys as $k => $v) { + $keys[$k] = '$keys[' . $k . ']'; + } + } + + // $in is the current 8 bytes block which has to be en/decrypt + $encrypt_block = $decrypt_block = ' + $in = unpack("v4", $in); + $r0 = $in[1]; + $r1 = $in[2]; + $r2 = $in[3]; + $r3 = $in[4]; + '; + + // Create code for encryption. + $limit = 20; + $actions = array($limit => 44, 44 => 64); + $j = 0; + + for (;;) { + // Mixing round. + $encrypt_block .= ' + $r0 = (($r0 + ' . $keys[$j++] . ' + + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1; + $r0 |= $r0 >> 16; + $r1 = (($r1 + ' . $keys[$j++] . ' + + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2; + $r1 |= $r1 >> 16; + $r2 = (($r2 + ' . $keys[$j++] . ' + + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3; + $r2 |= $r2 >> 16; + $r3 = (($r3 + ' . $keys[$j++] . ' + + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5; + $r3 |= $r3 >> 16;'; + + if ($j === $limit) { + if ($limit === 64) { + break; + } + + // Mashing round. + $encrypt_block .= ' + $r0 += $keys[$r3 & 0x3F]; + $r1 += $keys[$r0 & 0x3F]; + $r2 += $keys[$r1 & 0x3F]; + $r3 += $keys[$r2 & 0x3F];'; + $limit = $actions[$limit]; + } + } + + $encrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);'; + + // Create code for decryption. + $limit = 44; + $actions = array($limit => 20, 20 => 0); + $j = 64; + + for (;;) { + // R-mixing round. + $decrypt_block .= ' + $r3 = ($r3 | ($r3 << 16)) >> 5; + $r3 = ($r3 - ' . $keys[--$j] . ' - + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF; + $r2 = ($r2 | ($r2 << 16)) >> 3; + $r2 = ($r2 - ' . $keys[--$j] . ' - + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF; + $r1 = ($r1 | ($r1 << 16)) >> 2; + $r1 = ($r1 - ' . $keys[--$j] . ' - + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF; + $r0 = ($r0 | ($r0 << 16)) >> 1; + $r0 = ($r0 - ' . $keys[--$j] . ' - + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;'; + + if ($j === $limit) { + if ($limit === 0) { + break; + } + + // R-mashing round. + $decrypt_block .= ' + $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF; + $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF; + $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF; + $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;'; + $limit = $actions[$limit]; + } + } + + $decrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);'; + + // Creates the inline-crypt function + $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( + array( + 'init_crypt' => $init_crypt, + 'encrypt_block' => $encrypt_block, + 'decrypt_block' => $decrypt_block + ) + ); + } + + // Set the inline-crypt function as callback in: $this->inline_crypt + $this->inline_crypt = $lambda_functions[$code_hash]; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php new file mode 100644 index 0000000..7033ec2 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php @@ -0,0 +1,348 @@ + + * setKey('abcdefgh'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $rc4->decrypt($rc4->encrypt($plaintext)); + * ?> + * + * + * @category Crypt + * @package RC4 + * @author Jim Wigginton + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +/** + * Pure-PHP implementation of RC4. + * + * @package RC4 + * @author Jim Wigginton + * @access public + */ +class RC4 extends Base +{ + /**#@+ + * @access private + * @see \phpseclib\Crypt\RC4::_crypt() + */ + const ENCRYPT = 0; + const DECRYPT = 1; + /**#@-*/ + + /** + * Block Length of the cipher + * + * RC4 is a stream cipher + * so we the block_size to 0 + * + * @see \phpseclib\Crypt\Base::block_size + * @var int + * @access private + */ + var $block_size = 0; + + /** + * Key Length (in bytes) + * + * @see \phpseclib\Crypt\RC4::setKeyLength() + * @var int + * @access private + */ + var $key_length = 128; // = 1024 bits + + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib\Crypt\Base::cipher_name_mcrypt + * @var string + * @access private + */ + var $cipher_name_mcrypt = 'arcfour'; + + /** + * Holds whether performance-optimized $inline_crypt() can/should be used. + * + * @see \phpseclib\Crypt\Base::inline_crypt + * @var mixed + * @access private + */ + var $use_inline_crypt = false; // currently not available + + /** + * The Key + * + * @see self::setKey() + * @var string + * @access private + */ + var $key; + + /** + * The Key Stream for decryption and encryption + * + * @see self::setKey() + * @var array + * @access private + */ + var $stream; + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. + * + * @see \phpseclib\Crypt\Base::__construct() + * @return \phpseclib\Crypt\RC4 + * @access public + */ + function __construct() + { + parent::__construct(Base::MODE_STREAM); + } + + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() + * + * @see \phpseclib\Crypt\Base::__construct() + * @param int $engine + * @access public + * @return bool + */ + function isValidEngine($engine) + { + if ($engine == self::ENGINE_OPENSSL) { + // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1 + // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider" + // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not + if (version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) { + return false; + } + if (version_compare(PHP_VERSION, '5.3.7') >= 0) { + $this->cipher_name_openssl = 'rc4-40'; + } else { + switch (strlen($this->key)) { + case 5: + $this->cipher_name_openssl = 'rc4-40'; + break; + case 8: + $this->cipher_name_openssl = 'rc4-64'; + break; + case 16: + $this->cipher_name_openssl = 'rc4'; + break; + default: + return false; + } + } + } + + return parent::isValidEngine($engine); + } + + /** + * Dummy function. + * + * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1]. + * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before + * calling setKey(). + * + * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol, + * the IV's are relatively easy to predict, an attack described by + * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir} + * can be used to quickly guess at the rest of the key. The following links elaborate: + * + * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009} + * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack} + * + * @param string $iv + * @see self::setKey() + * @access public + */ + function setIV($iv) + { + } + + /** + * Sets the key length + * + * Keys can be between 1 and 256 bytes long. + * + * @access public + * @param int $length + */ + function setKeyLength($length) + { + if ($length < 8) { + $this->key_length = 1; + } elseif ($length > 2048) { + $this->key_length = 256; + } else { + $this->key_length = $length >> 3; + } + + parent::setKeyLength($length); + } + + /** + * Encrypts a message. + * + * @see \phpseclib\Crypt\Base::decrypt() + * @see self::_crypt() + * @access public + * @param string $plaintext + * @return string $ciphertext + */ + function encrypt($plaintext) + { + if ($this->engine != self::ENGINE_INTERNAL) { + return parent::encrypt($plaintext); + } + return $this->_crypt($plaintext, self::ENCRYPT); + } + + /** + * Decrypts a message. + * + * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)). + * At least if the continuous buffer is disabled. + * + * @see \phpseclib\Crypt\Base::encrypt() + * @see self::_crypt() + * @access public + * @param string $ciphertext + * @return string $plaintext + */ + function decrypt($ciphertext) + { + if ($this->engine != self::ENGINE_INTERNAL) { + return parent::decrypt($ciphertext); + } + return $this->_crypt($ciphertext, self::DECRYPT); + } + + /** + * Encrypts a block + * + * @access private + * @param string $in + */ + function _encryptBlock($in) + { + // RC4 does not utilize this method + } + + /** + * Decrypts a block + * + * @access private + * @param string $in + */ + function _decryptBlock($in) + { + // RC4 does not utilize this method + } + + /** + * Setup the key (expansion) + * + * @see \phpseclib\Crypt\Base::_setupKey() + * @access private + */ + function _setupKey() + { + $key = $this->key; + $keyLength = strlen($key); + $keyStream = range(0, 255); + $j = 0; + for ($i = 0; $i < 256; $i++) { + $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255; + $temp = $keyStream[$i]; + $keyStream[$i] = $keyStream[$j]; + $keyStream[$j] = $temp; + } + + $this->stream = array(); + $this->stream[self::DECRYPT] = $this->stream[self::ENCRYPT] = array( + 0, // index $i + 0, // index $j + $keyStream + ); + } + + /** + * Encrypts or decrypts a message. + * + * @see self::encrypt() + * @see self::decrypt() + * @access private + * @param string $text + * @param int $mode + * @return string $text + */ + function _crypt($text, $mode) + { + if ($this->changed) { + $this->_setup(); + $this->changed = false; + } + + $stream = &$this->stream[$mode]; + if ($this->continuousBuffer) { + $i = &$stream[0]; + $j = &$stream[1]; + $keyStream = &$stream[2]; + } else { + $i = $stream[0]; + $j = $stream[1]; + $keyStream = $stream[2]; + } + + $len = strlen($text); + for ($k = 0; $k < $len; ++$k) { + $i = ($i + 1) & 255; + $ksi = $keyStream[$i]; + $j = ($j + $ksi) & 255; + $ksj = $keyStream[$j]; + + $keyStream[$i] = $ksj; + $keyStream[$j] = $ksi; + $text[$k] = $text[$k] ^ chr($keyStream[($ksj + $ksi) & 255]); + } + + return $text; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php new file mode 100644 index 0000000..f486dad --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php @@ -0,0 +1,3342 @@ + + * createKey()); + * + * $plaintext = 'terrafrost'; + * + * $rsa->loadKey($privatekey); + * $ciphertext = $rsa->encrypt($plaintext); + * + * $rsa->loadKey($publickey); + * echo $rsa->decrypt($ciphertext); + * ?> + * + * + * Here's an example of how to create signatures and verify signatures with this library: + * + * createKey()); + * + * $plaintext = 'terrafrost'; + * + * $rsa->loadKey($privatekey); + * $signature = $rsa->sign($plaintext); + * + * $rsa->loadKey($publickey); + * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified'; + * ?> + * + * + * @category Crypt + * @package RSA + * @author Jim Wigginton + * @copyright 2009 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +use phpseclib\Math\BigInteger; + +/** + * Pure-PHP PKCS#1 compliant implementation of RSA. + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +class RSA +{ + /**#@+ + * @access public + * @see self::encrypt() + * @see self::decrypt() + */ + /** + * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding} + * (OAEP) for encryption / decryption. + * + * Uses sha1 by default. + * + * @see self::setHash() + * @see self::setMGFHash() + */ + const ENCRYPTION_OAEP = 1; + /** + * Use PKCS#1 padding. + * + * Although self::ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards + * compatibility with protocols (like SSH-1) written before OAEP's introduction. + */ + const ENCRYPTION_PKCS1 = 2; + /** + * Do not use any padding + * + * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy + * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc. + */ + const ENCRYPTION_NONE = 3; + /**#@-*/ + + /**#@+ + * @access public + * @see self::sign() + * @see self::verify() + * @see self::setHash() + */ + /** + * Use the Probabilistic Signature Scheme for signing + * + * Uses sha1 by default. + * + * @see self::setSaltLength() + * @see self::setMGFHash() + */ + const SIGNATURE_PSS = 1; + /** + * Use the PKCS#1 scheme by default. + * + * Although self::SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards + * compatibility with protocols (like SSH-2) written before PSS's introduction. + */ + const SIGNATURE_PKCS1 = 2; + /**#@-*/ + + /**#@+ + * @access private + * @see \phpseclib\Crypt\RSA::createKey() + */ + /** + * ASN1 Integer + */ + const ASN1_INTEGER = 2; + /** + * ASN1 Bit String + */ + const ASN1_BITSTRING = 3; + /** + * ASN1 Octet String + */ + const ASN1_OCTETSTRING = 4; + /** + * ASN1 Object Identifier + */ + const ASN1_OBJECT = 6; + /** + * ASN1 Sequence (with the constucted bit set) + */ + const ASN1_SEQUENCE = 48; + /**#@-*/ + + /**#@+ + * @access private + * @see \phpseclib\Crypt\RSA::__construct() + */ + /** + * To use the pure-PHP implementation + */ + const MODE_INTERNAL = 1; + /** + * To use the OpenSSL library + * + * (if enabled; otherwise, the internal implementation will be used) + */ + const MODE_OPENSSL = 2; + /**#@-*/ + + /**#@+ + * @access public + * @see \phpseclib\Crypt\RSA::createKey() + * @see \phpseclib\Crypt\RSA::setPrivateKeyFormat() + */ + /** + * PKCS#1 formatted private key + * + * Used by OpenSSH + */ + const PRIVATE_FORMAT_PKCS1 = 0; + /** + * PuTTY formatted private key + */ + const PRIVATE_FORMAT_PUTTY = 1; + /** + * XML formatted private key + */ + const PRIVATE_FORMAT_XML = 2; + /** + * PKCS#8 formatted private key + */ + const PRIVATE_FORMAT_PKCS8 = 8; + /** + * OpenSSH formatted private key + */ + const PRIVATE_FORMAT_OPENSSH = 9; + /**#@-*/ + + /**#@+ + * @access public + * @see \phpseclib\Crypt\RSA::createKey() + * @see \phpseclib\Crypt\RSA::setPublicKeyFormat() + */ + /** + * Raw public key + * + * An array containing two \phpseclib\Math\BigInteger objects. + * + * The exponent can be indexed with any of the following: + * + * 0, e, exponent, publicExponent + * + * The modulus can be indexed with any of the following: + * + * 1, n, modulo, modulus + */ + const PUBLIC_FORMAT_RAW = 3; + /** + * PKCS#1 formatted public key (raw) + * + * Used by File/X509.php + * + * Has the following header: + * + * -----BEGIN RSA PUBLIC KEY----- + * + * Analogous to ssh-keygen's pem format (as specified by -m) + */ + const PUBLIC_FORMAT_PKCS1 = 4; + const PUBLIC_FORMAT_PKCS1_RAW = 4; + /** + * XML formatted public key + */ + const PUBLIC_FORMAT_XML = 5; + /** + * OpenSSH formatted public key + * + * Place in $HOME/.ssh/authorized_keys + */ + const PUBLIC_FORMAT_OPENSSH = 6; + /** + * PKCS#1 formatted public key (encapsulated) + * + * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set) + * + * Has the following header: + * + * -----BEGIN PUBLIC KEY----- + * + * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8 + * is specific to private keys it's basically creating a DER-encoded wrapper + * for keys. This just extends that same concept to public keys (much like ssh-keygen) + */ + const PUBLIC_FORMAT_PKCS8 = 7; + /**#@-*/ + + /** + * Precomputed Zero + * + * @var \phpseclib\Math\BigInteger + * @access private + */ + var $zero; + + /** + * Precomputed One + * + * @var \phpseclib\Math\BigInteger + * @access private + */ + var $one; + + /** + * Private Key Format + * + * @var int + * @access private + */ + var $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1; + + /** + * Public Key Format + * + * @var int + * @access public + */ + var $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8; + + /** + * Modulus (ie. n) + * + * @var \phpseclib\Math\BigInteger + * @access private + */ + var $modulus; + + /** + * Modulus length + * + * @var \phpseclib\Math\BigInteger + * @access private + */ + var $k; + + /** + * Exponent (ie. e or d) + * + * @var \phpseclib\Math\BigInteger + * @access private + */ + var $exponent; + + /** + * Primes for Chinese Remainder Theorem (ie. p and q) + * + * @var array + * @access private + */ + var $primes; + + /** + * Exponents for Chinese Remainder Theorem (ie. dP and dQ) + * + * @var array + * @access private + */ + var $exponents; + + /** + * Coefficients for Chinese Remainder Theorem (ie. qInv) + * + * @var array + * @access private + */ + var $coefficients; + + /** + * Hash name + * + * @var string + * @access private + */ + var $hashName; + + /** + * Hash function + * + * @var \phpseclib\Crypt\Hash + * @access private + */ + var $hash; + + /** + * Length of hash function output + * + * @var int + * @access private + */ + var $hLen; + + /** + * Length of salt + * + * @var int + * @access private + */ + var $sLen; + + /** + * Hash function for the Mask Generation Function + * + * @var \phpseclib\Crypt\Hash + * @access private + */ + var $mgfHash; + + /** + * Length of MGF hash function output + * + * @var int + * @access private + */ + var $mgfHLen; + + /** + * Encryption mode + * + * @var int + * @access private + */ + var $encryptionMode = self::ENCRYPTION_OAEP; + + /** + * Signature mode + * + * @var int + * @access private + */ + var $signatureMode = self::SIGNATURE_PSS; + + /** + * Public Exponent + * + * @var mixed + * @access private + */ + var $publicExponent = false; + + /** + * Password + * + * @var string + * @access private + */ + var $password = false; + + /** + * Components + * + * For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions - + * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't. + * + * @see self::_start_element_handler() + * @var array + * @access private + */ + var $components = array(); + + /** + * Current String + * + * For use with parsing XML formatted keys. + * + * @see self::_character_handler() + * @see self::_stop_element_handler() + * @var mixed + * @access private + */ + var $current; + + /** + * OpenSSL configuration file name. + * + * Set to null to use system configuration file. + * @see self::createKey() + * @var mixed + * @Access public + */ + var $configFile; + + /** + * Public key comment field. + * + * @var string + * @access private + */ + var $comment = 'phpseclib-generated-key'; + + /** + * The constructor + * + * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason + * \phpseclib\Crypt\RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires + * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late. + * + * @return \phpseclib\Crypt\RSA + * @access public + */ + function __construct() + { + $this->configFile = dirname(__FILE__) . '/../openssl.cnf'; + + if (!defined('CRYPT_RSA_MODE')) { + switch (true) { + // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular, + // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger + // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either. + case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'): + define('CRYPT_RSA_MODE', self::MODE_INTERNAL); + break; + case function_exists('phpinfo') && extension_loaded('openssl') && file_exists($this->configFile): + // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work + $versions = array(); + + // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems) + if (strpos(ini_get('disable_functions'), 'phpinfo') === false) { + ob_start(); + @phpinfo(); + $content = ob_get_contents(); + ob_end_clean(); + + preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); + + if (!empty($matches[1])) { + for ($i = 0; $i < count($matches[1]); $i++) { + $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); + + // Remove letter part in OpenSSL version + if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { + $versions[$matches[1][$i]] = $fullVersion; + } else { + $versions[$matches[1][$i]] = $m[0]; + } + } + } + } + + // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ + switch (true) { + case !isset($versions['Header']): + case !isset($versions['Library']): + case $versions['Header'] == $versions['Library']: + case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0: + define('CRYPT_RSA_MODE', self::MODE_OPENSSL); + break; + default: + define('CRYPT_RSA_MODE', self::MODE_INTERNAL); + define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); + } + break; + default: + define('CRYPT_RSA_MODE', self::MODE_INTERNAL); + } + } + + $this->zero = new BigInteger(); + $this->one = new BigInteger(1); + + $this->hash = new Hash('sha1'); + $this->hLen = $this->hash->getLength(); + $this->hashName = 'sha1'; + $this->mgfHash = new Hash('sha1'); + $this->mgfHLen = $this->mgfHash->getLength(); + } + + /** + * Create public / private key pair + * + * Returns an array with the following three elements: + * - 'privatekey': The private key. + * - 'publickey': The public key. + * - 'partialkey': A partially computed key (if the execution time exceeded $timeout). + * Will need to be passed back to \phpseclib\Crypt\RSA::createKey() as the third parameter for further processing. + * + * @access public + * @param int $bits + * @param int $timeout + * @param array $partial + */ + function createKey($bits = 1024, $timeout = false, $partial = array()) + { + if (!defined('CRYPT_RSA_EXPONENT')) { + // http://en.wikipedia.org/wiki/65537_%28number%29 + define('CRYPT_RSA_EXPONENT', '65537'); + } + // per , this number ought not result in primes smaller + // than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME + // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if + // CRYPT_RSA_MODE is set to self::MODE_INTERNAL. if CRYPT_RSA_MODE is set to self::MODE_OPENSSL then + // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key + // generation when there's a chance neither gmp nor OpenSSL are installed) + if (!defined('CRYPT_RSA_SMALLEST_PRIME')) { + define('CRYPT_RSA_SMALLEST_PRIME', 4096); + } + + // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum + if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) { + $config = array(); + if (isset($this->configFile)) { + $config['config'] = $this->configFile; + } + $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config); + openssl_pkey_export($rsa, $privatekey, null, $config); + $publickey = openssl_pkey_get_details($rsa); + $publickey = $publickey['key']; + + $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, self::PRIVATE_FORMAT_PKCS1))); + $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, self::PUBLIC_FORMAT_PKCS1))); + + // clear the buffer of error strings stemming from a minimalistic openssl.cnf + while (openssl_error_string() !== false) { + } + + return array( + 'privatekey' => $privatekey, + 'publickey' => $publickey, + 'partialkey' => false + ); + } + + static $e; + if (!isset($e)) { + $e = new BigInteger(CRYPT_RSA_EXPONENT); + } + + extract($this->_generateMinMax($bits)); + $absoluteMin = $min; + $temp = $bits >> 1; // divide by two to see how many bits P and Q would be + if ($temp > CRYPT_RSA_SMALLEST_PRIME) { + $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME); + $temp = CRYPT_RSA_SMALLEST_PRIME; + } else { + $num_primes = 2; + } + extract($this->_generateMinMax($temp + $bits % $temp)); + $finalMax = $max; + extract($this->_generateMinMax($temp)); + + $generator = new BigInteger(); + + $n = $this->one->copy(); + if (!empty($partial)) { + extract(unserialize($partial)); + } else { + $exponents = $coefficients = $primes = array(); + $lcm = array( + 'top' => $this->one->copy(), + 'bottom' => false + ); + } + + $start = time(); + $i0 = count($primes) + 1; + + do { + for ($i = $i0; $i <= $num_primes; $i++) { + if ($timeout !== false) { + $timeout-= time() - $start; + $start = time(); + if ($timeout <= 0) { + return array( + 'privatekey' => '', + 'publickey' => '', + 'partialkey' => serialize(array( + 'primes' => $primes, + 'coefficients' => $coefficients, + 'lcm' => $lcm, + 'exponents' => $exponents + )) + ); + } + } + + if ($i == $num_primes) { + list($min, $temp) = $absoluteMin->divide($n); + if (!$temp->equals($this->zero)) { + $min = $min->add($this->one); // ie. ceil() + } + $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout); + } else { + $primes[$i] = $generator->randomPrime($min, $max, $timeout); + } + + if ($primes[$i] === false) { // if we've reached the timeout + if (count($primes) > 1) { + $partialkey = ''; + } else { + array_pop($primes); + $partialkey = serialize(array( + 'primes' => $primes, + 'coefficients' => $coefficients, + 'lcm' => $lcm, + 'exponents' => $exponents + )); + } + + return array( + 'privatekey' => '', + 'publickey' => '', + 'partialkey' => $partialkey + ); + } + + // the first coefficient is calculated differently from the rest + // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1]) + if ($i > 2) { + $coefficients[$i] = $n->modInverse($primes[$i]); + } + + $n = $n->multiply($primes[$i]); + + $temp = $primes[$i]->subtract($this->one); + + // textbook RSA implementations use Euler's totient function instead of the least common multiple. + // see http://en.wikipedia.org/wiki/Euler%27s_totient_function + $lcm['top'] = $lcm['top']->multiply($temp); + $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp); + + $exponents[$i] = $e->modInverse($temp); + } + + list($temp) = $lcm['top']->divide($lcm['bottom']); + $gcd = $temp->gcd($e); + $i0 = 1; + } while (!$gcd->equals($this->one)); + + $d = $e->modInverse($temp); + + $coefficients[2] = $primes[2]->modInverse($primes[1]); + + // from : + // RSAPrivateKey ::= SEQUENCE { + // version Version, + // modulus INTEGER, -- n + // publicExponent INTEGER, -- e + // privateExponent INTEGER, -- d + // prime1 INTEGER, -- p + // prime2 INTEGER, -- q + // exponent1 INTEGER, -- d mod (p-1) + // exponent2 INTEGER, -- d mod (q-1) + // coefficient INTEGER, -- (inverse of q) mod p + // otherPrimeInfos OtherPrimeInfos OPTIONAL + // } + + return array( + 'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), + 'publickey' => $this->_convertPublicKey($n, $e), + 'partialkey' => false + ); + } + + /** + * Convert a private key to the appropriate format. + * + * @access private + * @see self::setPrivateKeyFormat() + * @param Math_BigInteger $n + * @param Math_BigInteger $e + * @param Math_BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients + * @return string + */ + function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) + { + $signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML; + $num_primes = count($primes); + $raw = array( + 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi + 'modulus' => $n->toBytes($signed), + 'publicExponent' => $e->toBytes($signed), + 'privateExponent' => $d->toBytes($signed), + 'prime1' => $primes[1]->toBytes($signed), + 'prime2' => $primes[2]->toBytes($signed), + 'exponent1' => $exponents[1]->toBytes($signed), + 'exponent2' => $exponents[2]->toBytes($signed), + 'coefficient' => $coefficients[2]->toBytes($signed) + ); + + // if the format in question does not support multi-prime rsa and multi-prime rsa was used, + // call _convertPublicKey() instead. + switch ($this->privateKeyFormat) { + case self::PRIVATE_FORMAT_XML: + if ($num_primes != 2) { + return false; + } + return "\r\n" . + ' ' . base64_encode($raw['modulus']) . "\r\n" . + ' ' . base64_encode($raw['publicExponent']) . "\r\n" . + '

      ' . base64_encode($raw['prime1']) . "

      \r\n" . + ' ' . base64_encode($raw['prime2']) . "\r\n" . + ' ' . base64_encode($raw['exponent1']) . "\r\n" . + ' ' . base64_encode($raw['exponent2']) . "\r\n" . + ' ' . base64_encode($raw['coefficient']) . "\r\n" . + ' ' . base64_encode($raw['privateExponent']) . "\r\n" . + '
      '; + break; + case self::PRIVATE_FORMAT_PUTTY: + if ($num_primes != 2) { + return false; + } + $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: "; + $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none'; + $key.= $encryption; + $key.= "\r\nComment: " . $this->comment . "\r\n"; + $public = pack( + 'Na*Na*Na*', + strlen('ssh-rsa'), + 'ssh-rsa', + strlen($raw['publicExponent']), + $raw['publicExponent'], + strlen($raw['modulus']), + $raw['modulus'] + ); + $source = pack( + 'Na*Na*Na*Na*', + strlen('ssh-rsa'), + 'ssh-rsa', + strlen($encryption), + $encryption, + strlen($this->comment), + $this->comment, + strlen($public), + $public + ); + $public = base64_encode($public); + $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n"; + $key.= chunk_split($public, 64); + $private = pack( + 'Na*Na*Na*Na*', + strlen($raw['privateExponent']), + $raw['privateExponent'], + strlen($raw['prime1']), + $raw['prime1'], + strlen($raw['prime2']), + $raw['prime2'], + strlen($raw['coefficient']), + $raw['coefficient'] + ); + if (empty($this->password) && !is_string($this->password)) { + $source.= pack('Na*', strlen($private), $private); + $hashkey = 'putty-private-key-file-mac-key'; + } else { + $private.= Random::string(16 - (strlen($private) & 15)); + $source.= pack('Na*', strlen($private), $private); + $sequence = 0; + $symkey = ''; + while (strlen($symkey) < 32) { + $temp = pack('Na*', $sequence++, $this->password); + $symkey.= pack('H*', sha1($temp)); + } + $symkey = substr($symkey, 0, 32); + $crypto = new AES(); + + $crypto->setKey($symkey); + $crypto->disablePadding(); + $private = $crypto->encrypt($private); + $hashkey = 'putty-private-key-file-mac-key' . $this->password; + } + + $private = base64_encode($private); + $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n"; + $key.= chunk_split($private, 64); + $hash = new Hash('sha1'); + $hash->setKey(pack('H*', sha1($hashkey))); + $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n"; + + return $key; + case self::PRIVATE_FORMAT_OPENSSH: + if ($num_primes != 2) { + return false; + } + $publicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']); + $privateKey = pack( + 'Na*Na*Na*Na*Na*Na*Na*', + strlen('ssh-rsa'), + 'ssh-rsa', + strlen($raw['modulus']), + $raw['modulus'], + strlen($raw['publicExponent']), + $raw['publicExponent'], + strlen($raw['privateExponent']), + $raw['privateExponent'], + strlen($raw['coefficient']), + $raw['coefficient'], + strlen($raw['prime1']), + $raw['prime1'], + strlen($raw['prime2']), + $raw['prime2'] + ); + $checkint = Random::string(4); + $paddedKey = pack( + 'a*Na*', + $checkint . $checkint . $privateKey, + strlen($this->comment), + $this->comment + ); + $paddingLength = (7 * strlen($paddedKey)) % 8; + for ($i = 1; $i <= $paddingLength; $i++) { + $paddedKey.= chr($i); + } + $key = pack( + 'Na*Na*Na*NNa*Na*', + strlen('none'), + 'none', + strlen('none'), + 'none', + 0, + '', + 1, + strlen($publicKey), + $publicKey, + strlen($paddedKey), + $paddedKey + ); + $key = "openssh-key-v1\0$key"; + + return "-----BEGIN OPENSSH PRIVATE KEY-----\n" . + chunk_split(base64_encode($key), 70, "\n") . + "-----END OPENSSH PRIVATE KEY-----\n"; + default: // eg. self::PRIVATE_FORMAT_PKCS1 + $components = array(); + foreach ($raw as $name => $value) { + $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value); + } + + $RSAPrivateKey = implode('', $components); + + if ($num_primes > 2) { + $OtherPrimeInfos = ''; + for ($i = 3; $i <= $num_primes; $i++) { + // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo + // + // OtherPrimeInfo ::= SEQUENCE { + // prime INTEGER, -- ri + // exponent INTEGER, -- di + // coefficient INTEGER -- ti + // } + $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); + $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); + } + $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); + } + + $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + + if ($this->privateKeyFormat == self::PRIVATE_FORMAT_PKCS8) { + $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA + $RSAPrivateKey = pack( + 'Ca*a*Ca*a*', + self::ASN1_INTEGER, + "\01\00", + $rsaOID, + 4, + $this->_encodeLength(strlen($RSAPrivateKey)), + $RSAPrivateKey + ); + $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + if (!empty($this->password) || is_string($this->password)) { + $salt = Random::string(8); + $iterationCount = 2048; + + $crypto = new DES(); + $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount); + $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey); + + $parameters = pack( + 'Ca*a*Ca*N', + self::ASN1_OCTETSTRING, + $this->_encodeLength(strlen($salt)), + $salt, + self::ASN1_INTEGER, + $this->_encodeLength(4), + $iterationCount + ); + $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03"; + + $encryptionAlgorithm = pack( + 'Ca*a*Ca*a*', + self::ASN1_OBJECT, + $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)), + $pbeWithMD5AndDES_CBC, + self::ASN1_SEQUENCE, + $this->_encodeLength(strlen($parameters)), + $parameters + ); + + $RSAPrivateKey = pack( + 'Ca*a*Ca*a*', + self::ASN1_SEQUENCE, + $this->_encodeLength(strlen($encryptionAlgorithm)), + $encryptionAlgorithm, + self::ASN1_OCTETSTRING, + $this->_encodeLength(strlen($RSAPrivateKey)), + $RSAPrivateKey + ); + + $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + + $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" . + chunk_split(base64_encode($RSAPrivateKey), 64) . + '-----END ENCRYPTED PRIVATE KEY-----'; + } else { + $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" . + chunk_split(base64_encode($RSAPrivateKey), 64) . + '-----END PRIVATE KEY-----'; + } + return $RSAPrivateKey; + } + + if (!empty($this->password) || is_string($this->password)) { + $iv = Random::string(8); + $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key + $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8); + $des = new TripleDES(); + $des->setKey($symkey); + $des->setIV($iv); + $iv = strtoupper(bin2hex($iv)); + $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . + "Proc-Type: 4,ENCRYPTED\r\n" . + "DEK-Info: DES-EDE3-CBC,$iv\r\n" . + "\r\n" . + chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) . + '-----END RSA PRIVATE KEY-----'; + } else { + $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . + chunk_split(base64_encode($RSAPrivateKey), 64) . + '-----END RSA PRIVATE KEY-----'; + } + + return $RSAPrivateKey; + } + } + + /** + * Convert a public key to the appropriate format + * + * @access private + * @see self::setPublicKeyFormat() + * @param Math_BigInteger $n + * @param Math_BigInteger $e + * @return string|array + */ + function _convertPublicKey($n, $e) + { + $signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML; + + $modulus = $n->toBytes($signed); + $publicExponent = $e->toBytes($signed); + + switch ($this->publicKeyFormat) { + case self::PUBLIC_FORMAT_RAW: + return array('e' => $e->copy(), 'n' => $n->copy()); + case self::PUBLIC_FORMAT_XML: + return "\r\n" . + ' ' . base64_encode($modulus) . "\r\n" . + ' ' . base64_encode($publicExponent) . "\r\n" . + ''; + break; + case self::PUBLIC_FORMAT_OPENSSH: + // from : + // string "ssh-rsa" + // mpint e + // mpint n + $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); + $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment; + + return $RSAPublicKey; + default: // eg. self::PUBLIC_FORMAT_PKCS1_RAW or self::PUBLIC_FORMAT_PKCS1 + // from : + // RSAPublicKey ::= SEQUENCE { + // modulus INTEGER, -- n + // publicExponent INTEGER -- e + // } + $components = array( + 'modulus' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus), + 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent) + ); + + $RSAPublicKey = pack( + 'Ca*a*a*', + self::ASN1_SEQUENCE, + $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), + $components['modulus'], + $components['publicExponent'] + ); + + if ($this->publicKeyFormat == self::PUBLIC_FORMAT_PKCS1_RAW) { + $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" . + chunk_split(base64_encode($RSAPublicKey), 64) . + '-----END RSA PUBLIC KEY-----'; + } else { + // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. + $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA + $RSAPublicKey = chr(0) . $RSAPublicKey; + $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey; + + $RSAPublicKey = pack( + 'Ca*a*', + self::ASN1_SEQUENCE, + $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), + $rsaOID . $RSAPublicKey + ); + + $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . + chunk_split(base64_encode($RSAPublicKey), 64) . + '-----END PUBLIC KEY-----'; + } + + return $RSAPublicKey; + } + } + + /** + * Break a public or private key down into its constituant components + * + * @access private + * @see self::_convertPublicKey() + * @see self::_convertPrivateKey() + * @param string|array $key + * @param int $type + * @return array|bool + */ + function _parseKey($key, $type) + { + if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) { + return false; + } + + switch ($type) { + case self::PUBLIC_FORMAT_RAW: + if (!is_array($key)) { + return false; + } + $components = array(); + switch (true) { + case isset($key['e']): + $components['publicExponent'] = $key['e']->copy(); + break; + case isset($key['exponent']): + $components['publicExponent'] = $key['exponent']->copy(); + break; + case isset($key['publicExponent']): + $components['publicExponent'] = $key['publicExponent']->copy(); + break; + case isset($key[0]): + $components['publicExponent'] = $key[0]->copy(); + } + switch (true) { + case isset($key['n']): + $components['modulus'] = $key['n']->copy(); + break; + case isset($key['modulo']): + $components['modulus'] = $key['modulo']->copy(); + break; + case isset($key['modulus']): + $components['modulus'] = $key['modulus']->copy(); + break; + case isset($key[1]): + $components['modulus'] = $key[1]->copy(); + } + return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; + case self::PRIVATE_FORMAT_PKCS1: + case self::PRIVATE_FORMAT_PKCS8: + case self::PUBLIC_FORMAT_PKCS1: + /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is + "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to + protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding + two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: + + http://tools.ietf.org/html/rfc1421#section-4.6.1.1 + http://tools.ietf.org/html/rfc1421#section-4.6.1.3 + + DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. + DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation + function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's + own implementation. ie. the implementation *is* the standard and any bugs that may exist in that + implementation are part of the standard, as well. + + * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ + if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { + $iv = pack('H*', trim($matches[2])); + $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key + $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8))); + // remove the Proc-Type / DEK-Info sections as they're no longer needed + $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); + $ciphertext = $this->_extractBER($key); + if ($ciphertext === false) { + $ciphertext = $key; + } + switch ($matches[1]) { + case 'AES-256-CBC': + $crypto = new AES(); + break; + case 'AES-128-CBC': + $symkey = substr($symkey, 0, 16); + $crypto = new AES(); + break; + case 'DES-EDE3-CFB': + $crypto = new TripleDES(Base::MODE_CFB); + break; + case 'DES-EDE3-CBC': + $symkey = substr($symkey, 0, 24); + $crypto = new TripleDES(); + break; + case 'DES-CBC': + $crypto = new DES(); + break; + default: + return false; + } + $crypto->setKey($symkey); + $crypto->setIV($iv); + $decoded = $crypto->decrypt($ciphertext); + } else { + $decoded = $this->_extractBER($key); + } + + if ($decoded !== false) { + $key = $decoded; + } + + $components = array(); + + if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + if ($this->_decodeLength($key) != strlen($key)) { + return false; + } + + $tag = ord($this->_string_shift($key)); + /* intended for keys for which OpenSSL's asn1parse returns the following: + + 0:d=0 hl=4 l= 631 cons: SEQUENCE + 4:d=1 hl=2 l= 1 prim: INTEGER :00 + 7:d=1 hl=2 l= 13 cons: SEQUENCE + 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 20:d=2 hl=2 l= 0 prim: NULL + 22:d=1 hl=4 l= 609 prim: OCTET STRING + + ie. PKCS8 keys*/ + + if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") { + $this->_string_shift($key, 3); + $tag = self::ASN1_SEQUENCE; + } + + if ($tag == self::ASN1_SEQUENCE) { + $temp = $this->_string_shift($key, $this->_decodeLength($key)); + if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) { + return false; + } + $length = $this->_decodeLength($temp); + switch ($this->_string_shift($temp, $length)) { + case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption + case "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0A": // rsaPSS + break; + case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC + /* + PBEParameter ::= SEQUENCE { + salt OCTET STRING (SIZE(8)), + iterationCount INTEGER } + */ + if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) { + return false; + } + if ($this->_decodeLength($temp) != strlen($temp)) { + return false; + } + $this->_string_shift($temp); // assume it's an octet string + $salt = $this->_string_shift($temp, $this->_decodeLength($temp)); + if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) { + return false; + } + $this->_decodeLength($temp); + list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT)); + $this->_string_shift($key); // assume it's an octet string + $length = $this->_decodeLength($key); + if (strlen($key) != $length) { + return false; + } + + $crypto = new DES(); + $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount); + $key = $crypto->decrypt($key); + if ($key === false) { + return false; + } + return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1); + default: + return false; + } + /* intended for keys for which OpenSSL's asn1parse returns the following: + + 0:d=0 hl=4 l= 290 cons: SEQUENCE + 4:d=1 hl=2 l= 13 cons: SEQUENCE + 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 17:d=2 hl=2 l= 0 prim: NULL + 19:d=1 hl=4 l= 271 prim: BIT STRING */ + $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag + $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length + // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of + // unused bits in the final subsequent octet. The number shall be in the range zero to seven." + // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2) + if ($tag == self::ASN1_BITSTRING) { + $this->_string_shift($key); + } + if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + if ($this->_decodeLength($key) != strlen($key)) { + return false; + } + $tag = ord($this->_string_shift($key)); + } + if ($tag != self::ASN1_INTEGER) { + return false; + } + + $length = $this->_decodeLength($key); + $temp = $this->_string_shift($key, $length); + if (strlen($temp) != 1 || ord($temp) > 2) { + $components['modulus'] = new BigInteger($temp, 256); + $this->_string_shift($key); // skip over self::ASN1_INTEGER + $length = $this->_decodeLength($key); + $components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256); + + return $components; + } + if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) { + return false; + } + $length = $this->_decodeLength($key); + $components['modulus'] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['publicExponent'] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['primes'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256)); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['exponents'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256)); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($key, $length), 256)); + + if (!empty($key)) { + if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + $this->_decodeLength($key); + while (!empty($key)) { + if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + $this->_decodeLength($key); + $key = substr($key, 1); + $length = $this->_decodeLength($key); + $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['coefficients'][] = new BigInteger($this->_string_shift($key, $length), 256); + } + } + + return $components; + case self::PUBLIC_FORMAT_OPENSSH: + $parts = explode(' ', $key, 3); + + $key = isset($parts[1]) ? base64_decode($parts[1]) : false; + if ($key === false) { + return false; + } + + $comment = isset($parts[2]) ? $parts[2] : false; + + $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa"; + + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($key, 4))); + $publicExponent = new BigInteger($this->_string_shift($key, $length), -256); + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($key, 4))); + $modulus = new BigInteger($this->_string_shift($key, $length), -256); + + if ($cleanup && strlen($key)) { + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($key, 4))); + $realModulus = new BigInteger($this->_string_shift($key, $length), -256); + return strlen($key) ? false : array( + 'modulus' => $realModulus, + 'publicExponent' => $modulus, + 'comment' => $comment + ); + } else { + return strlen($key) ? false : array( + 'modulus' => $modulus, + 'publicExponent' => $publicExponent, + 'comment' => $comment + ); + } + // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue + // http://en.wikipedia.org/wiki/XML_Signature + case self::PRIVATE_FORMAT_XML: + case self::PUBLIC_FORMAT_XML: + if (!extension_loaded('xml')) { + return false; + } + + $this->components = array(); + + $xml = xml_parser_create('UTF-8'); + xml_set_object($xml, $this); + xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler'); + xml_set_character_data_handler($xml, '_data_handler'); + // add to account for "dangling" tags like ... that are sometimes added + if (!xml_parse($xml, '' . $key . '')) { + xml_parser_free($xml); + unset($xml); + return false; + } + + xml_parser_free($xml); + unset($xml); + + return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false; + // see PuTTY's SSHPUBK.C and https://tartarus.org/~simon/putty-snapshots/htmldoc/AppendixC.html + case self::PRIVATE_FORMAT_PUTTY: + $components = array(); + $key = preg_split('#\r\n|\r|\n#', $key); + if ($this->_string_shift($key[0], strlen('PuTTY-User-Key-File-')) != 'PuTTY-User-Key-File-') { + return false; + } + $version = (int) $this->_string_shift($key[0], 3); // should be either "2: " or "3: 0" prior to int casting + if ($version != 2 && $version != 3) { + return false; + } + $type = rtrim($key[0]); + if ($type != 'ssh-rsa') { + return false; + } + $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1])); + $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2])); + + $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); + $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); + $public = substr($public, 11); + extract(unpack('Nlength', $this->_string_shift($public, 4))); + $components['publicExponent'] = new BigInteger($this->_string_shift($public, $length), -256); + extract(unpack('Nlength', $this->_string_shift($public, 4))); + $components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256); + + $offset = $publicLength + 4; + switch ($encryption) { + case 'aes256-cbc': + $crypto = new AES(); + switch ($version) { + case 3: + if (!function_exists('sodium_crypto_pwhash')) { + return false; + } + $flavour = trim(preg_replace('#Key-Derivation: (.*)#', '$1', $key[$offset++])); + switch ($flavour) { + case 'Argon2i': + $flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13; + break; + case 'Argon2id': + $flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13; + break; + default: + return false; + } + $memory = trim(preg_replace('#Argon2-Memory: (\d+)#', '$1', $key[$offset++])); + $passes = trim(preg_replace('#Argon2-Passes: (\d+)#', '$1', $key[$offset++])); + $parallelism = trim(preg_replace('#Argon2-Parallelism: (\d+)#', '$1', $key[$offset++])); + $salt = pack('H*', trim(preg_replace('#Argon2-Salt: ([0-9a-f]+)#', '$1', $key[$offset++]))); + + $length = 80; // keylen + ivlen + mac_keylen + $temp = sodium_crypto_pwhash($length, $this->password, $salt, $passes, $memory << 10, $flavour); + + $symkey = substr($temp, 0, 32); + $symiv = substr($temp, 32, 16); + break; + case 2: + $symkey = ''; + $sequence = 0; + while (strlen($symkey) < 32) { + $temp = pack('Na*', $sequence++, $this->password); + $symkey.= pack('H*', sha1($temp)); + } + $symkey = substr($symkey, 0, 32); + $symiv = str_repeat("\0", 16); + } + } + + $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$offset++])); + $private = base64_decode(implode('', array_map('trim', array_slice($key, $offset, $privateLength)))); + + if ($encryption != 'none') { + $crypto->setKey($symkey); + $crypto->setIV($symiv); + $crypto->disablePadding(); + $private = $crypto->decrypt($private); + if ($private === false) { + return false; + } + } + + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['privateExponent'] = new BigInteger($this->_string_shift($private, $length), -256); + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['primes'] = array(1 => new BigInteger($this->_string_shift($private, $length), -256)); + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['primes'][] = new BigInteger($this->_string_shift($private, $length), -256); + + $temp = $components['primes'][1]->subtract($this->one); + $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); + $temp = $components['primes'][2]->subtract($this->one); + $components['exponents'][] = $components['publicExponent']->modInverse($temp); + + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256)); + + return $components; + case self::PRIVATE_FORMAT_OPENSSH: + $components = array(); + $decoded = $this->_extractBER($key); + $magic = $this->_string_shift($decoded, 15); + if ($magic !== "openssh-key-v1\0") { + return false; + } + extract(unpack('Nlength', $this->_string_shift($decoded, 4))); + if (strlen($decoded) < $length) { + return false; + } + $ciphername = $this->_string_shift($decoded, $length); + extract(unpack('Nlength', $this->_string_shift($decoded, 4))); + if (strlen($decoded) < $length) { + return false; + } + $kdfname = $this->_string_shift($decoded, $length); + extract(unpack('Nlength', $this->_string_shift($decoded, 4))); + if (strlen($decoded) < $length) { + return false; + } + $kdfoptions = $this->_string_shift($decoded, $length); + extract(unpack('Nnumkeys', $this->_string_shift($decoded, 4))); + if ($numkeys != 1 || ($ciphername != 'none' && $kdfname != 'bcrypt')) { + return false; + } + switch ($ciphername) { + case 'none': + break; + case 'aes256-ctr': + extract(unpack('Nlength', $this->_string_shift($kdfoptions, 4))); + if (strlen($kdfoptions) < $length) { + return false; + } + $salt = $this->_string_shift($kdfoptions, $length); + extract(unpack('Nrounds', $this->_string_shift($kdfoptions, 4))); + $crypto = new AES(AES::MODE_CTR); + $crypto->disablePadding(); + if (!$crypto->setPassword($this->password, 'bcrypt', $salt, $rounds, 32)) { + return false; + } + break; + default: + return false; + } + extract(unpack('Nlength', $this->_string_shift($decoded, 4))); + if (strlen($decoded) < $length) { + return false; + } + $publicKey = $this->_string_shift($decoded, $length); + extract(unpack('Nlength', $this->_string_shift($decoded, 4))); + if (strlen($decoded) < $length) { + return false; + } + + if ($this->_string_shift($publicKey, 11) !== "\0\0\0\7ssh-rsa") { + return false; + } + + $paddedKey = $this->_string_shift($decoded, $length); + if (isset($crypto)) { + $paddedKey = $crypto->decrypt($paddedKey); + } + + $checkint1 = $this->_string_shift($paddedKey, 4); + $checkint2 = $this->_string_shift($paddedKey, 4); + if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) { + return false; + } + + if ($this->_string_shift($paddedKey, 11) !== "\0\0\0\7ssh-rsa") { + return false; + } + + $values = array( + &$components['modulus'], + &$components['publicExponent'], + &$components['privateExponent'], + &$components['coefficients'][2], + &$components['primes'][1], + &$components['primes'][2] + ); + + foreach ($values as &$value) { + extract(unpack('Nlength', $this->_string_shift($paddedKey, 4))); + if (strlen($paddedKey) < $length) { + return false; + } + $value = new BigInteger($this->_string_shift($paddedKey, $length), -256); + } + + extract(unpack('Nlength', $this->_string_shift($paddedKey, 4))); + if (strlen($paddedKey) < $length) { + return false; + } + $components['comment'] = $this->_string_shift($decoded, $length); + + $temp = $components['primes'][1]->subtract($this->one); + $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); + $temp = $components['primes'][2]->subtract($this->one); + $components['exponents'][] = $components['publicExponent']->modInverse($temp); + + return $components; + } + + return false; + } + + /** + * Returns the key size + * + * More specifically, this returns the size of the modulo in bits. + * + * @access public + * @return int + */ + function getSize() + { + return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits()); + } + + /** + * Start Element Handler + * + * Called by xml_set_element_handler() + * + * @access private + * @param resource $parser + * @param string $name + * @param array $attribs + */ + function _start_element_handler($parser, $name, $attribs) + { + //$name = strtoupper($name); + switch ($name) { + case 'MODULUS': + $this->current = &$this->components['modulus']; + break; + case 'EXPONENT': + $this->current = &$this->components['publicExponent']; + break; + case 'P': + $this->current = &$this->components['primes'][1]; + break; + case 'Q': + $this->current = &$this->components['primes'][2]; + break; + case 'DP': + $this->current = &$this->components['exponents'][1]; + break; + case 'DQ': + $this->current = &$this->components['exponents'][2]; + break; + case 'INVERSEQ': + $this->current = &$this->components['coefficients'][2]; + break; + case 'D': + $this->current = &$this->components['privateExponent']; + } + $this->current = ''; + } + + /** + * Stop Element Handler + * + * Called by xml_set_element_handler() + * + * @access private + * @param resource $parser + * @param string $name + */ + function _stop_element_handler($parser, $name) + { + if (isset($this->current)) { + $this->current = new BigInteger(base64_decode($this->current), 256); + unset($this->current); + } + } + + /** + * Data Handler + * + * Called by xml_set_character_data_handler() + * + * @access private + * @param resource $parser + * @param string $data + */ + function _data_handler($parser, $data) + { + if (!isset($this->current) || is_object($this->current)) { + return; + } + $this->current.= trim($data); + } + + /** + * Loads a public or private key + * + * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed) + * + * @access public + * @param string|RSA|array $key + * @param bool|int $type optional + * @return bool + */ + function loadKey($key, $type = false) + { + if ($key instanceof RSA) { + $this->privateKeyFormat = $key->privateKeyFormat; + $this->publicKeyFormat = $key->publicKeyFormat; + $this->k = $key->k; + $this->hLen = $key->hLen; + $this->sLen = $key->sLen; + $this->mgfHLen = $key->mgfHLen; + $this->encryptionMode = $key->encryptionMode; + $this->signatureMode = $key->signatureMode; + $this->password = $key->password; + $this->configFile = $key->configFile; + $this->comment = $key->comment; + + if (is_object($key->hash)) { + $this->hash = new Hash($key->hash->getHash()); + } + if (is_object($key->mgfHash)) { + $this->mgfHash = new Hash($key->mgfHash->getHash()); + } + + if (is_object($key->modulus)) { + $this->modulus = $key->modulus->copy(); + } + if (is_object($key->exponent)) { + $this->exponent = $key->exponent->copy(); + } + if (is_object($key->publicExponent)) { + $this->publicExponent = $key->publicExponent->copy(); + } + + $this->primes = array(); + $this->exponents = array(); + $this->coefficients = array(); + + foreach ($this->primes as $prime) { + $this->primes[] = $prime->copy(); + } + foreach ($this->exponents as $exponent) { + $this->exponents[] = $exponent->copy(); + } + foreach ($this->coefficients as $coefficient) { + $this->coefficients[] = $coefficient->copy(); + } + + return true; + } + + if ($type === false) { + $types = array( + self::PUBLIC_FORMAT_RAW, + self::PRIVATE_FORMAT_PKCS1, + self::PRIVATE_FORMAT_XML, + self::PRIVATE_FORMAT_PUTTY, + self::PUBLIC_FORMAT_OPENSSH, + self::PRIVATE_FORMAT_OPENSSH + ); + foreach ($types as $type) { + $components = $this->_parseKey($key, $type); + if ($components !== false) { + break; + } + } + } else { + $components = $this->_parseKey($key, $type); + } + + if ($components === false) { + $this->comment = null; + $this->modulus = null; + $this->k = null; + $this->exponent = null; + $this->primes = null; + $this->exponents = null; + $this->coefficients = null; + $this->publicExponent = null; + + return false; + } + + if (isset($components['comment']) && $components['comment'] !== false) { + $this->comment = $components['comment']; + } + $this->modulus = $components['modulus']; + $this->k = strlen($this->modulus->toBytes()); + $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent']; + if (isset($components['primes'])) { + $this->primes = $components['primes']; + $this->exponents = $components['exponents']; + $this->coefficients = $components['coefficients']; + $this->publicExponent = $components['publicExponent']; + } else { + $this->primes = array(); + $this->exponents = array(); + $this->coefficients = array(); + $this->publicExponent = false; + } + + switch ($type) { + case self::PUBLIC_FORMAT_OPENSSH: + case self::PUBLIC_FORMAT_RAW: + $this->setPublicKey(); + break; + case self::PRIVATE_FORMAT_PKCS1: + switch (true) { + case strpos($key, '-BEGIN PUBLIC KEY-') !== false: + case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false: + $this->setPublicKey(); + } + } + + return true; + } + + /** + * Sets the password + * + * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false. + * Or rather, pass in $password such that empty($password) && !is_string($password) is true. + * + * @see self::createKey() + * @see self::loadKey() + * @access public + * @param string $password + */ + function setPassword($password = false) + { + $this->password = $password; + } + + /** + * Defines the public key + * + * Some private key formats define the public exponent and some don't. Those that don't define it are problematic when + * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a + * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys + * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public + * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used + * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being + * public. + * + * Do note that when a new key is loaded the index will be cleared. + * + * Returns true on success, false on failure + * + * @see self::getPublicKey() + * @access public + * @param string $key optional + * @param int $type optional + * @return bool + */ + function setPublicKey($key = false, $type = false) + { + // if a public key has already been loaded return false + if (!empty($this->publicExponent)) { + return false; + } + + if ($key === false && !empty($this->modulus)) { + $this->publicExponent = $this->exponent; + return true; + } + + if ($type === false) { + $types = array( + self::PUBLIC_FORMAT_RAW, + self::PUBLIC_FORMAT_PKCS1, + self::PUBLIC_FORMAT_XML, + self::PUBLIC_FORMAT_OPENSSH + ); + foreach ($types as $type) { + $components = $this->_parseKey($key, $type); + if ($components !== false) { + break; + } + } + } else { + $components = $this->_parseKey($key, $type); + } + + if ($components === false) { + return false; + } + + if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) { + $this->modulus = $components['modulus']; + $this->exponent = $this->publicExponent = $components['publicExponent']; + return true; + } + + $this->publicExponent = $components['publicExponent']; + + return true; + } + + /** + * Defines the private key + * + * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force + * phpseclib to treat the key as a private key. This function will do that. + * + * Do note that when a new key is loaded the index will be cleared. + * + * Returns true on success, false on failure + * + * @see self::getPublicKey() + * @access public + * @param string $key optional + * @param int $type optional + * @return bool + */ + function setPrivateKey($key = false, $type = false) + { + if ($key === false && !empty($this->publicExponent)) { + $this->publicExponent = false; + return true; + } + + $rsa = new RSA(); + if (!$rsa->loadKey($key, $type)) { + return false; + } + $rsa->publicExponent = false; + + // don't overwrite the old key if the new key is invalid + $this->loadKey($rsa); + return true; + } + + /** + * Returns the public key + * + * The public key is only returned under two circumstances - if the private key had the public key embedded within it + * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this + * function won't return it since this library, for the most part, doesn't distinguish between public and private keys. + * + * @see self::getPublicKey() + * @access public + * @param int $type optional + */ + function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8) + { + if (empty($this->modulus) || empty($this->publicExponent)) { + return false; + } + + $oldFormat = $this->publicKeyFormat; + $this->publicKeyFormat = $type; + $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent); + $this->publicKeyFormat = $oldFormat; + return $temp; + } + + /** + * Returns the public key's fingerprint + * + * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is + * no public key currently loaded, false is returned. + * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716) + * + * @access public + * @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned + * for invalid values. + * @return mixed + */ + function getPublicKeyFingerprint($algorithm = 'md5') + { + if (empty($this->modulus) || empty($this->publicExponent)) { + return false; + } + + $modulus = $this->modulus->toBytes(true); + $publicExponent = $this->publicExponent->toBytes(true); + + $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); + + switch ($algorithm) { + case 'sha256': + $hash = new Hash('sha256'); + $base = base64_encode($hash->hash($RSAPublicKey)); + return substr($base, 0, strlen($base) - 1); + case 'md5': + return substr(chunk_split(md5($RSAPublicKey), 2, ':'), 0, -1); + default: + return false; + } + } + + /** + * Returns the private key + * + * The private key is only returned if the currently loaded key contains the constituent prime numbers. + * + * @see self::getPublicKey() + * @access public + * @param int $type optional + * @return mixed + */ + function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1) + { + if (empty($this->primes)) { + return false; + } + + $oldFormat = $this->privateKeyFormat; + $this->privateKeyFormat = $type; + $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients); + $this->privateKeyFormat = $oldFormat; + return $temp; + } + + /** + * Returns a minimalistic private key + * + * Returns the private key without the prime number constituants. Structurally identical to a public key that + * hasn't been set as the public key + * + * @see self::getPrivateKey() + * @access private + * @param int $mode optional + */ + function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8) + { + if (empty($this->modulus) || empty($this->exponent)) { + return false; + } + + $oldFormat = $this->publicKeyFormat; + $this->publicKeyFormat = $mode; + $temp = $this->_convertPublicKey($this->modulus, $this->exponent); + $this->publicKeyFormat = $oldFormat; + return $temp; + } + + /** + * __toString() magic method + * + * @access public + * @return string + */ + function __toString() + { + $key = $this->getPrivateKey($this->privateKeyFormat); + if ($key !== false) { + return $key; + } + $key = $this->_getPrivatePublicKey($this->publicKeyFormat); + return $key !== false ? $key : ''; + } + + /** + * __clone() magic method + * + * @access public + * @return Crypt_RSA + */ + function __clone() + { + $key = new RSA(); + $key->loadKey($this); + return $key; + } + + /** + * Generates the smallest and largest numbers requiring $bits bits + * + * @access private + * @param int $bits + * @return array + */ + function _generateMinMax($bits) + { + $bytes = $bits >> 3; + $min = str_repeat(chr(0), $bytes); + $max = str_repeat(chr(0xFF), $bytes); + $msb = $bits & 7; + if ($msb) { + $min = chr(1 << ($msb - 1)) . $min; + $max = chr((1 << $msb) - 1) . $max; + } else { + $min[0] = chr(0x80); + } + + return array( + 'min' => new BigInteger($min, 256), + 'max' => new BigInteger($max, 256) + ); + } + + /** + * DER-decode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. + * + * @access private + * @param string $string + * @return int + */ + function _decodeLength(&$string) + { + $length = ord($this->_string_shift($string)); + if ($length & 0x80) { // definite length, long form + $length&= 0x7F; + $temp = $this->_string_shift($string, $length); + list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)); + } + return $length; + } + + /** + * DER-encode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. + * + * @access private + * @param int $length + * @return string + */ + function _encodeLength($length) + { + if ($length <= 0x7F) { + return chr($length); + } + + $temp = ltrim(pack('N', $length), chr(0)); + return pack('Ca*', 0x80 | strlen($temp), $temp); + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * Determines the private key format + * + * @see self::createKey() + * @access public + * @param int $format + */ + function setPrivateKeyFormat($format) + { + $this->privateKeyFormat = $format; + } + + /** + * Determines the public key format + * + * @see self::createKey() + * @access public + * @param int $format + */ + function setPublicKeyFormat($format) + { + $this->publicKeyFormat = $format; + } + + /** + * Determines which hashing function should be used + * + * Used with signature production / verification and (if the encryption mode is self::ENCRYPTION_OAEP) encryption and + * decryption. If $hash isn't supported, sha1 is used. + * + * @access public + * @param string $hash + */ + function setHash($hash) + { + // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. + switch ($hash) { + case 'md2': + case 'md5': + case 'sha1': + case 'sha256': + case 'sha384': + case 'sha512': + $this->hash = new Hash($hash); + $this->hashName = $hash; + break; + default: + $this->hash = new Hash('sha1'); + $this->hashName = 'sha1'; + } + $this->hLen = $this->hash->getLength(); + } + + /** + * Determines which hashing function should be used for the mask generation function + * + * The mask generation function is used by self::ENCRYPTION_OAEP and self::SIGNATURE_PSS and although it's + * best if Hash and MGFHash are set to the same thing this is not a requirement. + * + * @access public + * @param string $hash + */ + function setMGFHash($hash) + { + // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. + switch ($hash) { + case 'md2': + case 'md5': + case 'sha1': + case 'sha256': + case 'sha384': + case 'sha512': + $this->mgfHash = new Hash($hash); + break; + default: + $this->mgfHash = new Hash('sha1'); + } + $this->mgfHLen = $this->mgfHash->getLength(); + } + + /** + * Determines the salt length + * + * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}: + * + * Typical salt lengths in octets are hLen (the length of the output + * of the hash function Hash) and 0. + * + * @access public + * @param int $sLen + */ + function setSaltLength($sLen) + { + $this->sLen = $sLen; + } + + /** + * Integer-to-Octet-String primitive + * + * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}. + * + * @access private + * @param \phpseclib\Math\BigInteger $x + * @param int $xLen + * @return string + */ + function _i2osp($x, $xLen) + { + $x = $x->toBytes(); + if (strlen($x) > $xLen) { + user_error('Integer too large'); + return false; + } + return str_pad($x, $xLen, chr(0), STR_PAD_LEFT); + } + + /** + * Octet-String-to-Integer primitive + * + * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}. + * + * @access private + * @param int|string|resource $x + * @return \phpseclib\Math\BigInteger + */ + function _os2ip($x) + { + return new BigInteger($x, 256); + } + + /** + * Exponentiate with or without Chinese Remainder Theorem + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}. + * + * @access private + * @param \phpseclib\Math\BigInteger $x + * @return \phpseclib\Math\BigInteger + */ + function _exponentiate($x) + { + switch (true) { + case empty($this->primes): + case $this->primes[1]->equals($this->zero): + case empty($this->coefficients): + case $this->coefficients[2]->equals($this->zero): + case empty($this->exponents): + case $this->exponents[1]->equals($this->zero): + return $x->modPow($this->exponent, $this->modulus); + } + + $num_primes = count($this->primes); + + if (defined('CRYPT_RSA_DISABLE_BLINDING')) { + $m_i = array( + 1 => $x->modPow($this->exponents[1], $this->primes[1]), + 2 => $x->modPow($this->exponents[2], $this->primes[2]) + ); + $h = $m_i[1]->subtract($m_i[2]); + $h = $h->multiply($this->coefficients[2]); + list(, $h) = $h->divide($this->primes[1]); + $m = $m_i[2]->add($h->multiply($this->primes[2])); + + $r = $this->primes[1]; + for ($i = 3; $i <= $num_primes; $i++) { + $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]); + + $r = $r->multiply($this->primes[$i - 1]); + + $h = $m_i->subtract($m); + $h = $h->multiply($this->coefficients[$i]); + list(, $h) = $h->divide($this->primes[$i]); + + $m = $m->add($r->multiply($h)); + } + } else { + $smallest = $this->primes[1]; + for ($i = 2; $i <= $num_primes; $i++) { + if ($smallest->compare($this->primes[$i]) > 0) { + $smallest = $this->primes[$i]; + } + } + + $one = new BigInteger(1); + + $r = $one->random($one, $smallest->subtract($one)); + + $m_i = array( + 1 => $this->_blind($x, $r, 1), + 2 => $this->_blind($x, $r, 2) + ); + $h = $m_i[1]->subtract($m_i[2]); + $h = $h->multiply($this->coefficients[2]); + list(, $h) = $h->divide($this->primes[1]); + $m = $m_i[2]->add($h->multiply($this->primes[2])); + + $r = $this->primes[1]; + for ($i = 3; $i <= $num_primes; $i++) { + $m_i = $this->_blind($x, $r, $i); + + $r = $r->multiply($this->primes[$i - 1]); + + $h = $m_i->subtract($m); + $h = $h->multiply($this->coefficients[$i]); + list(, $h) = $h->divide($this->primes[$i]); + + $m = $m->add($r->multiply($h)); + } + } + + return $m; + } + + /** + * Performs RSA Blinding + * + * Protects against timing attacks by employing RSA Blinding. + * Returns $x->modPow($this->exponents[$i], $this->primes[$i]) + * + * @access private + * @param \phpseclib\Math\BigInteger $x + * @param \phpseclib\Math\BigInteger $r + * @param int $i + * @return \phpseclib\Math\BigInteger + */ + function _blind($x, $r, $i) + { + $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i])); + $x = $x->modPow($this->exponents[$i], $this->primes[$i]); + + $r = $r->modInverse($this->primes[$i]); + $x = $x->multiply($r); + list(, $x) = $x->divide($this->primes[$i]); + + return $x; + } + + /** + * Performs blinded RSA equality testing + * + * Protects against a particular type of timing attack described. + * + * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)} + * + * Thanks for the heads up singpolyma! + * + * @access private + * @param string $x + * @param string $y + * @return bool + */ + function _equals($x, $y) + { + if (function_exists('hash_equals')) { + return hash_equals($x, $y); + } + + if (strlen($x) != strlen($y)) { + return false; + } + + $result = "\0"; + $x^= $y; + for ($i = 0; $i < strlen($x); $i++) { + $result|= $x[$i]; + } + + return $result === "\0"; + } + + /** + * RSAEP + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}. + * + * @access private + * @param \phpseclib\Math\BigInteger $m + * @return \phpseclib\Math\BigInteger + */ + function _rsaep($m) + { + if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { + user_error('Message representative out of range'); + return false; + } + return $this->_exponentiate($m); + } + + /** + * RSADP + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}. + * + * @access private + * @param \phpseclib\Math\BigInteger $c + * @return \phpseclib\Math\BigInteger + */ + function _rsadp($c) + { + if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) { + user_error('Ciphertext representative out of range'); + return false; + } + return $this->_exponentiate($c); + } + + /** + * RSASP1 + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}. + * + * @access private + * @param \phpseclib\Math\BigInteger $m + * @return \phpseclib\Math\BigInteger + */ + function _rsasp1($m) + { + if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { + user_error('Message representative out of range'); + return false; + } + return $this->_exponentiate($m); + } + + /** + * RSAVP1 + * + * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}. + * + * @access private + * @param \phpseclib\Math\BigInteger $s + * @return \phpseclib\Math\BigInteger + */ + function _rsavp1($s) + { + if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) { + user_error('Signature representative out of range'); + return false; + } + return $this->_exponentiate($s); + } + + /** + * MGF1 + * + * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}. + * + * @access private + * @param string $mgfSeed + * @param int $maskLen + * @return string + */ + function _mgf1($mgfSeed, $maskLen) + { + // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output. + + $t = ''; + $count = ceil($maskLen / $this->mgfHLen); + for ($i = 0; $i < $count; $i++) { + $c = pack('N', $i); + $t.= $this->mgfHash->hash($mgfSeed . $c); + } + + return substr($t, 0, $maskLen); + } + + /** + * RSAES-OAEP-ENCRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and + * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}. + * + * @access private + * @param string $m + * @param string $l + * @return string + */ + function _rsaes_oaep_encrypt($m, $l = '') + { + $mLen = strlen($m); + + // Length checking + + // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + + if ($mLen > $this->k - 2 * $this->hLen - 2) { + user_error('Message too long'); + return false; + } + + // EME-OAEP encoding + + $lHash = $this->hash->hash($l); + $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2); + $db = $lHash . $ps . chr(1) . $m; + $seed = Random::string($this->hLen); + $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1); + $maskedDB = $db ^ $dbMask; + $seedMask = $this->_mgf1($maskedDB, $this->hLen); + $maskedSeed = $seed ^ $seedMask; + $em = chr(0) . $maskedSeed . $maskedDB; + + // RSA encryption + + $m = $this->_os2ip($em); + $c = $this->_rsaep($m); + $c = $this->_i2osp($c, $this->k); + + // Output the ciphertext C + + return $c; + } + + /** + * RSAES-OAEP-DECRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error + * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2: + * + * Note. Care must be taken to ensure that an opponent cannot + * distinguish the different error conditions in Step 3.g, whether by + * error message or timing, or, more generally, learn partial + * information about the encoded message EM. Otherwise an opponent may + * be able to obtain useful information about the decryption of the + * ciphertext C, leading to a chosen-ciphertext attack such as the one + * observed by Manger [36]. + * + * As for $l... to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}: + * + * Both the encryption and the decryption operations of RSAES-OAEP take + * the value of a label L as input. In this version of PKCS #1, L is + * the empty string; other uses of the label are outside the scope of + * this document. + * + * @access private + * @param string $c + * @param string $l + * @return string + */ + function _rsaes_oaep_decrypt($c, $l = '') + { + // Length checking + + // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + + if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { + user_error('Decryption error'); + return false; + } + + // RSA decryption + + $c = $this->_os2ip($c); + $m = $this->_rsadp($c); + if ($m === false) { + user_error('Decryption error'); + return false; + } + $em = $this->_i2osp($m, $this->k); + + // EME-OAEP decoding + + $lHash = $this->hash->hash($l); + $y = ord($em[0]); + $maskedSeed = substr($em, 1, $this->hLen); + $maskedDB = substr($em, $this->hLen + 1); + $seedMask = $this->_mgf1($maskedDB, $this->hLen); + $seed = $maskedSeed ^ $seedMask; + $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1); + $db = $maskedDB ^ $dbMask; + $lHash2 = substr($db, 0, $this->hLen); + $m = substr($db, $this->hLen); + $hashesMatch = $this->_equals($lHash, $lHash2); + $leadingZeros = 1; + $patternMatch = 0; + $offset = 0; + for ($i = 0; $i < strlen($m); $i++) { + $patternMatch|= $leadingZeros & ($m[$i] === "\1"); + $leadingZeros&= $m[$i] === "\0"; + $offset+= $patternMatch ? 0 : 1; + } + + // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation + // to protect against timing attacks + if (!$hashesMatch | !$patternMatch) { + user_error('Decryption error'); + return false; + } + + // Output the message M + + return substr($m, $offset + 1); + } + + /** + * Raw Encryption / Decryption + * + * Doesn't use padding and is not recommended. + * + * @access private + * @param string $m + * @return string + */ + function _raw_encrypt($m) + { + $temp = $this->_os2ip($m); + $temp = $this->_rsaep($temp); + return $this->_i2osp($temp, $this->k); + } + + /** + * RSAES-PKCS1-V1_5-ENCRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}. + * + * @access private + * @param string $m + * @return string + */ + function _rsaes_pkcs1_v1_5_encrypt($m) + { + $mLen = strlen($m); + + // Length checking + + if ($mLen > $this->k - 11) { + user_error('Message too long'); + return false; + } + + // EME-PKCS1-v1_5 encoding + + $psLen = $this->k - $mLen - 3; + $ps = ''; + while (strlen($ps) != $psLen) { + $temp = Random::string($psLen - strlen($ps)); + $temp = str_replace("\x00", '', $temp); + $ps.= $temp; + } + $type = 2; + // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done + if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) { + $type = 1; + // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF" + $ps = str_repeat("\xFF", $psLen); + } + $em = chr(0) . chr($type) . $ps . chr(0) . $m; + + // RSA encryption + $m = $this->_os2ip($em); + $c = $this->_rsaep($m); + $c = $this->_i2osp($c, $this->k); + + // Output the ciphertext C + + return $c; + } + + /** + * RSAES-PKCS1-V1_5-DECRYPT + * + * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}. + * + * For compatibility purposes, this function departs slightly from the description given in RFC3447. + * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the + * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the + * public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed + * to be 2 regardless of which key is used. For compatibility purposes, we'll just check to make sure the + * second byte is 2 or less. If it is, we'll accept the decrypted string as valid. + * + * As a consequence of this, a private key encrypted ciphertext produced with \phpseclib\Crypt\RSA may not decrypt + * with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but + * not private key encrypted ciphertext's. + * + * @access private + * @param string $c + * @return string + */ + function _rsaes_pkcs1_v1_5_decrypt($c) + { + // Length checking + + if (strlen($c) != $this->k) { // or if k < 11 + user_error('Decryption error'); + return false; + } + + // RSA decryption + + $c = $this->_os2ip($c); + $m = $this->_rsadp($c); + + if ($m === false) { + user_error('Decryption error'); + return false; + } + $em = $this->_i2osp($m, $this->k); + + // EME-PKCS1-v1_5 decoding + + if (ord($em[0]) != 0 || ord($em[1]) > 2) { + user_error('Decryption error'); + return false; + } + + $ps = substr($em, 2, strpos($em, chr(0), 2) - 2); + $m = substr($em, strlen($ps) + 3); + + if (strlen($ps) < 8) { + user_error('Decryption error'); + return false; + } + + // Output M + + return $m; + } + + /** + * EMSA-PSS-ENCODE + * + * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}. + * + * @access private + * @param string $m + * @param int $emBits + */ + function _emsa_pss_encode($m, $emBits) + { + // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + + $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8) + $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; + + $mHash = $this->hash->hash($m); + if ($emLen < $this->hLen + $sLen + 2) { + user_error('Encoding error'); + return false; + } + + $salt = Random::string($sLen); + $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; + $h = $this->hash->hash($m2); + $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2); + $db = $ps . chr(1) . $salt; + $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1); + $maskedDB = $db ^ $dbMask; + $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0]; + $em = $maskedDB . $h . chr(0xBC); + + return $em; + } + + /** + * EMSA-PSS-VERIFY + * + * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}. + * + * @access private + * @param string $m + * @param string $em + * @param int $emBits + * @return string + */ + function _emsa_pss_verify($m, $em, $emBits) + { + // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error + // be output. + + $emLen = ($emBits + 7) >> 3; // ie. ceil($emBits / 8); + $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; + + $mHash = $this->hash->hash($m); + if ($emLen < $this->hLen + $sLen + 2) { + return false; + } + + if ($em[strlen($em) - 1] != chr(0xBC)) { + return false; + } + + $maskedDB = substr($em, 0, -$this->hLen - 1); + $h = substr($em, -$this->hLen - 1, $this->hLen); + $temp = chr(0xFF << ($emBits & 7)); + if ((~$maskedDB[0] & $temp) != $temp) { + return false; + } + $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1); + $db = $maskedDB ^ $dbMask; + $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0]; + $temp = $emLen - $this->hLen - $sLen - 2; + if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) { + return false; + } + $salt = substr($db, $temp + 1); // should be $sLen long + $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; + $h2 = $this->hash->hash($m2); + return $this->_equals($h, $h2); + } + + /** + * RSASSA-PSS-SIGN + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}. + * + * @access private + * @param string $m + * @return string + */ + function _rsassa_pss_sign($m) + { + // EMSA-PSS encoding + + $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1); + + // RSA signature + + $m = $this->_os2ip($em); + $s = $this->_rsasp1($m); + $s = $this->_i2osp($s, $this->k); + + // Output the signature S + + return $s; + } + + /** + * RSASSA-PSS-VERIFY + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}. + * + * @access private + * @param string $m + * @param string $s + * @return string + */ + function _rsassa_pss_verify($m, $s) + { + // Length checking + + if (strlen($s) != $this->k) { + user_error('Invalid signature'); + return false; + } + + // RSA verification + + $modBits = strlen($this->modulus->toBits()); + + $s2 = $this->_os2ip($s); + $m2 = $this->_rsavp1($s2); + if ($m2 === false) { + user_error('Invalid signature'); + return false; + } + $em = $this->_i2osp($m2, $this->k); + if ($em === false) { + user_error('Invalid signature'); + return false; + } + + // EMSA-PSS verification + + return $this->_emsa_pss_verify($m, $em, $modBits - 1); + } + + /** + * EMSA-PKCS1-V1_5-ENCODE + * + * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}. + * + * @access private + * @param string $m + * @param int $emLen + * @return string + */ + function _emsa_pkcs1_v1_5_encode($m, $emLen) + { + $h = $this->hash->hash($m); + if ($h === false) { + return false; + } + + // see http://tools.ietf.org/html/rfc3447#page-43 + switch ($this->hashName) { + case 'md2': + $t = pack('H*', '3020300c06082a864886f70d020205000410'); + break; + case 'md5': + $t = pack('H*', '3020300c06082a864886f70d020505000410'); + break; + case 'sha1': + $t = pack('H*', '3021300906052b0e03021a05000414'); + break; + case 'sha256': + $t = pack('H*', '3031300d060960864801650304020105000420'); + break; + case 'sha384': + $t = pack('H*', '3041300d060960864801650304020205000430'); + break; + case 'sha512': + $t = pack('H*', '3051300d060960864801650304020305000440'); + } + $t.= $h; + $tLen = strlen($t); + + if ($emLen < $tLen + 11) { + user_error('Intended encoded message length too short'); + return false; + } + + $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); + + $em = "\0\1$ps\0$t"; + + return $em; + } + + /** + * EMSA-PKCS1-V1_5-ENCODE (without NULL) + * + * Quoting https://tools.ietf.org/html/rfc8017#page-65, + * + * "The parameters field associated with id-sha1, id-sha224, id-sha256, + * id-sha384, id-sha512, id-sha512/224, and id-sha512/256 should + * generally be omitted, but if present, it shall have a value of type + * NULL" + * + * @access private + * @param string $m + * @param int $emLen + * @return string + */ + function _emsa_pkcs1_v1_5_encode_without_null($m, $emLen) + { + $h = $this->hash->hash($m); + if ($h === false) { + return false; + } + + switch ($this->hashName) { + case 'sha1': + $t = pack('H*', '301f300706052b0e03021a0414'); + break; + case 'sha256': + $t = pack('H*', '302f300b06096086480165030402010420'); + break; + case 'sha384': + $t = pack('H*', '303f300b06096086480165030402020430'); + break; + case 'sha512': + $t = pack('H*', '304f300b06096086480165030402030440'); + break; + default: + return false; + } + $t.= $h; + $tLen = strlen($t); + + if ($emLen < $tLen + 11) { + user_error('Intended encoded message length too short'); + return false; + } + + $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); + + $em = "\0\1$ps\0$t"; + + return $em; + } + + /** + * RSASSA-PKCS1-V1_5-SIGN + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}. + * + * @access private + * @param string $m + * @return string + */ + function _rsassa_pkcs1_v1_5_sign($m) + { + // EMSA-PKCS1-v1_5 encoding + + $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); + if ($em === false) { + user_error('RSA modulus too short'); + return false; + } + + // RSA signature + + $m = $this->_os2ip($em); + $s = $this->_rsasp1($m); + $s = $this->_i2osp($s, $this->k); + + // Output the signature S + + return $s; + } + + /** + * RSASSA-PKCS1-V1_5-VERIFY + * + * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}. + * + * @access private + * @param string $m + * @param string $s + * @return string + */ + function _rsassa_pkcs1_v1_5_verify($m, $s) + { + // Length checking + + if (strlen($s) != $this->k) { + user_error('Invalid signature'); + return false; + } + + // RSA verification + + $s = $this->_os2ip($s); + $m2 = $this->_rsavp1($s); + if ($m2 === false) { + user_error('Invalid signature'); + return false; + } + $em = $this->_i2osp($m2, $this->k); + if ($em === false) { + user_error('Invalid signature'); + return false; + } + + // EMSA-PKCS1-v1_5 encoding + + $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); + $em3 = $this->_emsa_pkcs1_v1_5_encode_without_null($m, $this->k); + + if ($em2 === false && $em3 === false) { + user_error('RSA modulus too short'); + return false; + } + + // Compare + + return ($em2 !== false && $this->_equals($em, $em2)) || + ($em3 !== false && $this->_equals($em, $em3)); + } + + /** + * Set Encryption Mode + * + * Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1. + * + * @access public + * @param int $mode + */ + function setEncryptionMode($mode) + { + $this->encryptionMode = $mode; + } + + /** + * Set Signature Mode + * + * Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1 + * + * @access public + * @param int $mode + */ + function setSignatureMode($mode) + { + $this->signatureMode = $mode; + } + + /** + * Set public key comment. + * + * @access public + * @param string $comment + */ + function setComment($comment) + { + $this->comment = $comment; + } + + /** + * Get public key comment. + * + * @access public + * @return string + */ + function getComment() + { + return $this->comment; + } + + /** + * Encryption + * + * Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be. + * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will + * be concatenated together. + * + * @see self::decrypt() + * @access public + * @param string $plaintext + * @return string + */ + function encrypt($plaintext) + { + switch ($this->encryptionMode) { + case self::ENCRYPTION_NONE: + $plaintext = str_split($plaintext, $this->k); + $ciphertext = ''; + foreach ($plaintext as $m) { + $ciphertext.= $this->_raw_encrypt($m); + } + return $ciphertext; + case self::ENCRYPTION_PKCS1: + $length = $this->k - 11; + if ($length <= 0) { + return false; + } + + $plaintext = str_split($plaintext, $length); + $ciphertext = ''; + foreach ($plaintext as $m) { + $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m); + } + return $ciphertext; + //case self::ENCRYPTION_OAEP: + default: + $length = $this->k - 2 * $this->hLen - 2; + if ($length <= 0) { + return false; + } + + $plaintext = str_split($plaintext, $length); + $ciphertext = ''; + foreach ($plaintext as $m) { + $ciphertext.= $this->_rsaes_oaep_encrypt($m); + } + return $ciphertext; + } + } + + /** + * Decryption + * + * @see self::encrypt() + * @access public + * @param string $ciphertext + * @return string + */ + function decrypt($ciphertext) + { + if ($this->k <= 0) { + return false; + } + + $ciphertext = str_split($ciphertext, $this->k); + $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT); + + $plaintext = ''; + + switch ($this->encryptionMode) { + case self::ENCRYPTION_NONE: + $decrypt = '_raw_encrypt'; + break; + case self::ENCRYPTION_PKCS1: + $decrypt = '_rsaes_pkcs1_v1_5_decrypt'; + break; + //case self::ENCRYPTION_OAEP: + default: + $decrypt = '_rsaes_oaep_decrypt'; + } + + foreach ($ciphertext as $c) { + $temp = $this->$decrypt($c); + if ($temp === false) { + return false; + } + $plaintext.= $temp; + } + + return $plaintext; + } + + /** + * Create a signature + * + * @see self::verify() + * @access public + * @param string $message + * @return string + */ + function sign($message) + { + if (empty($this->modulus) || empty($this->exponent)) { + return false; + } + + switch ($this->signatureMode) { + case self::SIGNATURE_PKCS1: + return $this->_rsassa_pkcs1_v1_5_sign($message); + //case self::SIGNATURE_PSS: + default: + return $this->_rsassa_pss_sign($message); + } + } + + /** + * Verifies a signature + * + * @see self::sign() + * @access public + * @param string $message + * @param string $signature + * @return bool + */ + function verify($message, $signature) + { + if (empty($this->modulus) || empty($this->exponent)) { + return false; + } + + switch ($this->signatureMode) { + case self::SIGNATURE_PKCS1: + return $this->_rsassa_pkcs1_v1_5_verify($message, $signature); + //case self::SIGNATURE_PSS: + default: + return $this->_rsassa_pss_verify($message, $signature); + } + } + + /** + * Extract raw BER from Base64 encoding + * + * @access private + * @param string $str + * @return string + */ + function _extractBER($str) + { + /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them + * above and beyond the ceritificate. + * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line: + * + * Bag Attributes + * localKeyID: 01 00 00 00 + * subject=/O=organization/OU=org unit/CN=common name + * issuer=/O=organization/CN=common name + */ + $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); + // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff + $temp = preg_replace('#-+[^-]+-+#', '', $temp); + // remove new lines + $temp = str_replace(array("\r", "\n", ' '), '', $temp); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + return $temp != false ? $temp : $str; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php new file mode 100644 index 0000000..52ea726 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php @@ -0,0 +1,280 @@ + + * + * + * + * @category Crypt + * @package Random + * @author Jim Wigginton + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +/** + * Pure-PHP Random Number Generator + * + * @package Random + * @author Jim Wigginton + * @access public + */ +class Random +{ + /** + * Generate a random string. + * + * Although microoptimizations are generally discouraged as they impair readability this function is ripe with + * microoptimizations because this function has the potential of being called a huge number of times. + * eg. for RSA key generation. + * + * @param int $length + * @return string + */ + static function string($length) + { + if (!$length) { + return ''; + } + + if (version_compare(PHP_VERSION, '7.0.0', '>=')) { + try { + return \random_bytes($length); + } catch (\Throwable $e) { + // If a sufficient source of randomness is unavailable, random_bytes() will throw an + // object that implements the Throwable interface (Exception, TypeError, Error). + // We don't actually need to do anything here. The string() method should just continue + // as normal. Note, however, that if we don't have a sufficient source of randomness for + // random_bytes(), most of the other calls here will fail too, so we'll end up using + // the PHP implementation. + } + } + + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + // method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call. + // ie. class_alias is a function that was introduced in PHP 5.3 + if (extension_loaded('mcrypt') && function_exists('class_alias')) { + return @mcrypt_create_iv($length); + } + // method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was, + // to quote , "possible blocking behavior". as of 5.3.4 + // openssl_random_pseudo_bytes and mcrypt_create_iv do the exact same thing on Windows. ie. they both + // call php_win32_get_random_bytes(): + // + // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008 + // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392 + // + // php_win32_get_random_bytes() is defined thusly: + // + // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80 + // + // we're calling it, all the same, in the off chance that the mcrypt extension is not available + if (extension_loaded('openssl') && version_compare(PHP_VERSION, '5.3.4', '>=')) { + return openssl_random_pseudo_bytes($length); + } + } else { + // method 1. the fastest + if (extension_loaded('openssl')) { + return openssl_random_pseudo_bytes($length); + } + // method 2 + static $fp = true; + if ($fp === true) { + // warning's will be output unles the error suppression operator is used. errors such as + // "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc. + $fp = @fopen('/dev/urandom', 'rb'); + } + if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource() + $temp = fread($fp, $length); + if (strlen($temp) == $length) { + return $temp; + } + } + // method 3. pretty much does the same thing as method 2 per the following url: + // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391 + // surprisingly slower than method 2. maybe that's because mcrypt_create_iv does a bunch of error checking that we're + // not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir + // restrictions or some such + if (extension_loaded('mcrypt')) { + return @mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); + } + } + // at this point we have no choice but to use a pure-PHP CSPRNG + + // cascade entropy across multiple PHP instances by fixing the session and collecting all + // environmental variables, including the previous session data and the current session + // data. + // + // mt_rand seeds itself by looking at the PID and the time, both of which are (relatively) + // easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but + // PHP isn't low level to be able to use those as sources and on a web server there's not likely + // going to be a ton of keyboard or mouse action. web servers do have one thing that we can use + // however, a ton of people visiting the website. obviously you don't want to base your seeding + // soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled + // by the user and (2) this isn't just looking at the data sent by the current user - it's based + // on the data sent by all users. one user requests the page and a hash of their info is saved. + // another user visits the page and the serialization of their data is utilized along with the + // server envirnment stuff and a hash of the previous http request data (which itself utilizes + // a hash of the session data before that). certainly an attacker should be assumed to have + // full control over his own http requests. he, however, is not going to have control over + // everyone's http requests. + static $crypto = false, $v; + if ($crypto === false) { + // save old session data + $old_session_id = session_id(); + $old_use_cookies = ini_get('session.use_cookies'); + $old_session_cache_limiter = session_cache_limiter(); + $_OLD_SESSION = isset($_SESSION) ? $_SESSION : false; + if ($old_session_id != '') { + session_write_close(); + } + + session_id(1); + ini_set('session.use_cookies', 0); + session_cache_limiter(''); + session_start(); + + $v = $seed = $_SESSION['seed'] = pack('H*', sha1( + (isset($_SERVER) ? phpseclib_safe_serialize($_SERVER) : '') . + (isset($_POST) ? phpseclib_safe_serialize($_POST) : '') . + (isset($_GET) ? phpseclib_safe_serialize($_GET) : '') . + (isset($_COOKIE) ? phpseclib_safe_serialize($_COOKIE) : '') . + // as of PHP 8.1 $GLOBALS can't be accessed by reference, which eliminates + // the need for phpseclib_safe_serialize. see https://wiki.php.net/rfc/restrict_globals_usage + // for more info + (version_compare(PHP_VERSION, '8.1.0', '>=') ? serialize($GLOBALS) : phpseclib_safe_serialize($GLOBALS)) . + phpseclib_safe_serialize($_SESSION) . + phpseclib_safe_serialize($_OLD_SESSION) + )); + if (!isset($_SESSION['count'])) { + $_SESSION['count'] = 0; + } + $_SESSION['count']++; + + session_write_close(); + + // restore old session data + if ($old_session_id != '') { + session_id($old_session_id); + session_start(); + ini_set('session.use_cookies', $old_use_cookies); + session_cache_limiter($old_session_cache_limiter); + } else { + if ($_OLD_SESSION !== false) { + $_SESSION = $_OLD_SESSION; + unset($_OLD_SESSION); + } else { + unset($_SESSION); + } + } + + // in SSH2 a shared secret and an exchange hash are generated through the key exchange process. + // the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C. + // if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the + // original hash and the current hash. we'll be emulating that. for more info see the following URL: + // + // http://tools.ietf.org/html/rfc4253#section-7.2 + // + // see the is_string($crypto) part for an example of how to expand the keys + $key = pack('H*', sha1($seed . 'A')); + $iv = pack('H*', sha1($seed . 'C')); + + // ciphers are used as per the nist.gov link below. also, see this link: + // + // http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives + switch (true) { + case class_exists('\phpseclib\Crypt\AES'): + $crypto = new AES(Base::MODE_CTR); + break; + case class_exists('\phpseclib\Crypt\Twofish'): + $crypto = new Twofish(Base::MODE_CTR); + break; + case class_exists('\phpseclib\Crypt\Blowfish'): + $crypto = new Blowfish(Base::MODE_CTR); + break; + case class_exists('\phpseclib\Crypt\TripleDES'): + $crypto = new TripleDES(Base::MODE_CTR); + break; + case class_exists('\phpseclib\Crypt\DES'): + $crypto = new DES(Base::MODE_CTR); + break; + case class_exists('\phpseclib\Crypt\RC4'): + $crypto = new RC4(); + break; + default: + user_error(__CLASS__ . ' requires at least one symmetric cipher be loaded'); + return false; + } + + $crypto->setKey($key); + $crypto->setIV($iv); + $crypto->enableContinuousBuffer(); + } + + //return $crypto->encrypt(str_repeat("\0", $length)); + + // the following is based off of ANSI X9.31: + // + // http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf + // + // OpenSSL uses that same standard for it's random numbers: + // + // http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c + // (do a search for "ANS X9.31 A.2.4") + $result = ''; + while (strlen($result) < $length) { + $i = $crypto->encrypt(microtime()); // strlen(microtime()) == 21 + $r = $crypto->encrypt($i ^ $v); // strlen($v) == 20 + $v = $crypto->encrypt($r ^ $i); // strlen($r) == 20 + $result.= $r; + } + return substr($result, 0, $length); + } +} + +if (!function_exists('phpseclib_safe_serialize')) { + /** + * Safely serialize variables + * + * If a class has a private __sleep() method it'll give a fatal error on PHP 5.2 and earlier. + * PHP 5.3 will emit a warning. + * + * @param mixed $arr + * @access public + */ + function phpseclib_safe_serialize(&$arr) + { + if (is_object($arr)) { + return ''; + } + if (!is_array($arr)) { + return serialize($arr); + } + // prevent circular array recursion + if (isset($arr['__phpseclib_marker'])) { + return ''; + } + $safearr = array(); + $arr['__phpseclib_marker'] = true; + foreach (array_keys($arr) as $key) { + // do not recurse on the '__phpseclib_marker' key itself, for smaller memory usage + if ($key !== '__phpseclib_marker') { + $safearr[$key] = phpseclib_safe_serialize($arr[$key]); + } + } + unset($arr['__phpseclib_marker']); + return serialize($safearr); + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php new file mode 100644 index 0000000..6a251f5 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php @@ -0,0 +1,941 @@ + + * setKey('abcdefghijklmnop'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $rijndael->decrypt($rijndael->encrypt($plaintext)); + * ?> + * + * + * @category Crypt + * @package Rijndael + * @author Jim Wigginton + * @copyright 2008 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +/** + * Pure-PHP implementation of Rijndael. + * + * @package Rijndael + * @author Jim Wigginton + * @access public + */ +class Rijndael extends Base +{ + /** + * The mcrypt specific name of the cipher + * + * Mcrypt is useable for 128/192/256-bit $block_size/$key_length. For 160/224 not. + * \phpseclib\Crypt\Rijndael determines automatically whether mcrypt is useable + * or not for the current $block_size/$key_length. + * In case of, $cipher_name_mcrypt will be set dynamically at run time accordingly. + * + * @see \phpseclib\Crypt\Base::cipher_name_mcrypt + * @see \phpseclib\Crypt\Base::engine + * @see self::isValidEngine() + * @var string + * @access private + */ + var $cipher_name_mcrypt = 'rijndael-128'; + + /** + * The default salt used by setPassword() + * + * @see \phpseclib\Crypt\Base::password_default_salt + * @see \phpseclib\Crypt\Base::setPassword() + * @var string + * @access private + */ + var $password_default_salt = 'phpseclib'; + + /** + * The Key Schedule + * + * @see self::_setup() + * @var array + * @access private + */ + var $w; + + /** + * The Inverse Key Schedule + * + * @see self::_setup() + * @var array + * @access private + */ + var $dw; + + /** + * The Block Length divided by 32 + * + * @see self::setBlockLength() + * @var int + * @access private + * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size + * because the encryption / decryption / key schedule creation requires this number and not $block_size. We could + * derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu + * of that, we'll just precompute it once. + */ + var $Nb = 4; + + /** + * The Key Length (in bytes) + * + * @see self::setKeyLength() + * @var int + * @access private + * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk + * because the encryption / decryption / key schedule creation requires this number and not $key_length. We could + * derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu + * of that, we'll just precompute it once. + */ + var $key_length = 16; + + /** + * The Key Length divided by 32 + * + * @see self::setKeyLength() + * @var int + * @access private + * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4 + */ + var $Nk = 4; + + /** + * The Number of Rounds + * + * @var int + * @access private + * @internal The max value is 14, the min value is 10. + */ + var $Nr; + + /** + * Shift offsets + * + * @var array + * @access private + */ + var $c; + + /** + * Holds the last used key- and block_size information + * + * @var array + * @access private + */ + var $kl; + + /** + * Sets the key length. + * + * Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to + * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount. + * + * Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined + * and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to + * 192/256 bits as, for example, mcrypt will do. + * + * That said, if you want be compatible with other Rijndael and AES implementations, + * you should not setKeyLength(160) or setKeyLength(224). + * + * Additional: In case of 160- and 224-bit keys, phpseclib will/can, for that reason, not use + * the mcrypt php extension, even if available. + * This results then in slower encryption. + * + * @access public + * @param int $length + */ + function setKeyLength($length) + { + switch (true) { + case $length <= 128: + $this->key_length = 16; + break; + case $length <= 160: + $this->key_length = 20; + break; + case $length <= 192: + $this->key_length = 24; + break; + case $length <= 224: + $this->key_length = 28; + break; + default: + $this->key_length = 32; + } + + parent::setKeyLength($length); + } + + /** + * Sets the block length + * + * Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to + * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount. + * + * @access public + * @param int $length + */ + function setBlockLength($length) + { + $length >>= 5; + if ($length > 8) { + $length = 8; + } elseif ($length < 4) { + $length = 4; + } + $this->Nb = $length; + $this->block_size = $length << 2; + $this->changed = true; + $this->_setEngine(); + } + + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() + * + * @see \phpseclib\Crypt\Base::__construct() + * @param int $engine + * @access public + * @return bool + */ + function isValidEngine($engine) + { + switch ($engine) { + case self::ENGINE_OPENSSL: + if ($this->block_size != 16) { + return false; + } + $this->cipher_name_openssl_ecb = 'aes-' . ($this->key_length << 3) . '-ecb'; + $this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->_openssl_translate_mode(); + break; + case self::ENGINE_MCRYPT: + $this->cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3); + if ($this->key_length % 8) { // is it a 160/224-bit key? + // mcrypt is not usable for them, only for 128/192/256-bit keys + return false; + } + } + + return parent::isValidEngine($engine); + } + + /** + * Encrypts a block + * + * @access private + * @param string $in + * @return string + */ + function _encryptBlock($in) + { + static $tables; + if (empty($tables)) { + $tables = &$this->_getTables(); + } + $t0 = $tables[0]; + $t1 = $tables[1]; + $t2 = $tables[2]; + $t3 = $tables[3]; + $sbox = $tables[4]; + + $state = array(); + $words = unpack('N*', $in); + + $c = $this->c; + $w = $this->w; + $Nb = $this->Nb; + $Nr = $this->Nr; + + // addRoundKey + $wc = $Nb - 1; + foreach ($words as $word) { + $state[] = $word ^ $w[++$wc]; + } + + // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components - + // subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding + // Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf. + // Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization. + // Unfortunately, the description given there is not quite correct. Per aes.spec.v316.pdf#page=19 [1], + // equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well. + + // [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf + $temp = array(); + for ($round = 1; $round < $Nr; ++$round) { + $i = 0; // $c[0] == 0 + $j = $c[1]; + $k = $c[2]; + $l = $c[3]; + + while ($i < $Nb) { + $temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^ + $t1[$state[$j] >> 16 & 0x000000FF] ^ + $t2[$state[$k] >> 8 & 0x000000FF] ^ + $t3[$state[$l] & 0x000000FF] ^ + $w[++$wc]; + ++$i; + $j = ($j + 1) % $Nb; + $k = ($k + 1) % $Nb; + $l = ($l + 1) % $Nb; + } + $state = $temp; + } + + // subWord + for ($i = 0; $i < $Nb; ++$i) { + $state[$i] = $sbox[$state[$i] & 0x000000FF] | + ($sbox[$state[$i] >> 8 & 0x000000FF] << 8) | + ($sbox[$state[$i] >> 16 & 0x000000FF] << 16) | + ($sbox[$state[$i] >> 24 & 0x000000FF] << 24); + } + + // shiftRows + addRoundKey + $i = 0; // $c[0] == 0 + $j = $c[1]; + $k = $c[2]; + $l = $c[3]; + while ($i < $Nb) { + $temp[$i] = ($state[$i] & intval(0xFF000000)) ^ + ($state[$j] & 0x00FF0000) ^ + ($state[$k] & 0x0000FF00) ^ + ($state[$l] & 0x000000FF) ^ + $w[$i]; + ++$i; + $j = ($j + 1) % $Nb; + $k = ($k + 1) % $Nb; + $l = ($l + 1) % $Nb; + } + + switch ($Nb) { + case 8: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]); + case 7: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]); + case 6: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]); + case 5: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]); + default: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]); + } + } + + /** + * Decrypts a block + * + * @access private + * @param string $in + * @return string + */ + function _decryptBlock($in) + { + static $invtables; + if (empty($invtables)) { + $invtables = &$this->_getInvTables(); + } + $dt0 = $invtables[0]; + $dt1 = $invtables[1]; + $dt2 = $invtables[2]; + $dt3 = $invtables[3]; + $isbox = $invtables[4]; + + $state = array(); + $words = unpack('N*', $in); + + $c = $this->c; + $dw = $this->dw; + $Nb = $this->Nb; + $Nr = $this->Nr; + + // addRoundKey + $wc = $Nb - 1; + foreach ($words as $word) { + $state[] = $word ^ $dw[++$wc]; + } + + $temp = array(); + for ($round = $Nr - 1; $round > 0; --$round) { + $i = 0; // $c[0] == 0 + $j = $Nb - $c[1]; + $k = $Nb - $c[2]; + $l = $Nb - $c[3]; + + while ($i < $Nb) { + $temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^ + $dt1[$state[$j] >> 16 & 0x000000FF] ^ + $dt2[$state[$k] >> 8 & 0x000000FF] ^ + $dt3[$state[$l] & 0x000000FF] ^ + $dw[++$wc]; + ++$i; + $j = ($j + 1) % $Nb; + $k = ($k + 1) % $Nb; + $l = ($l + 1) % $Nb; + } + $state = $temp; + } + + // invShiftRows + invSubWord + addRoundKey + $i = 0; // $c[0] == 0 + $j = $Nb - $c[1]; + $k = $Nb - $c[2]; + $l = $Nb - $c[3]; + + while ($i < $Nb) { + $word = ($state[$i] & intval(0xFF000000)) | + ($state[$j] & 0x00FF0000) | + ($state[$k] & 0x0000FF00) | + ($state[$l] & 0x000000FF); + + $temp[$i] = $dw[$i] ^ ($isbox[$word & 0x000000FF] | + ($isbox[$word >> 8 & 0x000000FF] << 8) | + ($isbox[$word >> 16 & 0x000000FF] << 16) | + ($isbox[$word >> 24 & 0x000000FF] << 24)); + ++$i; + $j = ($j + 1) % $Nb; + $k = ($k + 1) % $Nb; + $l = ($l + 1) % $Nb; + } + + switch ($Nb) { + case 8: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]); + case 7: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]); + case 6: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]); + case 5: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]); + default: + return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]); + } + } + + /** + * Setup the key (expansion) + * + * @see \phpseclib\Crypt\Base::_setupKey() + * @access private + */ + function _setupKey() + { + // Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field. + // See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse + static $rcon; + + if (!isset($rcon)) { + $rcon = array(0, + 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, + 0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000, + 0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000, + 0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000, + 0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000 + ); + $rcon = array_map('intval', $rcon); + } + + if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) { + // already expanded + return; + } + $this->kl = array('key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size); + + $this->Nk = $this->key_length >> 2; + // see Rijndael-ammended.pdf#page=44 + $this->Nr = max($this->Nk, $this->Nb) + 6; + + // shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44, + // "Table 8: Shift offsets in Shiftrow for the alternative block lengths" + // shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14, + // "Table 2: Shift offsets for different block lengths" + switch ($this->Nb) { + case 4: + case 5: + case 6: + $this->c = array(0, 1, 2, 3); + break; + case 7: + $this->c = array(0, 1, 2, 4); + break; + case 8: + $this->c = array(0, 1, 3, 4); + } + + $w = array_values(unpack('N*words', $this->key)); + + $length = $this->Nb * ($this->Nr + 1); + for ($i = $this->Nk; $i < $length; $i++) { + $temp = $w[$i - 1]; + if ($i % $this->Nk == 0) { + // according to , "the size of an integer is platform-dependent". + // on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine, + // 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and' + // with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is. + $temp = (($temp << 8) & intval(0xFFFFFF00)) | (($temp >> 24) & 0x000000FF); // rotWord + $temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk]; + } elseif ($this->Nk > 6 && $i % $this->Nk == 4) { + $temp = $this->_subWord($temp); + } + $w[$i] = $w[$i - $this->Nk] ^ $temp; + } + + // convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns + // and generate the inverse key schedule. more specifically, + // according to (section 5.3.3), + // "The key expansion for the Inverse Cipher is defined as follows: + // 1. Apply the Key Expansion. + // 2. Apply InvMixColumn to all Round Keys except the first and the last one." + // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher" + list($dt0, $dt1, $dt2, $dt3) = $this->_getInvTables(); + $temp = $this->w = $this->dw = array(); + for ($i = $row = $col = 0; $i < $length; $i++, $col++) { + if ($col == $this->Nb) { + if ($row == 0) { + $this->dw[0] = $this->w[0]; + } else { + // subWord + invMixColumn + invSubWord = invMixColumn + $j = 0; + while ($j < $this->Nb) { + $dw = $this->_subWord($this->w[$row][$j]); + $temp[$j] = $dt0[$dw >> 24 & 0x000000FF] ^ + $dt1[$dw >> 16 & 0x000000FF] ^ + $dt2[$dw >> 8 & 0x000000FF] ^ + $dt3[$dw & 0x000000FF]; + $j++; + } + $this->dw[$row] = $temp; + } + + $col = 0; + $row++; + } + $this->w[$row][$col] = $w[$i]; + } + + $this->dw[$row] = $this->w[$row]; + + // Converting to 1-dim key arrays (both ascending) + $this->dw = array_reverse($this->dw); + $w = array_pop($this->w); + $dw = array_pop($this->dw); + foreach ($this->w as $r => $wr) { + foreach ($wr as $c => $wc) { + $w[] = $wc; + $dw[] = $this->dw[$r][$c]; + } + } + $this->w = $w; + $this->dw = $dw; + } + + /** + * Performs S-Box substitutions + * + * @access private + * @param int $word + */ + function _subWord($word) + { + static $sbox; + if (empty($sbox)) { + list(, , , , $sbox) = $this->_getTables(); + } + + return $sbox[$word & 0x000000FF] | + ($sbox[$word >> 8 & 0x000000FF] << 8) | + ($sbox[$word >> 16 & 0x000000FF] << 16) | + ($sbox[$word >> 24 & 0x000000FF] << 24); + } + + /** + * Provides the mixColumns and sboxes tables + * + * @see self::_encryptBlock() + * @see self::_setupInlineCrypt() + * @see self::_subWord() + * @access private + * @return array &$tables + */ + function &_getTables() + { + static $tables; + if (empty($tables)) { + // according to (section 5.2.1), + // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so + // those are the names we'll use. + $t3 = array_map('intval', array( + // with array_map('intval', ...) we ensure we have only int's and not + // some slower floats converted by php automatically on high values + 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, + 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, + 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, + 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, + 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, + 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, + 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, + 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, + 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, + 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, + 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, + 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, + 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, + 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, + 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, + 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, + 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, + 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, + 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, + 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, + 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, + 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, + 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, + 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, + 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, + 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, + 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, + 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, + 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, + 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, + 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, + 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C + )); + + foreach ($t3 as $t3i) { + $t0[] = (($t3i << 24) & intval(0xFF000000)) | (($t3i >> 8) & 0x00FFFFFF); + $t1[] = (($t3i << 16) & intval(0xFFFF0000)) | (($t3i >> 16) & 0x0000FFFF); + $t2[] = (($t3i << 8) & intval(0xFFFFFF00)) | (($t3i >> 24) & 0x000000FF); + } + + $tables = array( + // The Precomputed mixColumns tables t0 - t3 + $t0, + $t1, + $t2, + $t3, + // The SubByte S-Box + array( + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 + ) + ); + } + return $tables; + } + + /** + * Provides the inverse mixColumns and inverse sboxes tables + * + * @see self::_decryptBlock() + * @see self::_setupInlineCrypt() + * @see self::_setupKey() + * @access private + * @return array &$tables + */ + function &_getInvTables() + { + static $tables; + if (empty($tables)) { + $dt3 = array_map('intval', array( + 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B, + 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5, + 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B, + 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E, + 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D, + 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9, + 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66, + 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED, + 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4, + 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD, + 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60, + 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79, + 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C, + 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24, + 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C, + 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814, + 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B, + 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084, + 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077, + 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22, + 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F, + 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582, + 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB, + 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF, + 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035, + 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17, + 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46, + 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D, + 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A, + 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678, + 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF, + 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0 + )); + + foreach ($dt3 as $dt3i) { + $dt0[] = (($dt3i << 24) & intval(0xFF000000)) | (($dt3i >> 8) & 0x00FFFFFF); + $dt1[] = (($dt3i << 16) & intval(0xFFFF0000)) | (($dt3i >> 16) & 0x0000FFFF); + $dt2[] = (($dt3i << 8) & intval(0xFFFFFF00)) | (($dt3i >> 24) & 0x000000FF); + }; + + $tables = array( + // The Precomputed inverse mixColumns tables dt0 - dt3 + $dt0, + $dt1, + $dt2, + $dt3, + // The inverse SubByte S-Box + array( + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D + ) + ); + } + return $tables; + } + + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see \phpseclib\Crypt\Base::_setupInlineCrypt() + * @access private + */ + function _setupInlineCrypt() + { + // Note: _setupInlineCrypt() will be called only if $this->changed === true + // So here we are'nt under the same heavy timing-stress as we are in _de/encryptBlock() or de/encrypt(). + // However...the here generated function- $code, stored as php callback in $this->inline_crypt, must work as fast as even possible. + + $lambda_functions =& self::_getLambdaFunctions(); + + // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function. + // (Currently, for Crypt_Rijndael/AES, one generated $lambda_function cost on php5.5@32bit ~80kb unfreeable mem and ~130kb on php5.5@64bit) + // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one. + $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); + + // Generation of a uniqe hash for our generated code + $code_hash = "Crypt_Rijndael, {$this->mode}, {$this->Nr}, {$this->Nb}"; + if ($gen_hi_opt_code) { + $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); + } + + if (!isset($lambda_functions[$code_hash])) { + switch (true) { + case $gen_hi_opt_code: + // The hi-optimized $lambda_functions will use the key-words hardcoded for better performance. + $w = $this->w; + $dw = $this->dw; + $init_encrypt = ''; + $init_decrypt = ''; + break; + default: + for ($i = 0, $cw = count($this->w); $i < $cw; ++$i) { + $w[] = '$w[' . $i . ']'; + $dw[] = '$dw[' . $i . ']'; + } + $init_encrypt = '$w = $self->w;'; + $init_decrypt = '$dw = $self->dw;'; + } + + $Nr = $this->Nr; + $Nb = $this->Nb; + $c = $this->c; + + // Generating encrypt code: + $init_encrypt.= ' + static $tables; + if (empty($tables)) { + $tables = &$self->_getTables(); + } + $t0 = $tables[0]; + $t1 = $tables[1]; + $t2 = $tables[2]; + $t3 = $tables[3]; + $sbox = $tables[4]; + '; + + $s = 'e'; + $e = 's'; + $wc = $Nb - 1; + + // Preround: addRoundKey + $encrypt_block = '$in = unpack("N*", $in);'."\n"; + for ($i = 0; $i < $Nb; ++$i) { + $encrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$w[++$wc].";\n"; + } + + // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey + for ($round = 1; $round < $Nr; ++$round) { + list($s, $e) = array($e, $s); + for ($i = 0; $i < $Nb; ++$i) { + $encrypt_block.= + '$'.$e.$i.' = + $t0[($'.$s.$i .' >> 24) & 0xff] ^ + $t1[($'.$s.(($i + $c[1]) % $Nb).' >> 16) & 0xff] ^ + $t2[($'.$s.(($i + $c[2]) % $Nb).' >> 8) & 0xff] ^ + $t3[ $'.$s.(($i + $c[3]) % $Nb).' & 0xff] ^ + '.$w[++$wc].";\n"; + } + } + + // Finalround: subWord + shiftRows + addRoundKey + for ($i = 0; $i < $Nb; ++$i) { + $encrypt_block.= + '$'.$e.$i.' = + $sbox[ $'.$e.$i.' & 0xff] | + ($sbox[($'.$e.$i.' >> 8) & 0xff] << 8) | + ($sbox[($'.$e.$i.' >> 16) & 0xff] << 16) | + ($sbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n"; + } + $encrypt_block .= '$in = pack("N*"'."\n"; + for ($i = 0; $i < $Nb; ++$i) { + $encrypt_block.= ', + ($'.$e.$i .' & '.((int)0xFF000000).') ^ + ($'.$e.(($i + $c[1]) % $Nb).' & 0x00FF0000 ) ^ + ($'.$e.(($i + $c[2]) % $Nb).' & 0x0000FF00 ) ^ + ($'.$e.(($i + $c[3]) % $Nb).' & 0x000000FF ) ^ + '.$w[$i]."\n"; + } + $encrypt_block .= ');'; + + // Generating decrypt code: + $init_decrypt.= ' + static $invtables; + if (empty($invtables)) { + $invtables = &$self->_getInvTables(); + } + $dt0 = $invtables[0]; + $dt1 = $invtables[1]; + $dt2 = $invtables[2]; + $dt3 = $invtables[3]; + $isbox = $invtables[4]; + '; + + $s = 'e'; + $e = 's'; + $wc = $Nb - 1; + + // Preround: addRoundKey + $decrypt_block = '$in = unpack("N*", $in);'."\n"; + for ($i = 0; $i < $Nb; ++$i) { + $decrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$dw[++$wc].';'."\n"; + } + + // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey + for ($round = 1; $round < $Nr; ++$round) { + list($s, $e) = array($e, $s); + for ($i = 0; $i < $Nb; ++$i) { + $decrypt_block.= + '$'.$e.$i.' = + $dt0[($'.$s.$i .' >> 24) & 0xff] ^ + $dt1[($'.$s.(($Nb + $i - $c[1]) % $Nb).' >> 16) & 0xff] ^ + $dt2[($'.$s.(($Nb + $i - $c[2]) % $Nb).' >> 8) & 0xff] ^ + $dt3[ $'.$s.(($Nb + $i - $c[3]) % $Nb).' & 0xff] ^ + '.$dw[++$wc].";\n"; + } + } + + // Finalround: subWord + shiftRows + addRoundKey + for ($i = 0; $i < $Nb; ++$i) { + $decrypt_block.= + '$'.$e.$i.' = + $isbox[ $'.$e.$i.' & 0xff] | + ($isbox[($'.$e.$i.' >> 8) & 0xff] << 8) | + ($isbox[($'.$e.$i.' >> 16) & 0xff] << 16) | + ($isbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n"; + } + $decrypt_block .= '$in = pack("N*"'."\n"; + for ($i = 0; $i < $Nb; ++$i) { + $decrypt_block.= ', + ($'.$e.$i. ' & '.((int)0xFF000000).') ^ + ($'.$e.(($Nb + $i - $c[1]) % $Nb).' & 0x00FF0000 ) ^ + ($'.$e.(($Nb + $i - $c[2]) % $Nb).' & 0x0000FF00 ) ^ + ($'.$e.(($Nb + $i - $c[3]) % $Nb).' & 0x000000FF ) ^ + '.$dw[$i]."\n"; + } + $decrypt_block .= ');'; + + $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( + array( + 'init_crypt' => '', + 'init_encrypt' => $init_encrypt, + 'init_decrypt' => $init_decrypt, + 'encrypt_block' => $encrypt_block, + 'decrypt_block' => $decrypt_block + ) + ); + } + $this->inline_crypt = $lambda_functions[$code_hash]; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php new file mode 100644 index 0000000..b6d64f9 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php @@ -0,0 +1,460 @@ + + * setKey('abcdefghijklmnopqrstuvwx'); + * + * $size = 10 * 1024; + * $plaintext = ''; + * for ($i = 0; $i < $size; $i++) { + * $plaintext.= 'a'; + * } + * + * echo $des->decrypt($des->encrypt($plaintext)); + * ?> + * + * + * @category Crypt + * @package TripleDES + * @author Jim Wigginton + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +/** + * Pure-PHP implementation of Triple DES. + * + * @package TripleDES + * @author Jim Wigginton + * @access public + */ +class TripleDES extends DES +{ + /** + * Encrypt / decrypt using inner chaining + * + * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (self::MODE_CBC3). + */ + const MODE_3CBC = -2; + + /** + * Encrypt / decrypt using outer chaining + * + * Outer chaining is used by SSH-2 and when the mode is set to \phpseclib\Crypt\Base::MODE_CBC. + */ + const MODE_CBC3 = self::MODE_CBC; + + /** + * Key Length (in bytes) + * + * @see \phpseclib\Crypt\TripleDES::setKeyLength() + * @var int + * @access private + */ + var $key_length = 24; + + /** + * The default salt used by setPassword() + * + * @see \phpseclib\Crypt\Base::password_default_salt + * @see \phpseclib\Crypt\Base::setPassword() + * @var string + * @access private + */ + var $password_default_salt = 'phpseclib'; + + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib\Crypt\DES::cipher_name_mcrypt + * @see \phpseclib\Crypt\Base::cipher_name_mcrypt + * @var string + * @access private + */ + var $cipher_name_mcrypt = 'tripledes'; + + /** + * Optimizing value while CFB-encrypting + * + * @see \phpseclib\Crypt\Base::cfb_init_len + * @var int + * @access private + */ + var $cfb_init_len = 750; + + /** + * max possible size of $key + * + * @see self::setKey() + * @see \phpseclib\Crypt\DES::setKey() + * @var string + * @access private + */ + var $key_length_max = 24; + + /** + * Internal flag whether using self::MODE_3CBC or not + * + * @var bool + * @access private + */ + var $mode_3cbc; + + /** + * The \phpseclib\Crypt\DES objects + * + * Used only if $mode_3cbc === true + * + * @var array + * @access private + */ + var $des; + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. + * + * $mode could be: + * + * - \phpseclib\Crypt\Base::MODE_ECB + * + * - \phpseclib\Crypt\Base::MODE_CBC + * + * - \phpseclib\Crypt\Base::MODE_CTR + * + * - \phpseclib\Crypt\Base::MODE_CFB + * + * - \phpseclib\Crypt\Base::MODE_OFB + * + * - \phpseclib\Crypt\TripleDES::MODE_3CBC + * + * If not explicitly set, \phpseclib\Crypt\Base::MODE_CBC will be used. + * + * @see \phpseclib\Crypt\DES::__construct() + * @see \phpseclib\Crypt\Base::__construct() + * @param int $mode + * @access public + */ + function __construct($mode = self::MODE_CBC) + { + switch ($mode) { + // In case of self::MODE_3CBC, we init as CRYPT_DES_MODE_CBC + // and additional flag us internally as 3CBC + case self::MODE_3CBC: + parent::__construct(self::MODE_CBC); + $this->mode_3cbc = true; + + // This three $des'es will do the 3CBC work (if $key > 64bits) + $this->des = array( + new DES(self::MODE_CBC), + new DES(self::MODE_CBC), + new DES(self::MODE_CBC), + ); + + // we're going to be doing the padding, ourselves, so disable it in the \phpseclib\Crypt\DES objects + $this->des[0]->disablePadding(); + $this->des[1]->disablePadding(); + $this->des[2]->disablePadding(); + break; + // If not 3CBC, we init as usual + default: + parent::__construct($mode); + } + } + + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() + * + * @see \phpseclib\Crypt\Base::__construct() + * @param int $engine + * @access public + * @return bool + */ + function isValidEngine($engine) + { + if ($engine == self::ENGINE_OPENSSL) { + $this->cipher_name_openssl_ecb = 'des-ede3'; + $mode = $this->_openssl_translate_mode(); + $this->cipher_name_openssl = $mode == 'ecb' ? 'des-ede3' : 'des-ede3-' . $mode; + } + + return parent::isValidEngine($engine); + } + + /** + * Sets the initialization vector. (optional) + * + * SetIV is not required when \phpseclib\Crypt\Base::MODE_ECB is being used. If not explicitly set, it'll be assumed + * to be all zero's. + * + * @see \phpseclib\Crypt\Base::setIV() + * @access public + * @param string $iv + */ + function setIV($iv) + { + parent::setIV($iv); + if ($this->mode_3cbc) { + $this->des[0]->setIV($iv); + $this->des[1]->setIV($iv); + $this->des[2]->setIV($iv); + } + } + + /** + * Sets the key length. + * + * Valid key lengths are 64, 128 and 192 + * + * @see \phpseclib\Crypt\Base:setKeyLength() + * @access public + * @param int $length + */ + function setKeyLength($length) + { + $length >>= 3; + switch (true) { + case $length <= 8: + $this->key_length = 8; + break; + case $length <= 16: + $this->key_length = 16; + break; + default: + $this->key_length = 24; + } + + parent::setKeyLength($length); + } + + /** + * Sets the key. + * + * Keys can be of any length. Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or + * 192-bit (eg. strlen($key) == 24) keys. This function pads and truncates $key as appropriate. + * + * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. + * + * If the key is not explicitly set, it'll be assumed to be all null bytes. + * + * @access public + * @see \phpseclib\Crypt\DES::setKey() + * @see \phpseclib\Crypt\Base::setKey() + * @param string $key + */ + function setKey($key) + { + $length = $this->explicit_key_length ? $this->key_length : strlen($key); + if ($length > 8) { + $key = str_pad(substr($key, 0, 24), 24, chr(0)); + // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this: + // http://php.net/function.mcrypt-encrypt#47973 + $key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24); + } else { + $key = str_pad($key, 8, chr(0)); + } + parent::setKey($key); + + // And in case of self::MODE_3CBC: + // if key <= 64bits we not need the 3 $des to work, + // because we will then act as regular DES-CBC with just a <= 64bit key. + // So only if the key > 64bits (> 8 bytes) we will call setKey() for the 3 $des. + if ($this->mode_3cbc && $length > 8) { + $this->des[0]->setKey(substr($key, 0, 8)); + $this->des[1]->setKey(substr($key, 8, 8)); + $this->des[2]->setKey(substr($key, 16, 8)); + } + } + + /** + * Encrypts a message. + * + * @see \phpseclib\Crypt\Base::encrypt() + * @access public + * @param string $plaintext + * @return string $cipertext + */ + function encrypt($plaintext) + { + // parent::en/decrypt() is able to do all the work for all modes and keylengths, + // except for: self::MODE_3CBC (inner chaining CBC) with a key > 64bits + + // if the key is smaller then 8, do what we'd normally do + if ($this->mode_3cbc && strlen($this->key) > 8) { + return $this->des[2]->encrypt( + $this->des[1]->decrypt( + $this->des[0]->encrypt( + $this->_pad($plaintext) + ) + ) + ); + } + + return parent::encrypt($plaintext); + } + + /** + * Decrypts a message. + * + * @see \phpseclib\Crypt\Base::decrypt() + * @access public + * @param string $ciphertext + * @return string $plaintext + */ + function decrypt($ciphertext) + { + if ($this->mode_3cbc && strlen($this->key) > 8) { + return $this->_unpad( + $this->des[0]->decrypt( + $this->des[1]->encrypt( + $this->des[2]->decrypt( + str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, "\0") + ) + ) + ) + ); + } + + return parent::decrypt($ciphertext); + } + + /** + * Treat consecutive "packets" as if they are a continuous buffer. + * + * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets + * will yield different outputs: + * + * + * echo $des->encrypt(substr($plaintext, 0, 8)); + * echo $des->encrypt(substr($plaintext, 8, 8)); + * + * + * echo $des->encrypt($plaintext); + * + * + * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates + * another, as demonstrated with the following: + * + * + * $des->encrypt(substr($plaintext, 0, 8)); + * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8))); + * + * + * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8))); + * + * + * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different + * outputs. The reason is due to the fact that the initialization vector's change after every encryption / + * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. + * + * Put another way, when the continuous buffer is enabled, the state of the \phpseclib\Crypt\DES() object changes after each + * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that + * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), + * however, they are also less intuitive and more likely to cause you problems. + * + * @see \phpseclib\Crypt\Base::enableContinuousBuffer() + * @see self::disableContinuousBuffer() + * @access public + */ + function enableContinuousBuffer() + { + parent::enableContinuousBuffer(); + if ($this->mode_3cbc) { + $this->des[0]->enableContinuousBuffer(); + $this->des[1]->enableContinuousBuffer(); + $this->des[2]->enableContinuousBuffer(); + } + } + + /** + * Treat consecutive packets as if they are a discontinuous buffer. + * + * The default behavior. + * + * @see \phpseclib\Crypt\Base::disableContinuousBuffer() + * @see self::enableContinuousBuffer() + * @access public + */ + function disableContinuousBuffer() + { + parent::disableContinuousBuffer(); + if ($this->mode_3cbc) { + $this->des[0]->disableContinuousBuffer(); + $this->des[1]->disableContinuousBuffer(); + $this->des[2]->disableContinuousBuffer(); + } + } + + /** + * Creates the key schedule + * + * @see \phpseclib\Crypt\DES::_setupKey() + * @see \phpseclib\Crypt\Base::_setupKey() + * @access private + */ + function _setupKey() + { + switch (true) { + // if $key <= 64bits we configure our internal pure-php cipher engine + // to act as regular [1]DES, not as 3DES. mcrypt.so::tripledes does the same. + case strlen($this->key) <= 8: + $this->des_rounds = 1; + break; + + // otherwise, if $key > 64bits, we configure our engine to work as 3DES. + default: + $this->des_rounds = 3; + + // (only) if 3CBC is used we have, of course, to setup the $des[0-2] keys also separately. + if ($this->mode_3cbc) { + $this->des[0]->_setupKey(); + $this->des[1]->_setupKey(); + $this->des[2]->_setupKey(); + + // because $des[0-2] will, now, do all the work we can return here + // not need unnecessary stress parent::_setupKey() with our, now unused, $key. + return; + } + } + // setup our key + parent::_setupKey(); + } + + /** + * Sets the internal crypt engine + * + * @see \phpseclib\Crypt\Base::__construct() + * @see \phpseclib\Crypt\Base::setPreferredEngine() + * @param int $engine + * @access public + * @return int + */ + function setPreferredEngine($engine) + { + if ($this->mode_3cbc) { + $this->des[0]->setPreferredEngine($engine); + $this->des[1]->setPreferredEngine($engine); + $this->des[2]->setPreferredEngine($engine); + } + + return parent::setPreferredEngine($engine); + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php new file mode 100644 index 0000000..c0a42d6 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php @@ -0,0 +1,852 @@ + + * setKey('12345678901234567890123456789012'); + * + * $plaintext = str_repeat('a', 1024); + * + * echo $twofish->decrypt($twofish->encrypt($plaintext)); + * ?> + * + * + * @category Crypt + * @package Twofish + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +/** + * Pure-PHP implementation of Twofish. + * + * @package Twofish + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @access public + */ +class Twofish extends Base +{ + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib\Crypt\Base::cipher_name_mcrypt + * @var string + * @access private + */ + var $cipher_name_mcrypt = 'twofish'; + + /** + * Optimizing value while CFB-encrypting + * + * @see \phpseclib\Crypt\Base::cfb_init_len + * @var int + * @access private + */ + var $cfb_init_len = 800; + + /** + * Q-Table + * + * @var array + * @access private + */ + var $q0 = array( + 0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76, + 0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38, + 0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C, + 0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48, + 0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23, + 0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82, + 0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C, + 0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61, + 0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B, + 0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1, + 0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66, + 0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7, + 0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA, + 0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71, + 0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8, + 0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7, + 0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2, + 0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90, + 0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB, + 0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF, + 0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B, + 0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64, + 0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A, + 0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A, + 0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02, + 0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D, + 0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72, + 0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, + 0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8, + 0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4, + 0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00, + 0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0 + ); + + /** + * Q-Table + * + * @var array + * @access private + */ + var $q1 = array( + 0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, + 0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B, + 0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1, + 0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F, + 0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D, + 0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5, + 0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3, + 0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51, + 0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96, + 0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C, + 0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70, + 0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8, + 0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC, + 0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2, + 0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9, + 0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17, + 0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3, + 0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E, + 0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49, + 0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9, + 0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01, + 0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48, + 0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19, + 0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64, + 0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5, + 0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69, + 0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E, + 0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC, + 0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB, + 0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9, + 0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2, + 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91 + ); + + /** + * M-Table + * + * @var array + * @access private + */ + var $m0 = array( + 0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB, 0x02028B7B, 0xE2E22BFB, 0x9E9EFAC8, + 0xC9C9EC4A, 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B, 0x98980E45, 0xB2B2387D, 0xA6A6D2E8, 0x2626B74B, + 0x3C3C57D6, 0x93938A32, 0x8282EED8, 0x525298FD, 0x7B7BD437, 0xBBBB3771, 0x5B5B97F1, 0x474783E1, + 0x24243C30, 0x5151E20F, 0xBABAC6F8, 0x4A4AF31B, 0xBFBF4887, 0x0D0D70FA, 0xB0B0B306, 0x7575DE3F, + 0xD2D2FD5E, 0x7D7D20BA, 0x666631AE, 0x3A3AA35B, 0x59591C8A, 0x00000000, 0xCDCD93BC, 0x1A1AE09D, + 0xAEAE2C6D, 0x7F7FABC1, 0x2B2BC7B1, 0xBEBEB90E, 0xE0E0A080, 0x8A8A105D, 0x3B3B52D2, 0x6464BAD5, + 0xD8D888A0, 0xE7E7A584, 0x5F5FE807, 0x1B1B1114, 0x2C2CC2B5, 0xFCFCB490, 0x3131272C, 0x808065A3, + 0x73732AB2, 0x0C0C8173, 0x79795F4C, 0x6B6B4154, 0x4B4B0292, 0x53536974, 0x94948F36, 0x83831F51, + 0x2A2A3638, 0xC4C49CB0, 0x2222C8BD, 0xD5D5F85A, 0xBDBDC3FC, 0x48487860, 0xFFFFCE62, 0x4C4C0796, + 0x4141776C, 0xC7C7E642, 0xEBEB24F7, 0x1C1C1410, 0x5D5D637C, 0x36362228, 0x6767C027, 0xE9E9AF8C, + 0x4444F913, 0x1414EA95, 0xF5F5BB9C, 0xCFCF18C7, 0x3F3F2D24, 0xC0C0E346, 0x7272DB3B, 0x54546C70, + 0x29294CCA, 0xF0F035E3, 0x0808FE85, 0xC6C617CB, 0xF3F34F11, 0x8C8CE4D0, 0xA4A45993, 0xCACA96B8, + 0x68683BA6, 0xB8B84D83, 0x38382820, 0xE5E52EFF, 0xADAD569F, 0x0B0B8477, 0xC8C81DC3, 0x9999FFCC, + 0x5858ED03, 0x19199A6F, 0x0E0E0A08, 0x95957EBF, 0x70705040, 0xF7F730E7, 0x6E6ECF2B, 0x1F1F6EE2, + 0xB5B53D79, 0x09090F0C, 0x616134AA, 0x57571682, 0x9F9F0B41, 0x9D9D803A, 0x111164EA, 0x2525CDB9, + 0xAFAFDDE4, 0x4545089A, 0xDFDF8DA4, 0xA3A35C97, 0xEAEAD57E, 0x353558DA, 0xEDEDD07A, 0x4343FC17, + 0xF8F8CB66, 0xFBFBB194, 0x3737D3A1, 0xFAFA401D, 0xC2C2683D, 0xB4B4CCF0, 0x32325DDE, 0x9C9C71B3, + 0x5656E70B, 0xE3E3DA72, 0x878760A7, 0x15151B1C, 0xF9F93AEF, 0x6363BFD1, 0x3434A953, 0x9A9A853E, + 0xB1B1428F, 0x7C7CD133, 0x88889B26, 0x3D3DA65F, 0xA1A1D7EC, 0xE4E4DF76, 0x8181942A, 0x91910149, + 0x0F0FFB81, 0xEEEEAA88, 0x161661EE, 0xD7D77321, 0x9797F5C4, 0xA5A5A81A, 0xFEFE3FEB, 0x6D6DB5D9, + 0x7878AEC5, 0xC5C56D39, 0x1D1DE599, 0x7676A4CD, 0x3E3EDCAD, 0xCBCB6731, 0xB6B6478B, 0xEFEF5B01, + 0x12121E18, 0x6060C523, 0x6A6AB0DD, 0x4D4DF61F, 0xCECEE94E, 0xDEDE7C2D, 0x55559DF9, 0x7E7E5A48, + 0x2121B24F, 0x03037AF2, 0xA0A02665, 0x5E5E198E, 0x5A5A6678, 0x65654B5C, 0x62624E58, 0xFDFD4519, + 0x0606F48D, 0x404086E5, 0xF2F2BE98, 0x3333AC57, 0x17179067, 0x05058E7F, 0xE8E85E05, 0x4F4F7D64, + 0x89896AAF, 0x10109563, 0x74742FB6, 0x0A0A75FE, 0x5C5C92F5, 0x9B9B74B7, 0x2D2D333C, 0x3030D6A5, + 0x2E2E49CE, 0x494989E9, 0x46467268, 0x77775544, 0xA8A8D8E0, 0x9696044D, 0x2828BD43, 0xA9A92969, + 0xD9D97929, 0x8686912E, 0xD1D187AC, 0xF4F44A15, 0x8D8D1559, 0xD6D682A8, 0xB9B9BC0A, 0x42420D9E, + 0xF6F6C16E, 0x2F2FB847, 0xDDDD06DF, 0x23233934, 0xCCCC6235, 0xF1F1C46A, 0xC1C112CF, 0x8585EBDC, + 0x8F8F9E22, 0x7171A1C9, 0x9090F0C0, 0xAAAA539B, 0x0101F189, 0x8B8BE1D4, 0x4E4E8CED, 0x8E8E6FAB, + 0xABABA212, 0x6F6F3EA2, 0xE6E6540D, 0xDBDBF252, 0x92927BBB, 0xB7B7B602, 0x6969CA2F, 0x3939D9A9, + 0xD3D30CD7, 0xA7A72361, 0xA2A2AD1E, 0xC3C399B4, 0x6C6C4450, 0x07070504, 0x04047FF6, 0x272746C2, + 0xACACA716, 0xD0D07625, 0x50501386, 0xDCDCF756, 0x84841A55, 0xE1E15109, 0x7A7A25BE, 0x1313EF91 + ); + + /** + * M-Table + * + * @var array + * @access private + */ + var $m1 = array( + 0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707, 0xFD985252, 0xA3658080, 0x76DFE4E4, + 0x9A084545, 0x92024B4B, 0x80A0E0E0, 0x78665A5A, 0xE4DDAFAF, 0xDDB06A6A, 0xD1BF6363, 0x38362A2A, + 0x0D54E6E6, 0xC6432020, 0x3562CCCC, 0x98BEF2F2, 0x181E1212, 0xF724EBEB, 0xECD7A1A1, 0x6C774141, + 0x43BD2828, 0x7532BCBC, 0x37D47B7B, 0x269B8888, 0xFA700D0D, 0x13F94444, 0x94B1FBFB, 0x485A7E7E, + 0xF27A0303, 0xD0E48C8C, 0x8B47B6B6, 0x303C2424, 0x84A5E7E7, 0x54416B6B, 0xDF06DDDD, 0x23C56060, + 0x1945FDFD, 0x5BA33A3A, 0x3D68C2C2, 0x59158D8D, 0xF321ECEC, 0xAE316666, 0xA23E6F6F, 0x82165757, + 0x63951010, 0x015BEFEF, 0x834DB8B8, 0x2E918686, 0xD9B56D6D, 0x511F8383, 0x9B53AAAA, 0x7C635D5D, + 0xA63B6868, 0xEB3FFEFE, 0xA5D63030, 0xBE257A7A, 0x16A7ACAC, 0x0C0F0909, 0xE335F0F0, 0x6123A7A7, + 0xC0F09090, 0x8CAFE9E9, 0x3A809D9D, 0xF5925C5C, 0x73810C0C, 0x2C273131, 0x2576D0D0, 0x0BE75656, + 0xBB7B9292, 0x4EE9CECE, 0x89F10101, 0x6B9F1E1E, 0x53A93434, 0x6AC4F1F1, 0xB499C3C3, 0xF1975B5B, + 0xE1834747, 0xE66B1818, 0xBDC82222, 0x450E9898, 0xE26E1F1F, 0xF4C9B3B3, 0xB62F7474, 0x66CBF8F8, + 0xCCFF9999, 0x95EA1414, 0x03ED5858, 0x56F7DCDC, 0xD4E18B8B, 0x1C1B1515, 0x1EADA2A2, 0xD70CD3D3, + 0xFB2BE2E2, 0xC31DC8C8, 0x8E195E5E, 0xB5C22C2C, 0xE9894949, 0xCF12C1C1, 0xBF7E9595, 0xBA207D7D, + 0xEA641111, 0x77840B0B, 0x396DC5C5, 0xAF6A8989, 0x33D17C7C, 0xC9A17171, 0x62CEFFFF, 0x7137BBBB, + 0x81FB0F0F, 0x793DB5B5, 0x0951E1E1, 0xADDC3E3E, 0x242D3F3F, 0xCDA47676, 0xF99D5555, 0xD8EE8282, + 0xE5864040, 0xC5AE7878, 0xB9CD2525, 0x4D049696, 0x44557777, 0x080A0E0E, 0x86135050, 0xE730F7F7, + 0xA1D33737, 0x1D40FAFA, 0xAA346161, 0xED8C4E4E, 0x06B3B0B0, 0x706C5454, 0xB22A7373, 0xD2523B3B, + 0x410B9F9F, 0x7B8B0202, 0xA088D8D8, 0x114FF3F3, 0x3167CBCB, 0xC2462727, 0x27C06767, 0x90B4FCFC, + 0x20283838, 0xF67F0404, 0x60784848, 0xFF2EE5E5, 0x96074C4C, 0x5C4B6565, 0xB1C72B2B, 0xAB6F8E8E, + 0x9E0D4242, 0x9CBBF5F5, 0x52F2DBDB, 0x1BF34A4A, 0x5FA63D3D, 0x9359A4A4, 0x0ABCB9B9, 0xEF3AF9F9, + 0x91EF1313, 0x85FE0808, 0x49019191, 0xEE611616, 0x2D7CDEDE, 0x4FB22121, 0x8F42B1B1, 0x3BDB7272, + 0x47B82F2F, 0x8748BFBF, 0x6D2CAEAE, 0x46E3C0C0, 0xD6573C3C, 0x3E859A9A, 0x6929A9A9, 0x647D4F4F, + 0x2A948181, 0xCE492E2E, 0xCB17C6C6, 0x2FCA6969, 0xFCC3BDBD, 0x975CA3A3, 0x055EE8E8, 0x7AD0EDED, + 0xAC87D1D1, 0x7F8E0505, 0xD5BA6464, 0x1AA8A5A5, 0x4BB72626, 0x0EB9BEBE, 0xA7608787, 0x5AF8D5D5, + 0x28223636, 0x14111B1B, 0x3FDE7575, 0x2979D9D9, 0x88AAEEEE, 0x3C332D2D, 0x4C5F7979, 0x02B6B7B7, + 0xB896CACA, 0xDA583535, 0xB09CC4C4, 0x17FC4343, 0x551A8484, 0x1FF64D4D, 0x8A1C5959, 0x7D38B2B2, + 0x57AC3333, 0xC718CFCF, 0x8DF40606, 0x74695353, 0xB7749B9B, 0xC4F59797, 0x9F56ADAD, 0x72DAE3E3, + 0x7ED5EAEA, 0x154AF4F4, 0x229E8F8F, 0x12A2ABAB, 0x584E6262, 0x07E85F5F, 0x99E51D1D, 0x34392323, + 0x6EC1F6F6, 0x50446C6C, 0xDE5D3232, 0x68724646, 0x6526A0A0, 0xBC93CDCD, 0xDB03DADA, 0xF8C6BABA, + 0xC8FA9E9E, 0xA882D6D6, 0x2BCF6E6E, 0x40507070, 0xDCEB8585, 0xFE750A0A, 0x328A9393, 0xA48DDFDF, + 0xCA4C2929, 0x10141C1C, 0x2173D7D7, 0xF0CCB4B4, 0xD309D4D4, 0x5D108A8A, 0x0FE25151, 0x00000000, + 0x6F9A1919, 0x9DE01A1A, 0x368F9494, 0x42E6C7C7, 0x4AECC9C9, 0x5EFDD2D2, 0xC1AB7F7F, 0xE0D8A8A8 + ); + + /** + * M-Table + * + * @var array + * @access private + */ + var $m2 = array( + 0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03, 0x027B028B, 0xE2FBE22B, 0x9EC89EFA, + 0xC94AC9EC, 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F, 0x9845980E, 0xB27DB238, 0xA6E8A6D2, 0x264B26B7, + 0x3CD63C57, 0x9332938A, 0x82D882EE, 0x52FD5298, 0x7B377BD4, 0xBB71BB37, 0x5BF15B97, 0x47E14783, + 0x2430243C, 0x510F51E2, 0xBAF8BAC6, 0x4A1B4AF3, 0xBF87BF48, 0x0DFA0D70, 0xB006B0B3, 0x753F75DE, + 0xD25ED2FD, 0x7DBA7D20, 0x66AE6631, 0x3A5B3AA3, 0x598A591C, 0x00000000, 0xCDBCCD93, 0x1A9D1AE0, + 0xAE6DAE2C, 0x7FC17FAB, 0x2BB12BC7, 0xBE0EBEB9, 0xE080E0A0, 0x8A5D8A10, 0x3BD23B52, 0x64D564BA, + 0xD8A0D888, 0xE784E7A5, 0x5F075FE8, 0x1B141B11, 0x2CB52CC2, 0xFC90FCB4, 0x312C3127, 0x80A38065, + 0x73B2732A, 0x0C730C81, 0x794C795F, 0x6B546B41, 0x4B924B02, 0x53745369, 0x9436948F, 0x8351831F, + 0x2A382A36, 0xC4B0C49C, 0x22BD22C8, 0xD55AD5F8, 0xBDFCBDC3, 0x48604878, 0xFF62FFCE, 0x4C964C07, + 0x416C4177, 0xC742C7E6, 0xEBF7EB24, 0x1C101C14, 0x5D7C5D63, 0x36283622, 0x672767C0, 0xE98CE9AF, + 0x441344F9, 0x149514EA, 0xF59CF5BB, 0xCFC7CF18, 0x3F243F2D, 0xC046C0E3, 0x723B72DB, 0x5470546C, + 0x29CA294C, 0xF0E3F035, 0x088508FE, 0xC6CBC617, 0xF311F34F, 0x8CD08CE4, 0xA493A459, 0xCAB8CA96, + 0x68A6683B, 0xB883B84D, 0x38203828, 0xE5FFE52E, 0xAD9FAD56, 0x0B770B84, 0xC8C3C81D, 0x99CC99FF, + 0x580358ED, 0x196F199A, 0x0E080E0A, 0x95BF957E, 0x70407050, 0xF7E7F730, 0x6E2B6ECF, 0x1FE21F6E, + 0xB579B53D, 0x090C090F, 0x61AA6134, 0x57825716, 0x9F419F0B, 0x9D3A9D80, 0x11EA1164, 0x25B925CD, + 0xAFE4AFDD, 0x459A4508, 0xDFA4DF8D, 0xA397A35C, 0xEA7EEAD5, 0x35DA3558, 0xED7AEDD0, 0x431743FC, + 0xF866F8CB, 0xFB94FBB1, 0x37A137D3, 0xFA1DFA40, 0xC23DC268, 0xB4F0B4CC, 0x32DE325D, 0x9CB39C71, + 0x560B56E7, 0xE372E3DA, 0x87A78760, 0x151C151B, 0xF9EFF93A, 0x63D163BF, 0x345334A9, 0x9A3E9A85, + 0xB18FB142, 0x7C337CD1, 0x8826889B, 0x3D5F3DA6, 0xA1ECA1D7, 0xE476E4DF, 0x812A8194, 0x91499101, + 0x0F810FFB, 0xEE88EEAA, 0x16EE1661, 0xD721D773, 0x97C497F5, 0xA51AA5A8, 0xFEEBFE3F, 0x6DD96DB5, + 0x78C578AE, 0xC539C56D, 0x1D991DE5, 0x76CD76A4, 0x3EAD3EDC, 0xCB31CB67, 0xB68BB647, 0xEF01EF5B, + 0x1218121E, 0x602360C5, 0x6ADD6AB0, 0x4D1F4DF6, 0xCE4ECEE9, 0xDE2DDE7C, 0x55F9559D, 0x7E487E5A, + 0x214F21B2, 0x03F2037A, 0xA065A026, 0x5E8E5E19, 0x5A785A66, 0x655C654B, 0x6258624E, 0xFD19FD45, + 0x068D06F4, 0x40E54086, 0xF298F2BE, 0x335733AC, 0x17671790, 0x057F058E, 0xE805E85E, 0x4F644F7D, + 0x89AF896A, 0x10631095, 0x74B6742F, 0x0AFE0A75, 0x5CF55C92, 0x9BB79B74, 0x2D3C2D33, 0x30A530D6, + 0x2ECE2E49, 0x49E94989, 0x46684672, 0x77447755, 0xA8E0A8D8, 0x964D9604, 0x284328BD, 0xA969A929, + 0xD929D979, 0x862E8691, 0xD1ACD187, 0xF415F44A, 0x8D598D15, 0xD6A8D682, 0xB90AB9BC, 0x429E420D, + 0xF66EF6C1, 0x2F472FB8, 0xDDDFDD06, 0x23342339, 0xCC35CC62, 0xF16AF1C4, 0xC1CFC112, 0x85DC85EB, + 0x8F228F9E, 0x71C971A1, 0x90C090F0, 0xAA9BAA53, 0x018901F1, 0x8BD48BE1, 0x4EED4E8C, 0x8EAB8E6F, + 0xAB12ABA2, 0x6FA26F3E, 0xE60DE654, 0xDB52DBF2, 0x92BB927B, 0xB702B7B6, 0x692F69CA, 0x39A939D9, + 0xD3D7D30C, 0xA761A723, 0xA21EA2AD, 0xC3B4C399, 0x6C506C44, 0x07040705, 0x04F6047F, 0x27C22746, + 0xAC16ACA7, 0xD025D076, 0x50865013, 0xDC56DCF7, 0x8455841A, 0xE109E151, 0x7ABE7A25, 0x139113EF + ); + + /** + * M-Table + * + * @var array + * @access private + */ + var $m3 = array( + 0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405, 0x9852FD98, 0x6580A365, 0xDFE476DF, + 0x08459A08, 0x024B9202, 0xA0E080A0, 0x665A7866, 0xDDAFE4DD, 0xB06ADDB0, 0xBF63D1BF, 0x362A3836, + 0x54E60D54, 0x4320C643, 0x62CC3562, 0xBEF298BE, 0x1E12181E, 0x24EBF724, 0xD7A1ECD7, 0x77416C77, + 0xBD2843BD, 0x32BC7532, 0xD47B37D4, 0x9B88269B, 0x700DFA70, 0xF94413F9, 0xB1FB94B1, 0x5A7E485A, + 0x7A03F27A, 0xE48CD0E4, 0x47B68B47, 0x3C24303C, 0xA5E784A5, 0x416B5441, 0x06DDDF06, 0xC56023C5, + 0x45FD1945, 0xA33A5BA3, 0x68C23D68, 0x158D5915, 0x21ECF321, 0x3166AE31, 0x3E6FA23E, 0x16578216, + 0x95106395, 0x5BEF015B, 0x4DB8834D, 0x91862E91, 0xB56DD9B5, 0x1F83511F, 0x53AA9B53, 0x635D7C63, + 0x3B68A63B, 0x3FFEEB3F, 0xD630A5D6, 0x257ABE25, 0xA7AC16A7, 0x0F090C0F, 0x35F0E335, 0x23A76123, + 0xF090C0F0, 0xAFE98CAF, 0x809D3A80, 0x925CF592, 0x810C7381, 0x27312C27, 0x76D02576, 0xE7560BE7, + 0x7B92BB7B, 0xE9CE4EE9, 0xF10189F1, 0x9F1E6B9F, 0xA93453A9, 0xC4F16AC4, 0x99C3B499, 0x975BF197, + 0x8347E183, 0x6B18E66B, 0xC822BDC8, 0x0E98450E, 0x6E1FE26E, 0xC9B3F4C9, 0x2F74B62F, 0xCBF866CB, + 0xFF99CCFF, 0xEA1495EA, 0xED5803ED, 0xF7DC56F7, 0xE18BD4E1, 0x1B151C1B, 0xADA21EAD, 0x0CD3D70C, + 0x2BE2FB2B, 0x1DC8C31D, 0x195E8E19, 0xC22CB5C2, 0x8949E989, 0x12C1CF12, 0x7E95BF7E, 0x207DBA20, + 0x6411EA64, 0x840B7784, 0x6DC5396D, 0x6A89AF6A, 0xD17C33D1, 0xA171C9A1, 0xCEFF62CE, 0x37BB7137, + 0xFB0F81FB, 0x3DB5793D, 0x51E10951, 0xDC3EADDC, 0x2D3F242D, 0xA476CDA4, 0x9D55F99D, 0xEE82D8EE, + 0x8640E586, 0xAE78C5AE, 0xCD25B9CD, 0x04964D04, 0x55774455, 0x0A0E080A, 0x13508613, 0x30F7E730, + 0xD337A1D3, 0x40FA1D40, 0x3461AA34, 0x8C4EED8C, 0xB3B006B3, 0x6C54706C, 0x2A73B22A, 0x523BD252, + 0x0B9F410B, 0x8B027B8B, 0x88D8A088, 0x4FF3114F, 0x67CB3167, 0x4627C246, 0xC06727C0, 0xB4FC90B4, + 0x28382028, 0x7F04F67F, 0x78486078, 0x2EE5FF2E, 0x074C9607, 0x4B655C4B, 0xC72BB1C7, 0x6F8EAB6F, + 0x0D429E0D, 0xBBF59CBB, 0xF2DB52F2, 0xF34A1BF3, 0xA63D5FA6, 0x59A49359, 0xBCB90ABC, 0x3AF9EF3A, + 0xEF1391EF, 0xFE0885FE, 0x01914901, 0x6116EE61, 0x7CDE2D7C, 0xB2214FB2, 0x42B18F42, 0xDB723BDB, + 0xB82F47B8, 0x48BF8748, 0x2CAE6D2C, 0xE3C046E3, 0x573CD657, 0x859A3E85, 0x29A96929, 0x7D4F647D, + 0x94812A94, 0x492ECE49, 0x17C6CB17, 0xCA692FCA, 0xC3BDFCC3, 0x5CA3975C, 0x5EE8055E, 0xD0ED7AD0, + 0x87D1AC87, 0x8E057F8E, 0xBA64D5BA, 0xA8A51AA8, 0xB7264BB7, 0xB9BE0EB9, 0x6087A760, 0xF8D55AF8, + 0x22362822, 0x111B1411, 0xDE753FDE, 0x79D92979, 0xAAEE88AA, 0x332D3C33, 0x5F794C5F, 0xB6B702B6, + 0x96CAB896, 0x5835DA58, 0x9CC4B09C, 0xFC4317FC, 0x1A84551A, 0xF64D1FF6, 0x1C598A1C, 0x38B27D38, + 0xAC3357AC, 0x18CFC718, 0xF4068DF4, 0x69537469, 0x749BB774, 0xF597C4F5, 0x56AD9F56, 0xDAE372DA, + 0xD5EA7ED5, 0x4AF4154A, 0x9E8F229E, 0xA2AB12A2, 0x4E62584E, 0xE85F07E8, 0xE51D99E5, 0x39233439, + 0xC1F66EC1, 0x446C5044, 0x5D32DE5D, 0x72466872, 0x26A06526, 0x93CDBC93, 0x03DADB03, 0xC6BAF8C6, + 0xFA9EC8FA, 0x82D6A882, 0xCF6E2BCF, 0x50704050, 0xEB85DCEB, 0x750AFE75, 0x8A93328A, 0x8DDFA48D, + 0x4C29CA4C, 0x141C1014, 0x73D72173, 0xCCB4F0CC, 0x09D4D309, 0x108A5D10, 0xE2510FE2, 0x00000000, + 0x9A196F9A, 0xE01A9DE0, 0x8F94368F, 0xE6C742E6, 0xECC94AEC, 0xFDD25EFD, 0xAB7FC1AB, 0xD8A8E0D8 + ); + + /** + * The Key Schedule Array + * + * @var array + * @access private + */ + var $K = array(); + + /** + * The Key depended S-Table 0 + * + * @var array + * @access private + */ + var $S0 = array(); + + /** + * The Key depended S-Table 1 + * + * @var array + * @access private + */ + var $S1 = array(); + + /** + * The Key depended S-Table 2 + * + * @var array + * @access private + */ + var $S2 = array(); + + /** + * The Key depended S-Table 3 + * + * @var array + * @access private + */ + var $S3 = array(); + + /** + * Holds the last used key + * + * @var array + * @access private + */ + var $kl; + + /** + * The Key Length (in bytes) + * + * @see Crypt_Twofish::setKeyLength() + * @var int + * @access private + */ + var $key_length = 16; + + /** + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. + * + * $mode could be: + * + * - CRYPT_MODE_ECB + * + * - CRYPT_MODE_CBC + * + * - CRYPT_MODE_CTR + * + * - CRYPT_MODE_CFB + * + * - CRYPT_MODE_OFB + * + * (or the alias constants of the chosen cipher, for example for AES: CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC ...) + * + * If not explicitly set, CRYPT_MODE_CBC will be used. + * + * @param int $mode + * @access public + */ + function __construct($mode = self::MODE_CBC) + { + parent::__construct($mode); + + $this->m0 = array_map('intval', $this->m0); + $this->m1 = array_map('intval', $this->m1); + $this->m2 = array_map('intval', $this->m2); + $this->m3 = array_map('intval', $this->m3); + $this->q0 = array_map('intval', $this->q0); + $this->q1 = array_map('intval', $this->q1); + } + + /** + * Sets the key length. + * + * Valid key lengths are 128, 192 or 256 bits + * + * @access public + * @param int $length + */ + function setKeyLength($length) + { + switch (true) { + case $length <= 128: + $this->key_length = 16; + break; + case $length <= 192: + $this->key_length = 24; + break; + default: + $this->key_length = 32; + } + + parent::setKeyLength($length); + } + + /** + * Setup the key (expansion) + * + * @see \phpseclib\Crypt\Base::_setupKey() + * @access private + */ + function _setupKey() + { + if (isset($this->kl['key']) && $this->key === $this->kl['key']) { + // already expanded + return; + } + $this->kl = array('key' => $this->key); + + /* Key expanding and generating the key-depended s-boxes */ + $le_longs = unpack('V*', $this->key); + $key = unpack('C*', $this->key); + $m0 = $this->m0; + $m1 = $this->m1; + $m2 = $this->m2; + $m3 = $this->m3; + $q0 = $this->q0; + $q1 = $this->q1; + + $K = $S0 = $S1 = $S2 = $S3 = array(); + + switch (strlen($this->key)) { + case 16: + list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[1], $le_longs[2]); + list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[3], $le_longs[4]); + for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) { + $A = $m0[$q0[$q0[$i] ^ $key[ 9]] ^ $key[1]] ^ + $m1[$q0[$q1[$i] ^ $key[10]] ^ $key[2]] ^ + $m2[$q1[$q0[$i] ^ $key[11]] ^ $key[3]] ^ + $m3[$q1[$q1[$i] ^ $key[12]] ^ $key[4]]; + $B = $m0[$q0[$q0[$j] ^ $key[13]] ^ $key[5]] ^ + $m1[$q0[$q1[$j] ^ $key[14]] ^ $key[6]] ^ + $m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^ + $m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]]; + $B = ($B << 8) | ($B >> 24 & 0xff); + $A = $this->safe_intval($A + $B); + $K[] = $A; + $A = $this->safe_intval($A + $B); + $K[] = ($A << 9 | $A >> 23 & 0x1ff); + } + for ($i = 0; $i < 256; ++$i) { + $S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0]; + $S1[$i] = $m1[$q0[$q1[$i] ^ $s5] ^ $s1]; + $S2[$i] = $m2[$q1[$q0[$i] ^ $s6] ^ $s2]; + $S3[$i] = $m3[$q1[$q1[$i] ^ $s7] ^ $s3]; + } + break; + case 24: + list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[1], $le_longs[2]); + list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[3], $le_longs[4]); + list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[5], $le_longs[6]); + for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) { + $A = $m0[$q0[$q0[$q1[$i] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^ + $m1[$q0[$q1[$q1[$i] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^ + $m2[$q1[$q0[$q0[$i] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^ + $m3[$q1[$q1[$q0[$i] ^ $key[20]] ^ $key[12]] ^ $key[4]]; + $B = $m0[$q0[$q0[$q1[$j] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^ + $m1[$q0[$q1[$q1[$j] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^ + $m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ + $m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]]; + $B = ($B << 8) | ($B >> 24 & 0xff); + $A = $this->safe_intval($A + $B); + $K[] = $A; + $A = $this->safe_intval($A + $B); + $K[] = ($A << 9 | $A >> 23 & 0x1ff); + } + for ($i = 0; $i < 256; ++$i) { + $S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0]; + $S1[$i] = $m1[$q0[$q1[$q1[$i] ^ $s9] ^ $s5] ^ $s1]; + $S2[$i] = $m2[$q1[$q0[$q0[$i] ^ $sa] ^ $s6] ^ $s2]; + $S3[$i] = $m3[$q1[$q1[$q0[$i] ^ $sb] ^ $s7] ^ $s3]; + } + break; + default: // 32 + list($sf, $se, $sd, $sc) = $this->_mdsrem($le_longs[1], $le_longs[2]); + list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[3], $le_longs[4]); + list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[5], $le_longs[6]); + list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[7], $le_longs[8]); + for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) { + $A = $m0[$q0[$q0[$q1[$q1[$i] ^ $key[25]] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^ + $m1[$q0[$q1[$q1[$q0[$i] ^ $key[26]] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^ + $m2[$q1[$q0[$q0[$q0[$i] ^ $key[27]] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^ + $m3[$q1[$q1[$q0[$q1[$i] ^ $key[28]] ^ $key[20]] ^ $key[12]] ^ $key[4]]; + $B = $m0[$q0[$q0[$q1[$q1[$j] ^ $key[29]] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^ + $m1[$q0[$q1[$q1[$q0[$j] ^ $key[30]] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^ + $m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ + $m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]]; + $B = ($B << 8) | ($B >> 24 & 0xff); + $A = $this->safe_intval($A + $B); + $K[] = $A; + $A = $this->safe_intval($A + $B); + $K[] = ($A << 9 | $A >> 23 & 0x1ff); + } + for ($i = 0; $i < 256; ++$i) { + $S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0]; + $S1[$i] = $m1[$q0[$q1[$q1[$q0[$i] ^ $sd] ^ $s9] ^ $s5] ^ $s1]; + $S2[$i] = $m2[$q1[$q0[$q0[$q0[$i] ^ $se] ^ $sa] ^ $s6] ^ $s2]; + $S3[$i] = $m3[$q1[$q1[$q0[$q1[$i] ^ $sf] ^ $sb] ^ $s7] ^ $s3]; + } + } + + $this->K = $K; + $this->S0 = $S0; + $this->S1 = $S1; + $this->S2 = $S2; + $this->S3 = $S3; + } + + /** + * _mdsrem function using by the twofish cipher algorithm + * + * @access private + * @param string $A + * @param string $B + * @return array + */ + function _mdsrem($A, $B) + { + // No gain by unrolling this loop. + for ($i = 0; $i < 8; ++$i) { + // Get most significant coefficient. + $t = 0xff & ($B >> 24); + + // Shift the others up. + $B = ($B << 8) | (0xff & ($A >> 24)); + $A<<= 8; + + $u = $t << 1; + + // Subtract the modular polynomial on overflow. + if ($t & 0x80) { + $u^= 0x14d; + } + + // Remove t * (a * x^2 + 1). + $B ^= $t ^ ($u << 16); + + // Form u = a*t + t/a = t*(a + 1/a). + $u^= 0x7fffffff & ($t >> 1); + + // Add the modular polynomial on underflow. + if ($t & 0x01) { + $u^= 0xa6 ; + } + + // Remove t * (a + 1/a) * (x^3 + x). + $B^= ($u << 24) | ($u << 8); + } + + return array( + 0xff & $B >> 24, + 0xff & $B >> 16, + 0xff & $B >> 8, + 0xff & $B); + } + + /** + * Encrypts a block + * + * @access private + * @param string $in + * @return string + */ + function _encryptBlock($in) + { + $S0 = $this->S0; + $S1 = $this->S1; + $S2 = $this->S2; + $S3 = $this->S3; + $K = $this->K; + + $in = unpack("V4", $in); + $R0 = $K[0] ^ $in[1]; + $R1 = $K[1] ^ $in[2]; + $R2 = $K[2] ^ $in[3]; + $R3 = $K[3] ^ $in[4]; + + $ki = 7; + while ($ki < 39) { + $t0 = $S0[ $R0 & 0xff] ^ + $S1[($R0 >> 8) & 0xff] ^ + $S2[($R0 >> 16) & 0xff] ^ + $S3[($R0 >> 24) & 0xff]; + $t1 = $S0[($R1 >> 24) & 0xff] ^ + $S1[ $R1 & 0xff] ^ + $S2[($R1 >> 8) & 0xff] ^ + $S3[($R1 >> 16) & 0xff]; + $R2^= $this->safe_intval($t0 + $t1 + $K[++$ki]); + $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); + $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]); + + $t0 = $S0[ $R2 & 0xff] ^ + $S1[($R2 >> 8) & 0xff] ^ + $S2[($R2 >> 16) & 0xff] ^ + $S3[($R2 >> 24) & 0xff]; + $t1 = $S0[($R3 >> 24) & 0xff] ^ + $S1[ $R3 & 0xff] ^ + $S2[($R3 >> 8) & 0xff] ^ + $S3[($R3 >> 16) & 0xff]; + $R0^= $this->safe_intval($t0 + $t1 + $K[++$ki]); + $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); + $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]); + } + + // @codingStandardsIgnoreStart + return pack("V4", $K[4] ^ $R2, + $K[5] ^ $R3, + $K[6] ^ $R0, + $K[7] ^ $R1); + // @codingStandardsIgnoreEnd + } + + /** + * Decrypts a block + * + * @access private + * @param string $in + * @return string + */ + function _decryptBlock($in) + { + $S0 = $this->S0; + $S1 = $this->S1; + $S2 = $this->S2; + $S3 = $this->S3; + $K = $this->K; + + $in = unpack("V4", $in); + $R0 = $K[4] ^ $in[1]; + $R1 = $K[5] ^ $in[2]; + $R2 = $K[6] ^ $in[3]; + $R3 = $K[7] ^ $in[4]; + + $ki = 40; + while ($ki > 8) { + $t0 = $S0[$R0 & 0xff] ^ + $S1[$R0 >> 8 & 0xff] ^ + $S2[$R0 >> 16 & 0xff] ^ + $S3[$R0 >> 24 & 0xff]; + $t1 = $S0[$R1 >> 24 & 0xff] ^ + $S1[$R1 & 0xff] ^ + $S2[$R1 >> 8 & 0xff] ^ + $S3[$R1 >> 16 & 0xff]; + $R3^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]); + $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; + $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]); + + $t0 = $S0[$R2 & 0xff] ^ + $S1[$R2 >> 8 & 0xff] ^ + $S2[$R2 >> 16 & 0xff] ^ + $S3[$R2 >> 24 & 0xff]; + $t1 = $S0[$R3 >> 24 & 0xff] ^ + $S1[$R3 & 0xff] ^ + $S2[$R3 >> 8 & 0xff] ^ + $S3[$R3 >> 16 & 0xff]; + $R1^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]); + $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; + $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]); + } + + // @codingStandardsIgnoreStart + return pack("V4", $K[0] ^ $R2, + $K[1] ^ $R3, + $K[2] ^ $R0, + $K[3] ^ $R1); + // @codingStandardsIgnoreEnd + } + + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see \phpseclib\Crypt\Base::_setupInlineCrypt() + * @access private + */ + function _setupInlineCrypt() + { + $lambda_functions =& self::_getLambdaFunctions(); + + // Max. 10 Ultra-Hi-optimized inline-crypt functions. After that, we'll (still) create very fast code, but not the ultimate fast one. + // (Currently, for Crypt_Twofish, one generated $lambda_function cost on php5.5@32bit ~140kb unfreeable mem and ~240kb on php5.5@64bit) + $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); + + // Generation of a unique hash for our generated code + $code_hash = "Crypt_Twofish, {$this->mode}"; + if ($gen_hi_opt_code) { + $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); + } + + $safeint = $this->safe_intval_inline(); + + if (!isset($lambda_functions[$code_hash])) { + switch (true) { + case $gen_hi_opt_code: + $K = $this->K; + $init_crypt = ' + static $S0, $S1, $S2, $S3; + if (!$S0) { + for ($i = 0; $i < 256; ++$i) { + $S0[] = (int)$self->S0[$i]; + $S1[] = (int)$self->S1[$i]; + $S2[] = (int)$self->S2[$i]; + $S3[] = (int)$self->S3[$i]; + } + } + '; + break; + default: + $K = array(); + for ($i = 0; $i < 40; ++$i) { + $K[] = '$K_' . $i; + } + $init_crypt = ' + $S0 = $self->S0; + $S1 = $self->S1; + $S2 = $self->S2; + $S3 = $self->S3; + list(' . implode(',', $K) . ') = $self->K; + '; + } + + // Generating encrypt code: + $encrypt_block = ' + $in = unpack("V4", $in); + $R0 = '.$K[0].' ^ $in[1]; + $R1 = '.$K[1].' ^ $in[2]; + $R2 = '.$K[2].' ^ $in[3]; + $R3 = '.$K[3].' ^ $in[4]; + '; + for ($ki = 7, $i = 0; $i < 8; ++$i) { + $encrypt_block.= ' + $t0 = $S0[ $R0 & 0xff] ^ + $S1[($R0 >> 8) & 0xff] ^ + $S2[($R0 >> 16) & 0xff] ^ + $S3[($R0 >> 24) & 0xff]; + $t1 = $S0[($R1 >> 24) & 0xff] ^ + $S1[ $R1 & 0xff] ^ + $S2[($R1 >> 8) & 0xff] ^ + $S3[($R1 >> 16) & 0xff]; + $R2^= ' . sprintf($safeint, '$t0 + $t1 + ' . $K[++$ki]) . '; + $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); + $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . '; + + $t0 = $S0[ $R2 & 0xff] ^ + $S1[($R2 >> 8) & 0xff] ^ + $S2[($R2 >> 16) & 0xff] ^ + $S3[($R2 >> 24) & 0xff]; + $t1 = $S0[($R3 >> 24) & 0xff] ^ + $S1[ $R3 & 0xff] ^ + $S2[($R3 >> 8) & 0xff] ^ + $S3[($R3 >> 16) & 0xff]; + $R0^= ' . sprintf($safeint, '($t0 + $t1 + ' . $K[++$ki] . ')') . '; + $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); + $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . '; + '; + } + $encrypt_block.= ' + $in = pack("V4", ' . $K[4] . ' ^ $R2, + ' . $K[5] . ' ^ $R3, + ' . $K[6] . ' ^ $R0, + ' . $K[7] . ' ^ $R1); + '; + + // Generating decrypt code: + $decrypt_block = ' + $in = unpack("V4", $in); + $R0 = '.$K[4].' ^ $in[1]; + $R1 = '.$K[5].' ^ $in[2]; + $R2 = '.$K[6].' ^ $in[3]; + $R3 = '.$K[7].' ^ $in[4]; + '; + for ($ki = 40, $i = 0; $i < 8; ++$i) { + $decrypt_block.= ' + $t0 = $S0[$R0 & 0xff] ^ + $S1[$R0 >> 8 & 0xff] ^ + $S2[$R0 >> 16 & 0xff] ^ + $S3[$R0 >> 24 & 0xff]; + $t1 = $S0[$R1 >> 24 & 0xff] ^ + $S1[$R1 & 0xff] ^ + $S2[$R1 >> 8 & 0xff] ^ + $S3[$R1 >> 16 & 0xff]; + $R3^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . '; + $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; + $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . '; + + $t0 = $S0[$R2 & 0xff] ^ + $S1[$R2 >> 8 & 0xff] ^ + $S2[$R2 >> 16 & 0xff] ^ + $S3[$R2 >> 24 & 0xff]; + $t1 = $S0[$R3 >> 24 & 0xff] ^ + $S1[$R3 & 0xff] ^ + $S2[$R3 >> 8 & 0xff] ^ + $S3[$R3 >> 16 & 0xff]; + $R1^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . '; + $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; + $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . '; + '; + } + $decrypt_block.= ' + $in = pack("V4", ' . $K[0] . ' ^ $R2, + ' . $K[1] . ' ^ $R3, + ' . $K[2] . ' ^ $R0, + ' . $K[3] . ' ^ $R1); + '; + + $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( + array( + 'init_crypt' => $init_crypt, + 'init_encrypt' => '', + 'init_decrypt' => '', + 'encrypt_block' => $encrypt_block, + 'decrypt_block' => $decrypt_block + ) + ); + } + $this->inline_crypt = $lambda_functions[$code_hash]; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php b/msd/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php new file mode 100644 index 0000000..72477fc --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php @@ -0,0 +1,577 @@ + + * @copyright 2012 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File; + +/** + * Pure-PHP ANSI Decoder + * + * @package ANSI + * @author Jim Wigginton + * @access public + */ +class ANSI +{ + /** + * Max Width + * + * @var int + * @access private + */ + var $max_x; + + /** + * Max Height + * + * @var int + * @access private + */ + var $max_y; + + /** + * Max History + * + * @var int + * @access private + */ + var $max_history; + + /** + * History + * + * @var array + * @access private + */ + var $history; + + /** + * History Attributes + * + * @var array + * @access private + */ + var $history_attrs; + + /** + * Current Column + * + * @var int + * @access private + */ + var $x; + + /** + * Current Row + * + * @var int + * @access private + */ + var $y; + + /** + * Old Column + * + * @var int + * @access private + */ + var $old_x; + + /** + * Old Row + * + * @var int + * @access private + */ + var $old_y; + + /** + * An empty attribute cell + * + * @var object + * @access private + */ + var $base_attr_cell; + + /** + * The current attribute cell + * + * @var object + * @access private + */ + var $attr_cell; + + /** + * An empty attribute row + * + * @var array + * @access private + */ + var $attr_row; + + /** + * The current screen text + * + * @var array + * @access private + */ + var $screen; + + /** + * The current screen attributes + * + * @var array + * @access private + */ + var $attrs; + + /** + * Current ANSI code + * + * @var string + * @access private + */ + var $ansi; + + /** + * Tokenization + * + * @var array + * @access private + */ + var $tokenization; + + /** + * Default Constructor. + * + * @return \phpseclib\File\ANSI + * @access public + */ + function __construct() + { + $attr_cell = new \stdClass(); + $attr_cell->bold = false; + $attr_cell->underline = false; + $attr_cell->blink = false; + $attr_cell->background = 'black'; + $attr_cell->foreground = 'white'; + $attr_cell->reverse = false; + $this->base_attr_cell = clone $attr_cell; + $this->attr_cell = clone $attr_cell; + + $this->setHistory(200); + $this->setDimensions(80, 24); + } + + /** + * Set terminal width and height + * + * Resets the screen as well + * + * @param int $x + * @param int $y + * @access public + */ + function setDimensions($x, $y) + { + $this->max_x = $x - 1; + $this->max_y = $y - 1; + $this->x = $this->y = 0; + $this->history = $this->history_attrs = array(); + $this->attr_row = array_fill(0, $this->max_x + 2, $this->base_attr_cell); + $this->screen = array_fill(0, $this->max_y + 1, ''); + $this->attrs = array_fill(0, $this->max_y + 1, $this->attr_row); + $this->ansi = ''; + } + + /** + * Set the number of lines that should be logged past the terminal height + * + * @param int $history + * @access public + */ + function setHistory($history) + { + $this->max_history = $history; + } + + /** + * Load a string + * + * @param string $source + * @access public + */ + function loadString($source) + { + $this->setDimensions($this->max_x + 1, $this->max_y + 1); + $this->appendString($source); + } + + /** + * Appdend a string + * + * @param string $source + * @access public + */ + function appendString($source) + { + $this->tokenization = array(''); + for ($i = 0; $i < strlen($source); $i++) { + if (strlen($this->ansi)) { + $this->ansi.= $source[$i]; + $chr = ord($source[$i]); + // http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements + // single character CSI's not currently supported + switch (true) { + case $this->ansi == "\x1B=": + $this->ansi = ''; + continue 2; + case strlen($this->ansi) == 2 && $chr >= 64 && $chr <= 95 && $chr != ord('['): + case strlen($this->ansi) > 2 && $chr >= 64 && $chr <= 126: + break; + default: + continue 2; + } + $this->tokenization[] = $this->ansi; + $this->tokenization[] = ''; + // http://ascii-table.com/ansi-escape-sequences-vt-100.php + switch ($this->ansi) { + case "\x1B[H": // Move cursor to upper left corner + $this->old_x = $this->x; + $this->old_y = $this->y; + $this->x = $this->y = 0; + break; + case "\x1B[J": // Clear screen from cursor down + $this->history = array_merge($this->history, array_slice(array_splice($this->screen, $this->y + 1), 0, $this->old_y)); + $this->screen = array_merge($this->screen, array_fill($this->y, $this->max_y, '')); + + $this->history_attrs = array_merge($this->history_attrs, array_slice(array_splice($this->attrs, $this->y + 1), 0, $this->old_y)); + $this->attrs = array_merge($this->attrs, array_fill($this->y, $this->max_y, $this->attr_row)); + + if (count($this->history) == $this->max_history) { + array_shift($this->history); + array_shift($this->history_attrs); + } + case "\x1B[K": // Clear screen from cursor right + $this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x); + + array_splice($this->attrs[$this->y], $this->x + 1, $this->max_x - $this->x, array_fill($this->x, $this->max_x - ($this->x - 1), $this->base_attr_cell)); + break; + case "\x1B[2K": // Clear entire line + $this->screen[$this->y] = str_repeat(' ', $this->x); + $this->attrs[$this->y] = $this->attr_row; + break; + case "\x1B[?1h": // set cursor key to application + case "\x1B[?25h": // show the cursor + case "\x1B(B": // set united states g0 character set + break; + case "\x1BE": // Move to next line + $this->_newLine(); + $this->x = 0; + break; + default: + switch (true) { + case preg_match('#\x1B\[(\d+)B#', $this->ansi, $match): // Move cursor down n lines + $this->old_y = $this->y; + $this->y+= $match[1]; + break; + case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): // Move cursor to screen location v,h + $this->old_x = $this->x; + $this->old_y = $this->y; + $this->x = $match[2] - 1; + $this->y = $match[1] - 1; + break; + case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match): // Move cursor right n lines + $this->old_x = $this->x; + $this->x+= $match[1]; + break; + case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines + $this->old_x = $this->x; + $this->x-= $match[1]; + if ($this->x < 0) { + $this->x = 0; + } + break; + case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window + break; + case preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): // character attributes + $attr_cell = &$this->attr_cell; + $mods = explode(';', $match[1]); + foreach ($mods as $mod) { + switch ($mod) { + case '': + case '0': // Turn off character attributes + $attr_cell = clone $this->base_attr_cell; + break; + case '1': // Turn bold mode on + $attr_cell->bold = true; + break; + case '4': // Turn underline mode on + $attr_cell->underline = true; + break; + case '5': // Turn blinking mode on + $attr_cell->blink = true; + break; + case '7': // Turn reverse video on + $attr_cell->reverse = !$attr_cell->reverse; + $temp = $attr_cell->background; + $attr_cell->background = $attr_cell->foreground; + $attr_cell->foreground = $temp; + break; + default: // set colors + //$front = $attr_cell->reverse ? &$attr_cell->background : &$attr_cell->foreground; + $front = &$attr_cell->{ $attr_cell->reverse ? 'background' : 'foreground' }; + //$back = $attr_cell->reverse ? &$attr_cell->foreground : &$attr_cell->background; + $back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' }; + switch ($mod) { + // @codingStandardsIgnoreStart + case '30': $front = 'black'; break; + case '31': $front = 'red'; break; + case '32': $front = 'green'; break; + case '33': $front = 'yellow'; break; + case '34': $front = 'blue'; break; + case '35': $front = 'magenta'; break; + case '36': $front = 'cyan'; break; + case '37': $front = 'white'; break; + + case '40': $back = 'black'; break; + case '41': $back = 'red'; break; + case '42': $back = 'green'; break; + case '43': $back = 'yellow'; break; + case '44': $back = 'blue'; break; + case '45': $back = 'magenta'; break; + case '46': $back = 'cyan'; break; + case '47': $back = 'white'; break; + // @codingStandardsIgnoreEnd + + default: + //user_error('Unsupported attribute: ' . $mod); + $this->ansi = ''; + break 2; + } + } + } + break; + default: + //user_error("{$this->ansi} is unsupported\r\n"); + } + } + $this->ansi = ''; + continue; + } + + $this->tokenization[count($this->tokenization) - 1].= $source[$i]; + switch ($source[$i]) { + case "\r": + $this->x = 0; + break; + case "\n": + $this->_newLine(); + break; + case "\x08": // backspace + if ($this->x) { + $this->x--; + $this->attrs[$this->y][$this->x] = clone $this->base_attr_cell; + $this->screen[$this->y] = substr_replace( + $this->screen[$this->y], + $source[$i], + $this->x, + 1 + ); + } + break; + case "\x0F": // shift + break; + case "\x1B": // start ANSI escape code + $this->tokenization[count($this->tokenization) - 1] = substr($this->tokenization[count($this->tokenization) - 1], 0, -1); + //if (!strlen($this->tokenization[count($this->tokenization) - 1])) { + // array_pop($this->tokenization); + //} + $this->ansi.= "\x1B"; + break; + default: + $this->attrs[$this->y][$this->x] = clone $this->attr_cell; + if ($this->x > strlen($this->screen[$this->y])) { + $this->screen[$this->y] = str_repeat(' ', $this->x); + } + $this->screen[$this->y] = substr_replace( + $this->screen[$this->y], + $source[$i], + $this->x, + 1 + ); + + if ($this->x > $this->max_x) { + $this->x = 0; + $this->_newLine(); + } else { + $this->x++; + } + } + } + } + + /** + * Add a new line + * + * Also update the $this->screen and $this->history buffers + * + * @access private + */ + function _newLine() + { + //if ($this->y < $this->max_y) { + // $this->y++; + //} + + while ($this->y >= $this->max_y) { + $this->history = array_merge($this->history, array(array_shift($this->screen))); + $this->screen[] = ''; + + $this->history_attrs = array_merge($this->history_attrs, array(array_shift($this->attrs))); + $this->attrs[] = $this->attr_row; + + if (count($this->history) >= $this->max_history) { + array_shift($this->history); + array_shift($this->history_attrs); + } + + $this->y--; + } + $this->y++; + } + + /** + * Returns the current coordinate without preformating + * + * @access private + * @return string + */ + function _processCoordinate($last_attr, $cur_attr, $char) + { + $output = ''; + + if ($last_attr != $cur_attr) { + $close = $open = ''; + if ($last_attr->foreground != $cur_attr->foreground) { + if ($cur_attr->foreground != 'white') { + $open.= ''; + } + if ($last_attr->foreground != 'white') { + $close = '' . $close; + } + } + if ($last_attr->background != $cur_attr->background) { + if ($cur_attr->background != 'black') { + $open.= ''; + } + if ($last_attr->background != 'black') { + $close = '' . $close; + } + } + if ($last_attr->bold != $cur_attr->bold) { + if ($cur_attr->bold) { + $open.= ''; + } else { + $close = '' . $close; + } + } + if ($last_attr->underline != $cur_attr->underline) { + if ($cur_attr->underline) { + $open.= ''; + } else { + $close = '' . $close; + } + } + if ($last_attr->blink != $cur_attr->blink) { + if ($cur_attr->blink) { + $open.= ''; + } else { + $close = '' . $close; + } + } + $output.= $close . $open; + } + + $output.= htmlspecialchars($char); + + return $output; + } + + /** + * Returns the current screen without preformating + * + * @access private + * @return string + */ + function _getScreen() + { + $output = ''; + $last_attr = $this->base_attr_cell; + for ($i = 0; $i <= $this->max_y; $i++) { + for ($j = 0; $j <= $this->max_x; $j++) { + $cur_attr = $this->attrs[$i][$j]; + $output.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->screen[$i][$j]) ? $this->screen[$i][$j] : ''); + $last_attr = $this->attrs[$i][$j]; + } + $output.= "\r\n"; + } + $output = substr($output, 0, -2); + // close any remaining open tags + $output.= $this->_processCoordinate($last_attr, $this->base_attr_cell, ''); + return rtrim($output); + } + + /** + * Returns the current screen + * + * @access public + * @return string + */ + function getScreen() + { + return '
      ' . $this->_getScreen() . '
      '; + } + + /** + * Returns the current screen and the x previous lines + * + * @access public + * @return string + */ + function getHistory() + { + $scrollback = ''; + $last_attr = $this->base_attr_cell; + for ($i = 0; $i < count($this->history); $i++) { + for ($j = 0; $j <= $this->max_x + 1; $j++) { + $cur_attr = $this->history_attrs[$i][$j]; + $scrollback.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->history[$i][$j]) ? $this->history[$i][$j] : ''); + $last_attr = $this->history_attrs[$i][$j]; + } + $scrollback.= "\r\n"; + } + $base_attr_cell = $this->base_attr_cell; + $this->base_attr_cell = $last_attr; + $scrollback.= $this->_getScreen(); + $this->base_attr_cell = $base_attr_cell; + + return '
      ' . $scrollback . '
      '; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php b/msd/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php new file mode 100644 index 0000000..2cb3033 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php @@ -0,0 +1,1469 @@ + + * @copyright 2012 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File; + +use phpseclib\File\ASN1\Element; +use phpseclib\Math\BigInteger; +use DateTime; +use DateTimeZone; + +/** + * Pure-PHP ASN.1 Parser + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +class ASN1 +{ + /**#@+ + * Tag Classes + * + * @access private + * @link http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12 + */ + const CLASS_UNIVERSAL = 0; + const CLASS_APPLICATION = 1; + const CLASS_CONTEXT_SPECIFIC = 2; + const CLASS_PRIVATE = 3; + /**#@-*/ + + /**#@+ + * Tag Classes + * + * @access private + * @link http://www.obj-sys.com/asn1tutorial/node124.html + */ + const TYPE_BOOLEAN = 1; + const TYPE_INTEGER = 2; + const TYPE_BIT_STRING = 3; + const TYPE_OCTET_STRING = 4; + const TYPE_NULL = 5; + const TYPE_OBJECT_IDENTIFIER = 6; + //const TYPE_OBJECT_DESCRIPTOR = 7; + //const TYPE_INSTANCE_OF = 8; // EXTERNAL + const TYPE_REAL = 9; + const TYPE_ENUMERATED = 10; + //const TYPE_EMBEDDED = 11; + const TYPE_UTF8_STRING = 12; + //const TYPE_RELATIVE_OID = 13; + const TYPE_SEQUENCE = 16; // SEQUENCE OF + const TYPE_SET = 17; // SET OF + /**#@-*/ + /**#@+ + * More Tag Classes + * + * @access private + * @link http://www.obj-sys.com/asn1tutorial/node10.html + */ + const TYPE_NUMERIC_STRING = 18; + const TYPE_PRINTABLE_STRING = 19; + const TYPE_TELETEX_STRING = 20; // T61String + const TYPE_VIDEOTEX_STRING = 21; + const TYPE_IA5_STRING = 22; + const TYPE_UTC_TIME = 23; + const TYPE_GENERALIZED_TIME = 24; + const TYPE_GRAPHIC_STRING = 25; + const TYPE_VISIBLE_STRING = 26; // ISO646String + const TYPE_GENERAL_STRING = 27; + const TYPE_UNIVERSAL_STRING = 28; + //const TYPE_CHARACTER_STRING = 29; + const TYPE_BMP_STRING = 30; + /**#@-*/ + + /**#@+ + * Tag Aliases + * + * These tags are kinda place holders for other tags. + * + * @access private + */ + const TYPE_CHOICE = -1; + const TYPE_ANY = -2; + /**#@-*/ + + /** + * ASN.1 object identifier + * + * @var array + * @access private + * @link http://en.wikipedia.org/wiki/Object_identifier + */ + var $oids = array(); + + /** + * Default date format + * + * @var string + * @access private + * @link http://php.net/class.datetime + */ + var $format = 'D, d M Y H:i:s O'; + + /** + * Default date format + * + * @var array + * @access private + * @see self::setTimeFormat() + * @see self::asn1map() + * @link http://php.net/class.datetime + */ + var $encoded; + + /** + * Filters + * + * If the mapping type is self::TYPE_ANY what do we actually encode it as? + * + * @var array + * @access private + * @see self::_encode_der() + */ + var $filters; + + /** + * Current Location of most recent ASN.1 encode process + * + * Useful for debug purposes + * + * @var array + * @see self::encode_der() + */ + var $location; + + /** + * Type mapping table for the ANY type. + * + * Structured or unknown types are mapped to a \phpseclib\File\ASN1\Element. + * Unambiguous types get the direct mapping (int/real/bool). + * Others are mapped as a choice, with an extra indexing level. + * + * @var array + * @access public + */ + var $ANYmap = array( + self::TYPE_BOOLEAN => true, + self::TYPE_INTEGER => true, + self::TYPE_BIT_STRING => 'bitString', + self::TYPE_OCTET_STRING => 'octetString', + self::TYPE_NULL => 'null', + self::TYPE_OBJECT_IDENTIFIER => 'objectIdentifier', + self::TYPE_REAL => true, + self::TYPE_ENUMERATED => 'enumerated', + self::TYPE_UTF8_STRING => 'utf8String', + self::TYPE_NUMERIC_STRING => 'numericString', + self::TYPE_PRINTABLE_STRING => 'printableString', + self::TYPE_TELETEX_STRING => 'teletexString', + self::TYPE_VIDEOTEX_STRING => 'videotexString', + self::TYPE_IA5_STRING => 'ia5String', + self::TYPE_UTC_TIME => 'utcTime', + self::TYPE_GENERALIZED_TIME => 'generalTime', + self::TYPE_GRAPHIC_STRING => 'graphicString', + self::TYPE_VISIBLE_STRING => 'visibleString', + self::TYPE_GENERAL_STRING => 'generalString', + self::TYPE_UNIVERSAL_STRING => 'universalString', + //self::TYPE_CHARACTER_STRING => 'characterString', + self::TYPE_BMP_STRING => 'bmpString' + ); + + /** + * String type to character size mapping table. + * + * Non-convertable types are absent from this table. + * size == 0 indicates variable length encoding. + * + * @var array + * @access public + */ + var $stringTypeSize = array( + self::TYPE_UTF8_STRING => 0, + self::TYPE_BMP_STRING => 2, + self::TYPE_UNIVERSAL_STRING => 4, + self::TYPE_PRINTABLE_STRING => 1, + self::TYPE_TELETEX_STRING => 1, + self::TYPE_IA5_STRING => 1, + self::TYPE_VISIBLE_STRING => 1, + ); + + /** + * Parse BER-encoding + * + * Serves a similar purpose to openssl's asn1parse + * + * @param string $encoded + * @return array + * @access public + */ + function decodeBER($encoded) + { + if ($encoded instanceof Element) { + $encoded = $encoded->element; + } + + $this->encoded = $encoded; + // encapsulate in an array for BC with the old decodeBER + return array($this->_decode_ber($encoded)); + } + + /** + * Parse BER-encoding (Helper function) + * + * Sometimes we want to get the BER encoding of a particular tag. $start lets us do that without having to reencode. + * $encoded is passed by reference for the recursive calls done for self::TYPE_BIT_STRING and + * self::TYPE_OCTET_STRING. In those cases, the indefinite length is used. + * + * @param string $encoded + * @param int $start + * @param int $encoded_pos + * @return array + * @access private + */ + function _decode_ber($encoded, $start = 0, $encoded_pos = 0) + { + $current = array('start' => $start); + + if (!isset($encoded[$encoded_pos])) { + return false; + } + $type = ord($encoded[$encoded_pos++]); + $startOffset = 1; + + $constructed = ($type >> 5) & 1; + + $tag = $type & 0x1F; + if ($tag == 0x1F) { + $tag = 0; + // process septets (since the eighth bit is ignored, it's not an octet) + do { + if (!isset($encoded[$encoded_pos])) { + return false; + } + $temp = ord($encoded[$encoded_pos++]); + $startOffset++; + $loop = $temp >> 7; + $tag <<= 7; + $temp &= 0x7F; + // "bits 7 to 1 of the first subsequent octet shall not all be zero" + if ($startOffset == 2 && $temp == 0) { + return false; + } + $tag |= $temp; + } while ($loop); + } + + $start+= $startOffset; + + // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13 + if (!isset($encoded[$encoded_pos])) { + return false; + } + $length = ord($encoded[$encoded_pos++]); + $start++; + if ($length == 0x80) { // indefinite length + // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all + // immediately available." -- paragraph 8.1.3.2.c + $length = strlen($encoded) - $encoded_pos; + } elseif ($length & 0x80) { // definite length, long form + // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only + // support it up to four. + $length&= 0x7F; + $temp = substr($encoded, $encoded_pos, $length); + $encoded_pos += $length; + // tags of indefinte length don't really have a header length; this length includes the tag + $current+= array('headerlength' => $length + 2); + $start+= $length; + extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4))); + } else { + $current+= array('headerlength' => 2); + } + + if ($length > (strlen($encoded) - $encoded_pos)) { + return false; + } + + $content = substr($encoded, $encoded_pos, $length); + $content_pos = 0; + + // at this point $length can be overwritten. it's only accurate for definite length things as is + + /* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1 + built-in types. It defines an application-independent data type that must be distinguishable from all other + data types. The other three classes are user defined. The APPLICATION class distinguishes data types that + have a wide, scattered use within a particular presentation context. PRIVATE distinguishes data types within + a particular organization or country. CONTEXT-SPECIFIC distinguishes members of a sequence or set, the + alternatives of a CHOICE, or universally tagged set members. Only the class number appears in braces for this + data type; the term CONTEXT-SPECIFIC does not appear. + + -- http://www.obj-sys.com/asn1tutorial/node12.html */ + $class = ($type >> 6) & 3; + switch ($class) { + case self::CLASS_APPLICATION: + case self::CLASS_PRIVATE: + case self::CLASS_CONTEXT_SPECIFIC: + if (!$constructed) { + return array( + 'type' => $class, + 'constant' => $tag, + 'content' => $content, + 'length' => $length + $start - $current['start'] + ); + } + + $newcontent = array(); + $remainingLength = $length; + while ($remainingLength > 0) { + $temp = $this->_decode_ber($content, $start, $content_pos); + if ($temp === false) { + break; + } + $length = $temp['length']; + // end-of-content octets - see paragraph 8.1.5 + if (substr($content, $content_pos + $length, 2) == "\0\0") { + $length+= 2; + $start+= $length; + $newcontent[] = $temp; + break; + } + $start+= $length; + $remainingLength-= $length; + $newcontent[] = $temp; + $content_pos += $length; + } + + return array( + 'type' => $class, + 'constant' => $tag, + // the array encapsulation is for BC with the old format + 'content' => $newcontent, + // the only time when $content['headerlength'] isn't defined is when the length is indefinite. + // the absence of $content['headerlength'] is how we know if something is indefinite or not. + // technically, it could be defined to be 2 and then another indicator could be used but whatever. + 'length' => $start - $current['start'] + ) + $current; + } + + $current+= array('type' => $tag); + + // decode UNIVERSAL tags + switch ($tag) { + case self::TYPE_BOOLEAN: + // "The contents octets shall consist of a single octet." -- paragraph 8.2.1 + if ($constructed || strlen($content) != 1) { + return false; + } + $current['content'] = (bool) ord($content[$content_pos]); + break; + case self::TYPE_INTEGER: + case self::TYPE_ENUMERATED: + if ($constructed) { + return false; + } + $current['content'] = new BigInteger(substr($content, $content_pos), -256); + break; + case self::TYPE_REAL: // not currently supported + return false; + case self::TYPE_BIT_STRING: + // The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit, + // the number of unused bits in the final subsequent octet. The number shall be in the range zero to + // seven. + if (!$constructed) { + $current['content'] = substr($content, $content_pos); + } else { + $temp = $this->_decode_ber($content, $start, $content_pos); + if ($temp === false) { + return false; + } + $length-= (strlen($content) - $content_pos); + $last = count($temp) - 1; + for ($i = 0; $i < $last; $i++) { + // all subtags should be bit strings + if ($temp[$i]['type'] != self::TYPE_BIT_STRING) { + return false; + } + $current['content'].= substr($temp[$i]['content'], 1); + } + // all subtags should be bit strings + if ($temp[$last]['type'] != self::TYPE_BIT_STRING) { + return false; + } + $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1); + } + break; + case self::TYPE_OCTET_STRING: + if (!$constructed) { + $current['content'] = substr($content, $content_pos); + } else { + $current['content'] = ''; + $length = 0; + while (substr($content, $content_pos, 2) != "\0\0") { + $temp = $this->_decode_ber($content, $length + $start, $content_pos); + if ($temp === false) { + return false; + } + $content_pos += $temp['length']; + // all subtags should be octet strings + if ($temp['type'] != self::TYPE_OCTET_STRING) { + return false; + } + $current['content'].= $temp['content']; + $length+= $temp['length']; + } + if (substr($content, $content_pos, 2) == "\0\0") { + $length+= 2; // +2 for the EOC + } + } + break; + case self::TYPE_NULL: + // "The contents octets shall not contain any octets." -- paragraph 8.8.2 + if ($constructed || strlen($content)) { + return false; + } + break; + case self::TYPE_SEQUENCE: + case self::TYPE_SET: + if (!$constructed) { + return false; + } + $offset = 0; + $current['content'] = array(); + $content_len = strlen($content); + while ($content_pos < $content_len) { + // if indefinite length construction was used and we have an end-of-content string next + // see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2 + if (!isset($current['headerlength']) && substr($content, $content_pos, 2) == "\0\0") { + $length = $offset + 2; // +2 for the EOC + break 2; + } + $temp = $this->_decode_ber($content, $start + $offset, $content_pos); + if ($temp === false) { + return false; + } + $content_pos += $temp['length']; + $current['content'][] = $temp; + $offset+= $temp['length']; + } + break; + case self::TYPE_OBJECT_IDENTIFIER: + if ($constructed) { + return false; + } + $current['content'] = $this->_decodeOID(substr($content, $content_pos)); + if ($current['content'] === false) { + return false; + } + break; + /* Each character string type shall be encoded as if it had been declared: + [UNIVERSAL x] IMPLICIT OCTET STRING + + -- X.690-0207.pdf#page=23 (paragraph 8.21.3) + + Per that, we're not going to do any validation. If there are any illegal characters in the string, + we don't really care */ + case self::TYPE_NUMERIC_STRING: + // 0,1,2,3,4,5,6,7,8,9, and space + case self::TYPE_PRINTABLE_STRING: + // Upper and lower case letters, digits, space, apostrophe, left/right parenthesis, plus sign, comma, + // hyphen, full stop, solidus, colon, equal sign, question mark + case self::TYPE_TELETEX_STRING: + // The Teletex character set in CCITT's T61, space, and delete + // see http://en.wikipedia.org/wiki/Teletex#Character_sets + case self::TYPE_VIDEOTEX_STRING: + // The Videotex character set in CCITT's T.100 and T.101, space, and delete + case self::TYPE_VISIBLE_STRING: + // Printing character sets of international ASCII, and space + case self::TYPE_IA5_STRING: + // International Alphabet 5 (International ASCII) + case self::TYPE_GRAPHIC_STRING: + // All registered G sets, and space + case self::TYPE_GENERAL_STRING: + // All registered C and G sets, space and delete + case self::TYPE_UTF8_STRING: + // ???? + case self::TYPE_BMP_STRING: + if ($constructed) { + return false; + } + $current['content'] = substr($content, $content_pos); + break; + case self::TYPE_UTC_TIME: + case self::TYPE_GENERALIZED_TIME: + if ($constructed) { + return false; + } + $current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag); + break; + default: + return false; + } + + $start+= $length; + + // ie. length is the length of the full TLV encoding - it's not just the length of the value + return $current + array('length' => $start - $current['start']); + } + + /** + * ASN.1 Map + * + * Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format. + * + * "Special" mappings may be applied on a per tag-name basis via $special. + * + * @param array $decoded + * @param array $mapping + * @param array $special + * @return array + * @access public + */ + function asn1map($decoded, $mapping, $special = array()) + { + if (!is_array($decoded)) { + return false; + } + + if (isset($mapping['explicit']) && is_array($decoded['content'])) { + $decoded = $decoded['content'][0]; + } + + switch (true) { + case $mapping['type'] == self::TYPE_ANY: + $intype = $decoded['type']; + if (isset($decoded['constant']) || !isset($this->ANYmap[$intype]) || (ord($this->encoded[$decoded['start']]) & 0x20)) { + return new Element(substr($this->encoded, $decoded['start'], $decoded['length'])); + } + $inmap = $this->ANYmap[$intype]; + if (is_string($inmap)) { + return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping, $special)); + } + break; + case $mapping['type'] == self::TYPE_CHOICE: + foreach ($mapping['children'] as $key => $option) { + switch (true) { + case isset($option['constant']) && $option['constant'] == $decoded['constant']: + case !isset($option['constant']) && $option['type'] == $decoded['type']: + $value = $this->asn1map($decoded, $option, $special); + break; + case !isset($option['constant']) && $option['type'] == self::TYPE_CHOICE: + $v = $this->asn1map($decoded, $option, $special); + if (isset($v)) { + $value = $v; + } + } + if (isset($value)) { + if (isset($special[$key])) { + $value = call_user_func($special[$key], $value); + } + return array($key => $value); + } + } + return null; + case isset($mapping['implicit']): + case isset($mapping['explicit']): + case $decoded['type'] == $mapping['type']: + break; + default: + // if $decoded['type'] and $mapping['type'] are both strings, but different types of strings, + // let it through + switch (true) { + case $decoded['type'] < 18: // self::TYPE_NUMERIC_STRING == 18 + case $decoded['type'] > 30: // self::TYPE_BMP_STRING == 30 + case $mapping['type'] < 18: + case $mapping['type'] > 30: + return null; + } + } + + if (isset($mapping['implicit'])) { + $decoded['type'] = $mapping['type']; + } + + switch ($decoded['type']) { + case self::TYPE_SEQUENCE: + $map = array(); + + // ignore the min and max + if (isset($mapping['min']) && isset($mapping['max'])) { + $child = $mapping['children']; + foreach ($decoded['content'] as $content) { + if (($map[] = $this->asn1map($content, $child, $special)) === null) { + return null; + } + } + + return $map; + } + + $n = count($decoded['content']); + $i = 0; + + foreach ($mapping['children'] as $key => $child) { + $maymatch = $i < $n; // Match only existing input. + if ($maymatch) { + $temp = $decoded['content'][$i]; + + if ($child['type'] != self::TYPE_CHOICE) { + // Get the mapping and input class & constant. + $childClass = $tempClass = self::CLASS_UNIVERSAL; + $constant = null; + if (isset($temp['constant'])) { + $tempClass = $temp['type']; + } + if (isset($child['class'])) { + $childClass = $child['class']; + $constant = $child['cast']; + } elseif (isset($child['constant'])) { + $childClass = self::CLASS_CONTEXT_SPECIFIC; + $constant = $child['constant']; + } + + if (isset($constant) && isset($temp['constant'])) { + // Can only match if constants and class match. + $maymatch = $constant == $temp['constant'] && $childClass == $tempClass; + } else { + // Can only match if no constant expected and type matches or is generic. + $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false; + } + } + } + + if ($maymatch) { + // Attempt submapping. + $candidate = $this->asn1map($temp, $child, $special); + $maymatch = $candidate !== null; + } + + if ($maymatch) { + // Got the match: use it. + if (isset($special[$key])) { + $candidate = call_user_func($special[$key], $candidate); + } + $map[$key] = $candidate; + $i++; + } elseif (isset($child['default'])) { + $map[$key] = $child['default']; // Use default. + } elseif (!isset($child['optional'])) { + return null; // Syntax error. + } + } + + // Fail mapping if all input items have not been consumed. + return $i < $n ? null: $map; + + // the main diff between sets and sequences is the encapsulation of the foreach in another for loop + case self::TYPE_SET: + $map = array(); + + // ignore the min and max + if (isset($mapping['min']) && isset($mapping['max'])) { + $child = $mapping['children']; + foreach ($decoded['content'] as $content) { + if (($map[] = $this->asn1map($content, $child, $special)) === null) { + return null; + } + } + + return $map; + } + + for ($i = 0; $i < count($decoded['content']); $i++) { + $temp = $decoded['content'][$i]; + $tempClass = self::CLASS_UNIVERSAL; + if (isset($temp['constant'])) { + $tempClass = $temp['type']; + } + + foreach ($mapping['children'] as $key => $child) { + if (isset($map[$key])) { + continue; + } + $maymatch = true; + if ($child['type'] != self::TYPE_CHOICE) { + $childClass = self::CLASS_UNIVERSAL; + $constant = null; + if (isset($child['class'])) { + $childClass = $child['class']; + $constant = $child['cast']; + } elseif (isset($child['constant'])) { + $childClass = self::CLASS_CONTEXT_SPECIFIC; + $constant = $child['constant']; + } + + if (isset($constant) && isset($temp['constant'])) { + // Can only match if constants and class match. + $maymatch = $constant == $temp['constant'] && $childClass == $tempClass; + } else { + // Can only match if no constant expected and type matches or is generic. + $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false; + } + } + + if ($maymatch) { + // Attempt submapping. + $candidate = $this->asn1map($temp, $child, $special); + $maymatch = $candidate !== null; + } + + if (!$maymatch) { + break; + } + + // Got the match: use it. + if (isset($special[$key])) { + $candidate = call_user_func($special[$key], $candidate); + } + $map[$key] = $candidate; + break; + } + } + + foreach ($mapping['children'] as $key => $child) { + if (!isset($map[$key])) { + if (isset($child['default'])) { + $map[$key] = $child['default']; + } elseif (!isset($child['optional'])) { + return null; + } + } + } + return $map; + case self::TYPE_OBJECT_IDENTIFIER: + return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content']; + case self::TYPE_UTC_TIME: + case self::TYPE_GENERALIZED_TIME: + // for explicitly tagged optional stuff + if (is_array($decoded['content'])) { + $decoded['content'] = $decoded['content'][0]['content']; + } + // for implicitly tagged optional stuff + // in theory, doing isset($mapping['implicit']) would work but malformed certs do exist + // in the wild that OpenSSL decodes without issue so we'll support them as well + if (!is_object($decoded['content'])) { + $decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']); + } + return $decoded['content'] ? $decoded['content']->format($this->format) : false; + case self::TYPE_BIT_STRING: + if (isset($mapping['mapping'])) { + $offset = ord($decoded['content'][0]); + $size = (strlen($decoded['content']) - 1) * 8 - $offset; + /* + From X.680-0207.pdf#page=46 (21.7): + + "When a "NamedBitList" is used in defining a bitstring type ASN.1 encoding rules are free to add (or remove) + arbitrarily any trailing 0 bits to (or from) values that are being encoded or decoded. Application designers should + therefore ensure that different semantics are not associated with such values which differ only in the number of trailing + 0 bits." + */ + $bits = count($mapping['mapping']) == $size ? array() : array_fill(0, count($mapping['mapping']) - $size, false); + for ($i = strlen($decoded['content']) - 1; $i > 0; $i--) { + $current = ord($decoded['content'][$i]); + for ($j = $offset; $j < 8; $j++) { + $bits[] = (bool) ($current & (1 << $j)); + } + $offset = 0; + } + $values = array(); + $map = array_reverse($mapping['mapping']); + foreach ($map as $i => $value) { + if ($bits[$i]) { + $values[] = $value; + } + } + return $values; + } + case self::TYPE_OCTET_STRING: + return base64_encode($decoded['content']); + case self::TYPE_NULL: + return ''; + case self::TYPE_BOOLEAN: + return $decoded['content']; + case self::TYPE_NUMERIC_STRING: + case self::TYPE_PRINTABLE_STRING: + case self::TYPE_TELETEX_STRING: + case self::TYPE_VIDEOTEX_STRING: + case self::TYPE_IA5_STRING: + case self::TYPE_GRAPHIC_STRING: + case self::TYPE_VISIBLE_STRING: + case self::TYPE_GENERAL_STRING: + case self::TYPE_UNIVERSAL_STRING: + case self::TYPE_UTF8_STRING: + case self::TYPE_BMP_STRING: + return $decoded['content']; + case self::TYPE_INTEGER: + case self::TYPE_ENUMERATED: + $temp = $decoded['content']; + if (isset($mapping['implicit'])) { + $temp = new BigInteger($decoded['content'], -256); + } + if (isset($mapping['mapping'])) { + $temp = (int) $temp->toString(); + return isset($mapping['mapping'][$temp]) ? + $mapping['mapping'][$temp] : + false; + } + return $temp; + } + } + + /** + * ASN.1 Encode + * + * DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries would probably call this function + * an ASN.1 compiler. + * + * "Special" mappings can be applied via $special. + * + * @param string $source + * @param string $mapping + * @param array $special + * @return string + * @access public + */ + function encodeDER($source, $mapping, $special = array()) + { + $this->location = array(); + return $this->_encode_der($source, $mapping, null, $special); + } + + /** + * ASN.1 Encode (Helper function) + * + * @param string $source + * @param string $mapping + * @param int $idx + * @param array $special + * @return string + * @access private + */ + function _encode_der($source, $mapping, $idx = null, $special = array()) + { + if ($source instanceof Element) { + return $source->element; + } + + // do not encode (implicitly optional) fields with value set to default + if (isset($mapping['default']) && $source === $mapping['default']) { + return ''; + } + + if (isset($idx)) { + if (isset($special[$idx])) { + $source = call_user_func($special[$idx], $source); + } + $this->location[] = $idx; + } + + $tag = $mapping['type']; + + switch ($tag) { + case self::TYPE_SET: // Children order is not important, thus process in sequence. + case self::TYPE_SEQUENCE: + $tag|= 0x20; // set the constructed bit + + // ignore the min and max + if (isset($mapping['min']) && isset($mapping['max'])) { + $value = array(); + $child = $mapping['children']; + + foreach ($source as $content) { + $temp = $this->_encode_der($content, $child, null, $special); + if ($temp === false) { + return false; + } + $value[]= $temp; + } + /* "The encodings of the component values of a set-of value shall appear in ascending order, the encodings being compared + as octet strings with the shorter components being padded at their trailing end with 0-octets. + NOTE - The padding octets are for comparison purposes only and do not appear in the encodings." + + -- sec 11.6 of http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf */ + if ($mapping['type'] == self::TYPE_SET) { + sort($value); + } + $value = implode('', $value); + break; + } + + $value = ''; + foreach ($mapping['children'] as $key => $child) { + if (!array_key_exists($key, $source)) { + if (!isset($child['optional'])) { + return false; + } + continue; + } + + $temp = $this->_encode_der($source[$key], $child, $key, $special); + if ($temp === false) { + return false; + } + + // An empty child encoding means it has been optimized out. + // Else we should have at least one tag byte. + if ($temp === '') { + continue; + } + + // if isset($child['constant']) is true then isset($child['optional']) should be true as well + if (isset($child['constant'])) { + /* + From X.680-0207.pdf#page=58 (30.6): + + "The tagging construction specifies explicit tagging if any of the following holds: + ... + c) the "Tag Type" alternative is used and the value of "TagDefault" for the module is IMPLICIT TAGS or + AUTOMATIC TAGS, but the type defined by "Type" is an untagged choice type, an untagged open type, or + an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)." + */ + if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) { + $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']); + $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp; + } else { + $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); + $temp = $subtag . substr($temp, 1); + } + } + $value.= $temp; + } + break; + case self::TYPE_CHOICE: + $temp = false; + + foreach ($mapping['children'] as $key => $child) { + if (!isset($source[$key])) { + continue; + } + + $temp = $this->_encode_der($source[$key], $child, $key, $special); + if ($temp === false) { + return false; + } + + // An empty child encoding means it has been optimized out. + // Else we should have at least one tag byte. + if ($temp === '') { + continue; + } + + $tag = ord($temp[0]); + + // if isset($child['constant']) is true then isset($child['optional']) should be true as well + if (isset($child['constant'])) { + if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) { + $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']); + $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp; + } else { + $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); + $temp = $subtag . substr($temp, 1); + } + } + } + + if (isset($idx)) { + array_pop($this->location); + } + + if ($temp && isset($mapping['cast'])) { + $temp[0] = chr(($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']); + } + + return $temp; + case self::TYPE_INTEGER: + case self::TYPE_ENUMERATED: + if (!isset($mapping['mapping'])) { + if (is_numeric($source)) { + $source = new BigInteger($source); + } + $value = $source->toBytes(true); + } else { + $value = array_search($source, $mapping['mapping']); + if ($value === false) { + return false; + } + $value = new BigInteger($value); + $value = $value->toBytes(true); + } + if (!strlen($value)) { + $value = chr(0); + } + break; + case self::TYPE_UTC_TIME: + case self::TYPE_GENERALIZED_TIME: + $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y'; + $format.= 'mdHis'; + // if $source does _not_ include timezone information within it then assume that the timezone is GMT + $date = new DateTime($source, new DateTimeZone('GMT')); + // if $source _does_ include timezone information within it then convert the time to GMT + $date->setTimezone(new DateTimeZone('GMT')); + $value = $date->format($format) . 'Z'; + break; + case self::TYPE_BIT_STRING: + if (isset($mapping['mapping'])) { + $bits = array_fill(0, count($mapping['mapping']), 0); + $size = 0; + for ($i = 0; $i < count($mapping['mapping']); $i++) { + if (in_array($mapping['mapping'][$i], $source)) { + $bits[$i] = 1; + $size = $i; + } + } + + if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) { + $size = $mapping['min'] - 1; + } + + $offset = 8 - (($size + 1) & 7); + $offset = $offset !== 8 ? $offset : 0; + + $value = chr($offset); + + for ($i = $size + 1; $i < count($mapping['mapping']); $i++) { + unset($bits[$i]); + } + + $bits = implode('', array_pad($bits, $size + $offset + 1, 0)); + $bytes = explode(' ', rtrim(chunk_split($bits, 8, ' '))); + foreach ($bytes as $byte) { + $value.= chr(bindec($byte)); + } + + break; + } + case self::TYPE_OCTET_STRING: + /* The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit, + the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven. + + -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */ + $value = base64_decode($source); + break; + case self::TYPE_OBJECT_IDENTIFIER: + $value = $this->_encodeOID($source); + break; + case self::TYPE_ANY: + $loc = $this->location; + if (isset($idx)) { + array_pop($this->location); + } + + switch (true) { + case !isset($source): + return $this->_encode_der(null, array('type' => self::TYPE_NULL) + $mapping, null, $special); + case is_int($source): + case $source instanceof BigInteger: + return $this->_encode_der($source, array('type' => self::TYPE_INTEGER) + $mapping, null, $special); + case is_float($source): + return $this->_encode_der($source, array('type' => self::TYPE_REAL) + $mapping, null, $special); + case is_bool($source): + return $this->_encode_der($source, array('type' => self::TYPE_BOOLEAN) + $mapping, null, $special); + case is_array($source) && count($source) == 1: + $typename = implode('', array_keys($source)); + $outtype = array_search($typename, $this->ANYmap, true); + if ($outtype !== false) { + return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping, null, $special); + } + } + + $filters = $this->filters; + foreach ($loc as $part) { + if (!isset($filters[$part])) { + $filters = false; + break; + } + $filters = $filters[$part]; + } + if ($filters === false) { + user_error('No filters defined for ' . implode('/', $loc)); + return false; + } + return $this->_encode_der($source, $filters + $mapping, null, $special); + case self::TYPE_NULL: + $value = ''; + break; + case self::TYPE_NUMERIC_STRING: + case self::TYPE_TELETEX_STRING: + case self::TYPE_PRINTABLE_STRING: + case self::TYPE_UNIVERSAL_STRING: + case self::TYPE_UTF8_STRING: + case self::TYPE_BMP_STRING: + case self::TYPE_IA5_STRING: + case self::TYPE_VISIBLE_STRING: + case self::TYPE_VIDEOTEX_STRING: + case self::TYPE_GRAPHIC_STRING: + case self::TYPE_GENERAL_STRING: + $value = $source; + break; + case self::TYPE_BOOLEAN: + $value = $source ? "\xFF" : "\x00"; + break; + default: + user_error('Mapping provides no type definition for ' . implode('/', $this->location)); + return false; + } + + if (isset($idx)) { + array_pop($this->location); + } + + if (isset($mapping['cast'])) { + if (isset($mapping['explicit']) || $mapping['type'] == self::TYPE_CHOICE) { + $value = chr($tag) . $this->_encodeLength(strlen($value)) . $value; + $tag = ($mapping['class'] << 6) | 0x20 | $mapping['cast']; + } else { + $tag = ($mapping['class'] << 6) | (ord($temp[0]) & 0x20) | $mapping['cast']; + } + } + + return chr($tag) . $this->_encodeLength(strlen($value)) . $value; + } + + /** + * DER-encode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. + * + * @access private + * @param int $length + * @return string + */ + function _encodeLength($length) + { + if ($length <= 0x7F) { + return chr($length); + } + + $temp = ltrim(pack('N', $length), chr(0)); + return pack('Ca*', 0x80 | strlen($temp), $temp); + } + + /** + * BER-decode the OID + * + * Called by _decode_ber() + * + * @access private + * @param string $content + * @return string + */ + function _decodeOID($content) + { + static $eighty; + if (!$eighty) { + $eighty = new BigInteger(80); + } + + $oid = array(); + $pos = 0; + $len = strlen($content); + + if (ord($content[$len - 1]) & 0x80) { + return false; + } + + $n = new BigInteger(); + while ($pos < $len) { + $temp = ord($content[$pos++]); + $n = $n->bitwise_leftShift(7); + $n = $n->bitwise_or(new BigInteger($temp & 0x7F)); + if (~$temp & 0x80) { + $oid[] = $n; + $n = new BigInteger(); + } + } + $part1 = array_shift($oid); + $first = floor(ord($content[0]) / 40); + /* + "This packing of the first two object identifier components recognizes that only three values are allocated from the root + node, and at most 39 subsequent values from nodes reached by X = 0 and X = 1." + + -- https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=22 + */ + if ($first <= 2) { // ie. 0 <= ord($content[0]) < 120 (0x78) + array_unshift($oid, ord($content[0]) % 40); + array_unshift($oid, $first); + } else { + array_unshift($oid, $part1->subtract($eighty)); + array_unshift($oid, 2); + } + + return implode('.', $oid); + } + + /** + * DER-encode the OID + * + * Called by _encode_der() + * + * @access private + * @param string $source + * @return string + */ + function _encodeOID($source) + { + static $mask, $zero, $forty; + if (!$mask) { + $mask = new BigInteger(0x7F); + $zero = new BigInteger(); + $forty = new BigInteger(40); + } + + $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids); + if ($oid === false) { + user_error('Invalid OID'); + return false; + } + $parts = explode('.', $oid); + $part1 = array_shift($parts); + $part2 = array_shift($parts); + + $first = new BigInteger($part1); + $first = $first->multiply($forty); + $first = $first->add(new BigInteger($part2)); + + array_unshift($parts, $first->toString()); + + $value = ''; + foreach ($parts as $part) { + if (!$part) { + $temp = "\0"; + } else { + $temp = ''; + $part = new BigInteger($part); + while (!$part->equals($zero)) { + $submask = $part->bitwise_and($mask); + $submask->setPrecision(8); + $temp = (chr(0x80) | $submask->toBytes()) . $temp; + $part = $part->bitwise_rightShift(7); + } + $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F); + } + $value.= $temp; + } + + return $value; + } + + /** + * BER-decode the time + * + * Called by _decode_ber() and in the case of implicit tags asn1map(). + * + * @access private + * @param string $content + * @param int $tag + * @return string + */ + function _decodeTime($content, $tag) + { + /* UTCTime: + http://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 + http://www.obj-sys.com/asn1tutorial/node15.html + + GeneralizedTime: + http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2 + http://www.obj-sys.com/asn1tutorial/node14.html */ + + $format = 'YmdHis'; + + if ($tag == self::TYPE_UTC_TIME) { + // https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=28 says "the seconds + // element shall always be present" but none-the-less I've seen X509 certs where it isn't and if the + // browsers parse it phpseclib ought to too + if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content, $matches)) { + $content = $matches[1] . '00' . $matches[2]; + } + $prefix = substr($content, 0, 2) >= 50 ? '19' : '20'; + $content = $prefix . $content; + } elseif (strpos($content, '.') !== false) { + $format.= '.u'; + } + + if ($content[strlen($content) - 1] == 'Z') { + $content = substr($content, 0, -1) . '+0000'; + } + + if (strpos($content, '-') !== false || strpos($content, '+') !== false) { + $format.= 'O'; + } + + // error supression isn't necessary as of PHP 7.0: + // http://php.net/manual/en/migration70.other-changes.php + return @DateTime::createFromFormat($format, $content); + } + + /** + * Set the time format + * + * Sets the time / date format for asn1map(). + * + * @access public + * @param string $format + */ + function setTimeFormat($format) + { + $this->format = $format; + } + + /** + * Load OIDs + * + * Load the relevant OIDs for a particular ASN.1 semantic mapping. + * + * @access public + * @param array $oids + */ + function loadOIDs($oids) + { + $this->oids = $oids; + } + + /** + * Load filters + * + * See \phpseclib\File\X509, etc, for an example. + * + * @access public + * @param array $filters + */ + function loadFilters($filters) + { + $this->filters = $filters; + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * String type conversion + * + * This is a lazy conversion, dealing only with character size. + * No real conversion table is used. + * + * @param string $in + * @param int $from + * @param int $to + * @return string + * @access public + */ + function convert($in, $from = self::TYPE_UTF8_STRING, $to = self::TYPE_UTF8_STRING) + { + if (!isset($this->stringTypeSize[$from]) || !isset($this->stringTypeSize[$to])) { + return false; + } + $insize = $this->stringTypeSize[$from]; + $outsize = $this->stringTypeSize[$to]; + $inlength = strlen($in); + $out = ''; + + for ($i = 0; $i < $inlength;) { + if ($inlength - $i < $insize) { + return false; + } + + // Get an input character as a 32-bit value. + $c = ord($in[$i++]); + switch (true) { + case $insize == 4: + $c = ($c << 8) | ord($in[$i++]); + $c = ($c << 8) | ord($in[$i++]); + case $insize == 2: + $c = ($c << 8) | ord($in[$i++]); + case $insize == 1: + break; + case ($c & 0x80) == 0x00: + break; + case ($c & 0x40) == 0x00: + return false; + default: + $bit = 6; + do { + if ($bit > 25 || $i >= $inlength || (ord($in[$i]) & 0xC0) != 0x80) { + return false; + } + $c = ($c << 6) | (ord($in[$i++]) & 0x3F); + $bit += 5; + $mask = 1 << $bit; + } while ($c & $bit); + $c &= $mask - 1; + break; + } + + // Convert and append the character to output string. + $v = ''; + switch (true) { + case $outsize == 4: + $v .= chr($c & 0xFF); + $c >>= 8; + $v .= chr($c & 0xFF); + $c >>= 8; + case $outsize == 2: + $v .= chr($c & 0xFF); + $c >>= 8; + case $outsize == 1: + $v .= chr($c & 0xFF); + $c >>= 8; + if ($c) { + return false; + } + break; + case ($c & 0x80000000) != 0: + return false; + case $c >= 0x04000000: + $v .= chr(0x80 | ($c & 0x3F)); + $c = ($c >> 6) | 0x04000000; + case $c >= 0x00200000: + $v .= chr(0x80 | ($c & 0x3F)); + $c = ($c >> 6) | 0x00200000; + case $c >= 0x00010000: + $v .= chr(0x80 | ($c & 0x3F)); + $c = ($c >> 6) | 0x00010000; + case $c >= 0x00000800: + $v .= chr(0x80 | ($c & 0x3F)); + $c = ($c >> 6) | 0x00000800; + case $c >= 0x00000080: + $v .= chr(0x80 | ($c & 0x3F)); + $c = ($c >> 6) | 0x000000C0; + default: + $v .= chr($c); + break; + } + $out .= strrev($v); + } + return $out; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php b/msd/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php new file mode 100644 index 0000000..3b97440 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php @@ -0,0 +1,47 @@ + + * @copyright 2012 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1; + +/** + * ASN.1 Element + * + * Bypass normal encoding rules in phpseclib\File\ASN1::encodeDER() + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +class Element +{ + /** + * Raw element value + * + * @var string + * @access private + */ + var $element; + + /** + * Constructor + * + * @param string $encoded + * @return \phpseclib\File\ASN1\Element + * @access public + */ + function __construct($encoded) + { + $this->element = $encoded; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/File/X509.php b/msd/vendor/phpseclib/phpseclib/phpseclib/File/X509.php new file mode 100644 index 0000000..9bb5474 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/File/X509.php @@ -0,0 +1,5102 @@ + + * @copyright 2012 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File; + +use phpseclib\Crypt\Hash; +use phpseclib\Crypt\Random; +use phpseclib\Crypt\RSA; +use phpseclib\File\ASN1\Element; +use phpseclib\Math\BigInteger; +use DateTime; +use DateTimeZone; + +/** + * Pure-PHP X.509 Parser + * + * @package X509 + * @author Jim Wigginton + * @access public + */ +class X509 +{ + /** + * Flag to only accept signatures signed by certificate authorities + * + * Not really used anymore but retained all the same to suppress E_NOTICEs from old installs + * + * @access public + */ + const VALIDATE_SIGNATURE_BY_CA = 1; + + /**#@+ + * @access public + * @see \phpseclib\File\X509::getDN() + */ + /** + * Return internal array representation + */ + const DN_ARRAY = 0; + /** + * Return string + */ + const DN_STRING = 1; + /** + * Return ASN.1 name string + */ + const DN_ASN1 = 2; + /** + * Return OpenSSL compatible array + */ + const DN_OPENSSL = 3; + /** + * Return canonical ASN.1 RDNs string + */ + const DN_CANON = 4; + /** + * Return name hash for file indexing + */ + const DN_HASH = 5; + /**#@-*/ + + /**#@+ + * @access public + * @see \phpseclib\File\X509::saveX509() + * @see \phpseclib\File\X509::saveCSR() + * @see \phpseclib\File\X509::saveCRL() + */ + /** + * Save as PEM + * + * ie. a base64-encoded PEM with a header and a footer + */ + const FORMAT_PEM = 0; + /** + * Save as DER + */ + const FORMAT_DER = 1; + /** + * Save as a SPKAC + * + * Only works on CSRs. Not currently supported. + */ + const FORMAT_SPKAC = 2; + /** + * Auto-detect the format + * + * Used only by the load*() functions + */ + const FORMAT_AUTO_DETECT = 3; + /**#@-*/ + + /** + * Attribute value disposition. + * If disposition is >= 0, this is the index of the target value. + */ + const ATTR_ALL = -1; // All attribute values (array). + const ATTR_APPEND = -2; // Add a value. + const ATTR_REPLACE = -3; // Clear first, then add a value. + + /** + * ASN.1 syntax for X.509 certificates + * + * @var array + * @access private + */ + var $Certificate; + + /**#@+ + * ASN.1 syntax for various extensions + * + * @access private + */ + var $DirectoryString; + var $PKCS9String; + var $AttributeValue; + var $Extensions; + var $KeyUsage; + var $ExtKeyUsageSyntax; + var $BasicConstraints; + var $KeyIdentifier; + var $CRLDistributionPoints; + var $AuthorityKeyIdentifier; + var $CertificatePolicies; + var $AuthorityInfoAccessSyntax; + var $SubjectInfoAccessSyntax; + var $SubjectAltName; + var $SubjectDirectoryAttributes; + var $PrivateKeyUsagePeriod; + var $IssuerAltName; + var $PolicyMappings; + var $NameConstraints; + + var $CPSuri; + var $UserNotice; + + var $netscape_cert_type; + var $netscape_comment; + var $netscape_ca_policy_url; + + var $Name; + var $RelativeDistinguishedName; + var $CRLNumber; + var $CRLReason; + var $IssuingDistributionPoint; + var $InvalidityDate; + var $CertificateIssuer; + var $HoldInstructionCode; + var $SignedPublicKeyAndChallenge; + /**#@-*/ + + /**#@+ + * ASN.1 syntax for various DN attributes + * + * @access private + */ + var $PostalAddress; + /**#@-*/ + + /** + * ASN.1 syntax for Certificate Signing Requests (RFC2986) + * + * @var array + * @access private + */ + var $CertificationRequest; + + /** + * ASN.1 syntax for Certificate Revocation Lists (RFC5280) + * + * @var array + * @access private + */ + var $CertificateList; + + /** + * Distinguished Name + * + * @var array + * @access private + */ + var $dn; + + /** + * Public key + * + * @var string + * @access private + */ + var $publicKey; + + /** + * Private key + * + * @var string + * @access private + */ + var $privateKey; + + /** + * Object identifiers for X.509 certificates + * + * @var array + * @access private + * @link http://en.wikipedia.org/wiki/Object_identifier + */ + var $oids; + + /** + * The certificate authorities + * + * @var array + * @access private + */ + var $CAs; + + /** + * The currently loaded certificate + * + * @var array + * @access private + */ + var $currentCert; + + /** + * The signature subject + * + * There's no guarantee \phpseclib\File\X509 is going to re-encode an X.509 cert in the same way it was originally + * encoded so we take save the portion of the original cert that the signature would have made for. + * + * @var string + * @access private + */ + var $signatureSubject; + + /** + * Certificate Start Date + * + * @var string + * @access private + */ + var $startDate; + + /** + * Certificate End Date + * + * @var string + * @access private + */ + var $endDate; + + /** + * Serial Number + * + * @var string + * @access private + */ + var $serialNumber; + + /** + * Key Identifier + * + * See {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.1 RFC5280#section-4.2.1.1} and + * {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2 RFC5280#section-4.2.1.2}. + * + * @var string + * @access private + */ + var $currentKeyIdentifier; + + /** + * CA Flag + * + * @var bool + * @access private + */ + var $caFlag = false; + + /** + * SPKAC Challenge + * + * @var string + * @access private + */ + var $challenge; + + /** + * Recursion Limit + * + * @var int + * @access private + */ + static $recur_limit = 5; + + /** + * URL fetch flag + * + * @var bool + * @access private + */ + static $disable_url_fetch = false; + + /** + * Default Constructor. + * + * @return \phpseclib\File\X509 + * @access public + */ + function __construct() + { + // Explicitly Tagged Module, 1988 Syntax + // http://tools.ietf.org/html/rfc5280#appendix-A.1 + + $this->DirectoryString = array( + 'type' => ASN1::TYPE_CHOICE, + 'children' => array( + 'teletexString' => array('type' => ASN1::TYPE_TELETEX_STRING), + 'printableString' => array('type' => ASN1::TYPE_PRINTABLE_STRING), + 'universalString' => array('type' => ASN1::TYPE_UNIVERSAL_STRING), + 'utf8String' => array('type' => ASN1::TYPE_UTF8_STRING), + 'bmpString' => array('type' => ASN1::TYPE_BMP_STRING) + ) + ); + + $this->PKCS9String = array( + 'type' => ASN1::TYPE_CHOICE, + 'children' => array( + 'ia5String' => array('type' => ASN1::TYPE_IA5_STRING), + 'directoryString' => $this->DirectoryString + ) + ); + + $this->AttributeValue = array('type' => ASN1::TYPE_ANY); + + $AttributeType = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); + + $AttributeTypeAndValue = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'type' => $AttributeType, + 'value'=> $this->AttributeValue + ) + ); + + /* + In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare, + but they can be useful at times when either there is no unique attribute in the entry or you + want to ensure that the entry's DN contains some useful identifying information. + + - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName + */ + $this->RelativeDistinguishedName = array( + 'type' => ASN1::TYPE_SET, + 'min' => 1, + 'max' => -1, + 'children' => $AttributeTypeAndValue + ); + + // http://tools.ietf.org/html/rfc5280#section-4.1.2.4 + $RDNSequence = array( + 'type' => ASN1::TYPE_SEQUENCE, + // RDNSequence does not define a min or a max, which means it doesn't have one + 'min' => 0, + 'max' => -1, + 'children' => $this->RelativeDistinguishedName + ); + + $this->Name = array( + 'type' => ASN1::TYPE_CHOICE, + 'children' => array( + 'rdnSequence' => $RDNSequence + ) + ); + + // http://tools.ietf.org/html/rfc5280#section-4.1.1.2 + $AlgorithmIdentifier = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'algorithm' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), + 'parameters' => array( + 'type' => ASN1::TYPE_ANY, + 'optional' => true + ) + ) + ); + + /* + A certificate using system MUST reject the certificate if it encounters + a critical extension it does not recognize; however, a non-critical + extension may be ignored if it is not recognized. + + http://tools.ietf.org/html/rfc5280#section-4.2 + */ + $Extension = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'extnId' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), + 'critical' => array( + 'type' => ASN1::TYPE_BOOLEAN, + 'optional' => true, + 'default' => false + ), + 'extnValue' => array('type' => ASN1::TYPE_OCTET_STRING) + ) + ); + + $this->Extensions = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + // technically, it's MAX, but we'll assume anything < 0 is MAX + 'max' => -1, + // if 'children' isn't an array then 'min' and 'max' must be defined + 'children' => $Extension + ); + + $SubjectPublicKeyInfo = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'algorithm' => $AlgorithmIdentifier, + 'subjectPublicKey' => array('type' => ASN1::TYPE_BIT_STRING) + ) + ); + + $UniqueIdentifier = array('type' => ASN1::TYPE_BIT_STRING); + + $Time = array( + 'type' => ASN1::TYPE_CHOICE, + 'children' => array( + 'utcTime' => array('type' => ASN1::TYPE_UTC_TIME), + 'generalTime' => array('type' => ASN1::TYPE_GENERALIZED_TIME) + ) + ); + + // http://tools.ietf.org/html/rfc5280#section-4.1.2.5 + $Validity = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'notBefore' => $Time, + 'notAfter' => $Time + ) + ); + + $CertificateSerialNumber = array('type' => ASN1::TYPE_INTEGER); + + $Version = array( + 'type' => ASN1::TYPE_INTEGER, + 'mapping' => array('v1', 'v2', 'v3') + ); + + // assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm']) + $TBSCertificate = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + // technically, default implies optional, but we'll define it as being optional, none-the-less, just to + // reenforce that fact + 'version' => array( + 'constant' => 0, + 'optional' => true, + 'explicit' => true, + 'default' => 'v1' + ) + $Version, + 'serialNumber' => $CertificateSerialNumber, + 'signature' => $AlgorithmIdentifier, + 'issuer' => $this->Name, + 'validity' => $Validity, + 'subject' => $this->Name, + 'subjectPublicKeyInfo' => $SubjectPublicKeyInfo, + // implicit means that the T in the TLV structure is to be rewritten, regardless of the type + 'issuerUniqueID' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ) + $UniqueIdentifier, + 'subjectUniqueID' => array( + 'constant' => 2, + 'optional' => true, + 'implicit' => true + ) + $UniqueIdentifier, + // doesn't use the EXPLICIT keyword but if + // it's not IMPLICIT, it's EXPLICIT + 'extensions' => array( + 'constant' => 3, + 'optional' => true, + 'explicit' => true + ) + $this->Extensions + ) + ); + + $this->Certificate = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'tbsCertificate' => $TBSCertificate, + 'signatureAlgorithm' => $AlgorithmIdentifier, + 'signature' => array('type' => ASN1::TYPE_BIT_STRING) + ) + ); + + $this->KeyUsage = array( + 'type' => ASN1::TYPE_BIT_STRING, + 'mapping' => array( + 'digitalSignature', + 'nonRepudiation', + 'keyEncipherment', + 'dataEncipherment', + 'keyAgreement', + 'keyCertSign', + 'cRLSign', + 'encipherOnly', + 'decipherOnly' + ) + ); + + $this->BasicConstraints = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'cA' => array( + 'type' => ASN1::TYPE_BOOLEAN, + 'optional' => true, + 'default' => false + ), + 'pathLenConstraint' => array( + 'type' => ASN1::TYPE_INTEGER, + 'optional' => true + ) + ) + ); + + $this->KeyIdentifier = array('type' => ASN1::TYPE_OCTET_STRING); + + $OrganizationalUnitNames = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => 4, // ub-organizational-units + 'children' => array('type' => ASN1::TYPE_PRINTABLE_STRING) + ); + + $PersonalName = array( + 'type' => ASN1::TYPE_SET, + 'children' => array( + 'surname' => array( + 'type' => ASN1::TYPE_PRINTABLE_STRING, + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ), + 'given-name' => array( + 'type' => ASN1::TYPE_PRINTABLE_STRING, + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ), + 'initials' => array( + 'type' => ASN1::TYPE_PRINTABLE_STRING, + 'constant' => 2, + 'optional' => true, + 'implicit' => true + ), + 'generation-qualifier' => array( + 'type' => ASN1::TYPE_PRINTABLE_STRING, + 'constant' => 3, + 'optional' => true, + 'implicit' => true + ) + ) + ); + + $NumericUserIdentifier = array('type' => ASN1::TYPE_NUMERIC_STRING); + + $OrganizationName = array('type' => ASN1::TYPE_PRINTABLE_STRING); + + $PrivateDomainName = array( + 'type' => ASN1::TYPE_CHOICE, + 'children' => array( + 'numeric' => array('type' => ASN1::TYPE_NUMERIC_STRING), + 'printable' => array('type' => ASN1::TYPE_PRINTABLE_STRING) + ) + ); + + $TerminalIdentifier = array('type' => ASN1::TYPE_PRINTABLE_STRING); + + $NetworkAddress = array('type' => ASN1::TYPE_NUMERIC_STRING); + + $AdministrationDomainName = array( + 'type' => ASN1::TYPE_CHOICE, + // if class isn't present it's assumed to be \phpseclib\File\ASN1::CLASS_UNIVERSAL or + // (if constant is present) \phpseclib\File\ASN1::CLASS_CONTEXT_SPECIFIC + 'class' => ASN1::CLASS_APPLICATION, + 'cast' => 2, + 'children' => array( + 'numeric' => array('type' => ASN1::TYPE_NUMERIC_STRING), + 'printable' => array('type' => ASN1::TYPE_PRINTABLE_STRING) + ) + ); + + $CountryName = array( + 'type' => ASN1::TYPE_CHOICE, + // if class isn't present it's assumed to be \phpseclib\File\ASN1::CLASS_UNIVERSAL or + // (if constant is present) \phpseclib\File\ASN1::CLASS_CONTEXT_SPECIFIC + 'class' => ASN1::CLASS_APPLICATION, + 'cast' => 1, + 'children' => array( + 'x121-dcc-code' => array('type' => ASN1::TYPE_NUMERIC_STRING), + 'iso-3166-alpha2-code' => array('type' => ASN1::TYPE_PRINTABLE_STRING) + ) + ); + + $AnotherName = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'type-id' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), + 'value' => array( + 'type' => ASN1::TYPE_ANY, + 'constant' => 0, + 'optional' => true, + 'explicit' => true + ) + ) + ); + + $ExtensionAttribute = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'extension-attribute-type' => array( + 'type' => ASN1::TYPE_PRINTABLE_STRING, + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ), + 'extension-attribute-value' => array( + 'type' => ASN1::TYPE_ANY, + 'constant' => 1, + 'optional' => true, + 'explicit' => true + ) + ) + ); + + $ExtensionAttributes = array( + 'type' => ASN1::TYPE_SET, + 'min' => 1, + 'max' => 256, // ub-extension-attributes + 'children' => $ExtensionAttribute + ); + + $BuiltInDomainDefinedAttribute = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'type' => array('type' => ASN1::TYPE_PRINTABLE_STRING), + 'value' => array('type' => ASN1::TYPE_PRINTABLE_STRING) + ) + ); + + $BuiltInDomainDefinedAttributes = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => 4, // ub-domain-defined-attributes + 'children' => $BuiltInDomainDefinedAttribute + ); + + $BuiltInStandardAttributes = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'country-name' => array('optional' => true) + $CountryName, + 'administration-domain-name' => array('optional' => true) + $AdministrationDomainName, + 'network-address' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ) + $NetworkAddress, + 'terminal-identifier' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ) + $TerminalIdentifier, + 'private-domain-name' => array( + 'constant' => 2, + 'optional' => true, + 'explicit' => true + ) + $PrivateDomainName, + 'organization-name' => array( + 'constant' => 3, + 'optional' => true, + 'implicit' => true + ) + $OrganizationName, + 'numeric-user-identifier' => array( + 'constant' => 4, + 'optional' => true, + 'implicit' => true + ) + $NumericUserIdentifier, + 'personal-name' => array( + 'constant' => 5, + 'optional' => true, + 'implicit' => true + ) + $PersonalName, + 'organizational-unit-names' => array( + 'constant' => 6, + 'optional' => true, + 'implicit' => true + ) + $OrganizationalUnitNames + ) + ); + + $ORAddress = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'built-in-standard-attributes' => $BuiltInStandardAttributes, + 'built-in-domain-defined-attributes' => array('optional' => true) + $BuiltInDomainDefinedAttributes, + 'extension-attributes' => array('optional' => true) + $ExtensionAttributes + ) + ); + + $EDIPartyName = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'nameAssigner' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ) + $this->DirectoryString, + // partyName is technically required but \phpseclib\File\ASN1 doesn't currently support non-optional constants and + // setting it to optional gets the job done in any event. + 'partyName' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ) + $this->DirectoryString + ) + ); + + $GeneralName = array( + 'type' => ASN1::TYPE_CHOICE, + 'children' => array( + 'otherName' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ) + $AnotherName, + 'rfc822Name' => array( + 'type' => ASN1::TYPE_IA5_STRING, + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ), + 'dNSName' => array( + 'type' => ASN1::TYPE_IA5_STRING, + 'constant' => 2, + 'optional' => true, + 'implicit' => true + ), + 'x400Address' => array( + 'constant' => 3, + 'optional' => true, + 'implicit' => true + ) + $ORAddress, + 'directoryName' => array( + 'constant' => 4, + 'optional' => true, + 'explicit' => true + ) + $this->Name, + 'ediPartyName' => array( + 'constant' => 5, + 'optional' => true, + 'implicit' => true + ) + $EDIPartyName, + 'uniformResourceIdentifier' => array( + 'type' => ASN1::TYPE_IA5_STRING, + 'constant' => 6, + 'optional' => true, + 'implicit' => true + ), + 'iPAddress' => array( + 'type' => ASN1::TYPE_OCTET_STRING, + 'constant' => 7, + 'optional' => true, + 'implicit' => true + ), + 'registeredID' => array( + 'type' => ASN1::TYPE_OBJECT_IDENTIFIER, + 'constant' => 8, + 'optional' => true, + 'implicit' => true + ) + ) + ); + + $GeneralNames = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $GeneralName + ); + + $this->IssuerAltName = $GeneralNames; + + $ReasonFlags = array( + 'type' => ASN1::TYPE_BIT_STRING, + 'mapping' => array( + 'unused', + 'keyCompromise', + 'cACompromise', + 'affiliationChanged', + 'superseded', + 'cessationOfOperation', + 'certificateHold', + 'privilegeWithdrawn', + 'aACompromise' + ) + ); + + $DistributionPointName = array( + 'type' => ASN1::TYPE_CHOICE, + 'children' => array( + 'fullName' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ) + $GeneralNames, + 'nameRelativeToCRLIssuer' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ) + $this->RelativeDistinguishedName + ) + ); + + $DistributionPoint = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'distributionPoint' => array( + 'constant' => 0, + 'optional' => true, + 'explicit' => true + ) + $DistributionPointName, + 'reasons' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ) + $ReasonFlags, + 'cRLIssuer' => array( + 'constant' => 2, + 'optional' => true, + 'implicit' => true + ) + $GeneralNames + ) + ); + + $this->CRLDistributionPoints = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $DistributionPoint + ); + + $this->AuthorityKeyIdentifier = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'keyIdentifier' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ) + $this->KeyIdentifier, + 'authorityCertIssuer' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ) + $GeneralNames, + 'authorityCertSerialNumber' => array( + 'constant' => 2, + 'optional' => true, + 'implicit' => true + ) + $CertificateSerialNumber + ) + ); + + $PolicyQualifierId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); + + $PolicyQualifierInfo = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'policyQualifierId' => $PolicyQualifierId, + 'qualifier' => array('type' => ASN1::TYPE_ANY) + ) + ); + + $CertPolicyId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); + + $PolicyInformation = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'policyIdentifier' => $CertPolicyId, + 'policyQualifiers' => array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 0, + 'max' => -1, + 'optional' => true, + 'children' => $PolicyQualifierInfo + ) + ) + ); + + $this->CertificatePolicies = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $PolicyInformation + ); + + $this->PolicyMappings = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'issuerDomainPolicy' => $CertPolicyId, + 'subjectDomainPolicy' => $CertPolicyId + ) + ) + ); + + $KeyPurposeId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); + + $this->ExtKeyUsageSyntax = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $KeyPurposeId + ); + + $AccessDescription = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'accessMethod' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), + 'accessLocation' => $GeneralName + ) + ); + + $this->AuthorityInfoAccessSyntax = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $AccessDescription + ); + + $this->SubjectInfoAccessSyntax = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $AccessDescription + ); + + $this->SubjectAltName = $GeneralNames; + + $this->PrivateKeyUsagePeriod = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'notBefore' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true, + 'type' => ASN1::TYPE_GENERALIZED_TIME), + 'notAfter' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true, + 'type' => ASN1::TYPE_GENERALIZED_TIME) + ) + ); + + $BaseDistance = array('type' => ASN1::TYPE_INTEGER); + + $GeneralSubtree = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'base' => $GeneralName, + 'minimum' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true, + 'default' => new BigInteger(0) + ) + $BaseDistance, + 'maximum' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true, + ) + $BaseDistance + ) + ); + + $GeneralSubtrees = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $GeneralSubtree + ); + + $this->NameConstraints = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'permittedSubtrees' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ) + $GeneralSubtrees, + 'excludedSubtrees' => array( + 'constant' => 1, + 'optional' => true, + 'implicit' => true + ) + $GeneralSubtrees + ) + ); + + $this->CPSuri = array('type' => ASN1::TYPE_IA5_STRING); + + $DisplayText = array( + 'type' => ASN1::TYPE_CHOICE, + 'children' => array( + 'ia5String' => array('type' => ASN1::TYPE_IA5_STRING), + 'visibleString' => array('type' => ASN1::TYPE_VISIBLE_STRING), + 'bmpString' => array('type' => ASN1::TYPE_BMP_STRING), + 'utf8String' => array('type' => ASN1::TYPE_UTF8_STRING) + ) + ); + + $NoticeReference = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'organization' => $DisplayText, + 'noticeNumbers' => array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => 200, + 'children' => array('type' => ASN1::TYPE_INTEGER) + ) + ) + ); + + $this->UserNotice = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'noticeRef' => array( + 'optional' => true, + 'implicit' => true + ) + $NoticeReference, + 'explicitText' => array( + 'optional' => true, + 'implicit' => true + ) + $DisplayText + ) + ); + + // mapping is from + $this->netscape_cert_type = array( + 'type' => ASN1::TYPE_BIT_STRING, + 'mapping' => array( + 'SSLClient', + 'SSLServer', + 'Email', + 'ObjectSigning', + 'Reserved', + 'SSLCA', + 'EmailCA', + 'ObjectSigningCA' + ) + ); + + $this->netscape_comment = array('type' => ASN1::TYPE_IA5_STRING); + $this->netscape_ca_policy_url = array('type' => ASN1::TYPE_IA5_STRING); + + // attribute is used in RFC2986 but we're using the RFC5280 definition + + $Attribute = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'type' => $AttributeType, + 'value'=> array( + 'type' => ASN1::TYPE_SET, + 'min' => 1, + 'max' => -1, + 'children' => $this->AttributeValue + ) + ) + ); + + $this->SubjectDirectoryAttributes = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $Attribute + ); + + // adapted from + + $Attributes = array( + 'type' => ASN1::TYPE_SET, + 'min' => 1, + 'max' => -1, + 'children' => $Attribute + ); + + $CertificationRequestInfo = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'version' => array( + 'type' => ASN1::TYPE_INTEGER, + 'mapping' => array('v1') + ), + 'subject' => $this->Name, + 'subjectPKInfo' => $SubjectPublicKeyInfo, + 'attributes' => array( + 'constant' => 0, + 'optional' => true, + 'implicit' => true + ) + $Attributes, + ) + ); + + $this->CertificationRequest = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'certificationRequestInfo' => $CertificationRequestInfo, + 'signatureAlgorithm' => $AlgorithmIdentifier, + 'signature' => array('type' => ASN1::TYPE_BIT_STRING) + ) + ); + + $RevokedCertificate = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'userCertificate' => $CertificateSerialNumber, + 'revocationDate' => $Time, + 'crlEntryExtensions' => array( + 'optional' => true + ) + $this->Extensions + ) + ); + + $TBSCertList = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'version' => array( + 'optional' => true, + 'default' => 'v1' + ) + $Version, + 'signature' => $AlgorithmIdentifier, + 'issuer' => $this->Name, + 'thisUpdate' => $Time, + 'nextUpdate' => array( + 'optional' => true + ) + $Time, + 'revokedCertificates' => array( + 'type' => ASN1::TYPE_SEQUENCE, + 'optional' => true, + 'min' => 0, + 'max' => -1, + 'children' => $RevokedCertificate + ), + 'crlExtensions' => array( + 'constant' => 0, + 'optional' => true, + 'explicit' => true + ) + $this->Extensions + ) + ); + + $this->CertificateList = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'tbsCertList' => $TBSCertList, + 'signatureAlgorithm' => $AlgorithmIdentifier, + 'signature' => array('type' => ASN1::TYPE_BIT_STRING) + ) + ); + + $this->CRLNumber = array('type' => ASN1::TYPE_INTEGER); + + $this->CRLReason = array('type' => ASN1::TYPE_ENUMERATED, + 'mapping' => array( + 'unspecified', + 'keyCompromise', + 'cACompromise', + 'affiliationChanged', + 'superseded', + 'cessationOfOperation', + 'certificateHold', + // Value 7 is not used. + 8 => 'removeFromCRL', + 'privilegeWithdrawn', + 'aACompromise' + ) + ); + + $this->IssuingDistributionPoint = array('type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'distributionPoint' => array( + 'constant' => 0, + 'optional' => true, + 'explicit' => true + ) + $DistributionPointName, + 'onlyContainsUserCerts' => array( + 'type' => ASN1::TYPE_BOOLEAN, + 'constant' => 1, + 'optional' => true, + 'default' => false, + 'implicit' => true + ), + 'onlyContainsCACerts' => array( + 'type' => ASN1::TYPE_BOOLEAN, + 'constant' => 2, + 'optional' => true, + 'default' => false, + 'implicit' => true + ), + 'onlySomeReasons' => array( + 'constant' => 3, + 'optional' => true, + 'implicit' => true + ) + $ReasonFlags, + 'indirectCRL' => array( + 'type' => ASN1::TYPE_BOOLEAN, + 'constant' => 4, + 'optional' => true, + 'default' => false, + 'implicit' => true + ), + 'onlyContainsAttributeCerts' => array( + 'type' => ASN1::TYPE_BOOLEAN, + 'constant' => 5, + 'optional' => true, + 'default' => false, + 'implicit' => true + ) + ) + ); + + $this->InvalidityDate = array('type' => ASN1::TYPE_GENERALIZED_TIME); + + $this->CertificateIssuer = $GeneralNames; + + $this->HoldInstructionCode = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); + + $PublicKeyAndChallenge = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'spki' => $SubjectPublicKeyInfo, + 'challenge' => array('type' => ASN1::TYPE_IA5_STRING) + ) + ); + + $this->SignedPublicKeyAndChallenge = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'publicKeyAndChallenge' => $PublicKeyAndChallenge, + 'signatureAlgorithm' => $AlgorithmIdentifier, + 'signature' => array('type' => ASN1::TYPE_BIT_STRING) + ) + ); + + $this->PostalAddress = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'optional' => true, + 'min' => 1, + 'max' => -1, + 'children' => $this->DirectoryString + ); + + // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2 + $this->oids = array( + '1.3.6.1.5.5.7' => 'id-pkix', + '1.3.6.1.5.5.7.1' => 'id-pe', + '1.3.6.1.5.5.7.2' => 'id-qt', + '1.3.6.1.5.5.7.3' => 'id-kp', + '1.3.6.1.5.5.7.48' => 'id-ad', + '1.3.6.1.5.5.7.2.1' => 'id-qt-cps', + '1.3.6.1.5.5.7.2.2' => 'id-qt-unotice', + '1.3.6.1.5.5.7.48.1' =>'id-ad-ocsp', + '1.3.6.1.5.5.7.48.2' => 'id-ad-caIssuers', + '1.3.6.1.5.5.7.48.3' => 'id-ad-timeStamping', + '1.3.6.1.5.5.7.48.5' => 'id-ad-caRepository', + '2.5.4' => 'id-at', + '2.5.4.41' => 'id-at-name', + '2.5.4.4' => 'id-at-surname', + '2.5.4.42' => 'id-at-givenName', + '2.5.4.43' => 'id-at-initials', + '2.5.4.44' => 'id-at-generationQualifier', + '2.5.4.3' => 'id-at-commonName', + '2.5.4.7' => 'id-at-localityName', + '2.5.4.8' => 'id-at-stateOrProvinceName', + '2.5.4.10' => 'id-at-organizationName', + '2.5.4.11' => 'id-at-organizationalUnitName', + '2.5.4.12' => 'id-at-title', + '2.5.4.13' => 'id-at-description', + '2.5.4.46' => 'id-at-dnQualifier', + '2.5.4.6' => 'id-at-countryName', + '2.5.4.5' => 'id-at-serialNumber', + '2.5.4.65' => 'id-at-pseudonym', + '2.5.4.17' => 'id-at-postalCode', + '2.5.4.9' => 'id-at-streetAddress', + '2.5.4.45' => 'id-at-uniqueIdentifier', + '2.5.4.72' => 'id-at-role', + '2.5.4.16' => 'id-at-postalAddress', + + '0.9.2342.19200300.100.1.25' => 'id-domainComponent', + '1.2.840.113549.1.9' => 'pkcs-9', + '1.2.840.113549.1.9.1' => 'pkcs-9-at-emailAddress', + '2.5.29' => 'id-ce', + '2.5.29.35' => 'id-ce-authorityKeyIdentifier', + '2.5.29.14' => 'id-ce-subjectKeyIdentifier', + '2.5.29.15' => 'id-ce-keyUsage', + '2.5.29.16' => 'id-ce-privateKeyUsagePeriod', + '2.5.29.32' => 'id-ce-certificatePolicies', + '2.5.29.32.0' => 'anyPolicy', + + '2.5.29.33' => 'id-ce-policyMappings', + '2.5.29.17' => 'id-ce-subjectAltName', + '2.5.29.18' => 'id-ce-issuerAltName', + '2.5.29.9' => 'id-ce-subjectDirectoryAttributes', + '2.5.29.19' => 'id-ce-basicConstraints', + '2.5.29.30' => 'id-ce-nameConstraints', + '2.5.29.36' => 'id-ce-policyConstraints', + '2.5.29.31' => 'id-ce-cRLDistributionPoints', + '2.5.29.37' => 'id-ce-extKeyUsage', + '2.5.29.37.0' => 'anyExtendedKeyUsage', + '1.3.6.1.5.5.7.3.1' => 'id-kp-serverAuth', + '1.3.6.1.5.5.7.3.2' => 'id-kp-clientAuth', + '1.3.6.1.5.5.7.3.3' => 'id-kp-codeSigning', + '1.3.6.1.5.5.7.3.4' => 'id-kp-emailProtection', + '1.3.6.1.5.5.7.3.8' => 'id-kp-timeStamping', + '1.3.6.1.5.5.7.3.9' => 'id-kp-OCSPSigning', + '2.5.29.54' => 'id-ce-inhibitAnyPolicy', + '2.5.29.46' => 'id-ce-freshestCRL', + '1.3.6.1.5.5.7.1.1' => 'id-pe-authorityInfoAccess', + '1.3.6.1.5.5.7.1.11' => 'id-pe-subjectInfoAccess', + '2.5.29.20' => 'id-ce-cRLNumber', + '2.5.29.28' => 'id-ce-issuingDistributionPoint', + '2.5.29.27' => 'id-ce-deltaCRLIndicator', + '2.5.29.21' => 'id-ce-cRLReasons', + '2.5.29.29' => 'id-ce-certificateIssuer', + '2.5.29.23' => 'id-ce-holdInstructionCode', + '1.2.840.10040.2' => 'holdInstruction', + '1.2.840.10040.2.1' => 'id-holdinstruction-none', + '1.2.840.10040.2.2' => 'id-holdinstruction-callissuer', + '1.2.840.10040.2.3' => 'id-holdinstruction-reject', + '2.5.29.24' => 'id-ce-invalidityDate', + + '1.2.840.113549.2.2' => 'md2', + '1.2.840.113549.2.5' => 'md5', + '1.3.14.3.2.26' => 'id-sha1', + '1.2.840.10040.4.1' => 'id-dsa', + '1.2.840.10040.4.3' => 'id-dsa-with-sha1', + '1.2.840.113549.1.1' => 'pkcs-1', + '1.2.840.113549.1.1.1' => 'rsaEncryption', + '1.2.840.113549.1.1.2' => 'md2WithRSAEncryption', + '1.2.840.113549.1.1.4' => 'md5WithRSAEncryption', + '1.2.840.113549.1.1.5' => 'sha1WithRSAEncryption', + '1.2.840.10046.2.1' => 'dhpublicnumber', + '2.16.840.1.101.2.1.1.22' => 'id-keyExchangeAlgorithm', + '1.2.840.10045' => 'ansi-X9-62', + '1.2.840.10045.4' => 'id-ecSigType', + '1.2.840.10045.4.1' => 'ecdsa-with-SHA1', + '1.2.840.10045.1' => 'id-fieldType', + '1.2.840.10045.1.1' => 'prime-field', + '1.2.840.10045.1.2' => 'characteristic-two-field', + '1.2.840.10045.1.2.3' => 'id-characteristic-two-basis', + '1.2.840.10045.1.2.3.1' => 'gnBasis', + '1.2.840.10045.1.2.3.2' => 'tpBasis', + '1.2.840.10045.1.2.3.3' => 'ppBasis', + '1.2.840.10045.2' => 'id-publicKeyType', + '1.2.840.10045.2.1' => 'id-ecPublicKey', + '1.2.840.10045.3' => 'ellipticCurve', + '1.2.840.10045.3.0' => 'c-TwoCurve', + '1.2.840.10045.3.0.1' => 'c2pnb163v1', + '1.2.840.10045.3.0.2' => 'c2pnb163v2', + '1.2.840.10045.3.0.3' => 'c2pnb163v3', + '1.2.840.10045.3.0.4' => 'c2pnb176w1', + '1.2.840.10045.3.0.5' => 'c2pnb191v1', + '1.2.840.10045.3.0.6' => 'c2pnb191v2', + '1.2.840.10045.3.0.7' => 'c2pnb191v3', + '1.2.840.10045.3.0.8' => 'c2pnb191v4', + '1.2.840.10045.3.0.9' => 'c2pnb191v5', + '1.2.840.10045.3.0.10' => 'c2pnb208w1', + '1.2.840.10045.3.0.11' => 'c2pnb239v1', + '1.2.840.10045.3.0.12' => 'c2pnb239v2', + '1.2.840.10045.3.0.13' => 'c2pnb239v3', + '1.2.840.10045.3.0.14' => 'c2pnb239v4', + '1.2.840.10045.3.0.15' => 'c2pnb239v5', + '1.2.840.10045.3.0.16' => 'c2pnb272w1', + '1.2.840.10045.3.0.17' => 'c2pnb304w1', + '1.2.840.10045.3.0.18' => 'c2pnb359v1', + '1.2.840.10045.3.0.19' => 'c2pnb368w1', + '1.2.840.10045.3.0.20' => 'c2pnb431r1', + '1.2.840.10045.3.1' => 'primeCurve', + '1.2.840.10045.3.1.1' => 'prime192v1', + '1.2.840.10045.3.1.2' => 'prime192v2', + '1.2.840.10045.3.1.3' => 'prime192v3', + '1.2.840.10045.3.1.4' => 'prime239v1', + '1.2.840.10045.3.1.5' => 'prime239v2', + '1.2.840.10045.3.1.6' => 'prime239v3', + '1.2.840.10045.3.1.7' => 'prime256v1', + '1.2.840.113549.1.1.7' => 'id-RSAES-OAEP', + '1.2.840.113549.1.1.9' => 'id-pSpecified', + '1.2.840.113549.1.1.10' => 'id-RSASSA-PSS', + '1.2.840.113549.1.1.8' => 'id-mgf1', + '1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption', + '1.2.840.113549.1.1.11' => 'sha256WithRSAEncryption', + '1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption', + '1.2.840.113549.1.1.13' => 'sha512WithRSAEncryption', + '2.16.840.1.101.3.4.2.4' => 'id-sha224', + '2.16.840.1.101.3.4.2.1' => 'id-sha256', + '2.16.840.1.101.3.4.2.2' => 'id-sha384', + '2.16.840.1.101.3.4.2.3' => 'id-sha512', + '1.2.643.2.2.4' => 'id-GostR3411-94-with-GostR3410-94', + '1.2.643.2.2.3' => 'id-GostR3411-94-with-GostR3410-2001', + '1.2.643.2.2.20' => 'id-GostR3410-2001', + '1.2.643.2.2.19' => 'id-GostR3410-94', + // Netscape Object Identifiers from "Netscape Certificate Extensions" + '2.16.840.1.113730' => 'netscape', + '2.16.840.1.113730.1' => 'netscape-cert-extension', + '2.16.840.1.113730.1.1' => 'netscape-cert-type', + '2.16.840.1.113730.1.13' => 'netscape-comment', + '2.16.840.1.113730.1.8' => 'netscape-ca-policy-url', + // the following are X.509 extensions not supported by phpseclib + '1.3.6.1.5.5.7.1.12' => 'id-pe-logotype', + '1.2.840.113533.7.65.0' => 'entrustVersInfo', + '2.16.840.1.113733.1.6.9' => 'verisignPrivate', + // for Certificate Signing Requests + // see http://tools.ietf.org/html/rfc2985 + '1.2.840.113549.1.9.2' => 'pkcs-9-at-unstructuredName', // PKCS #9 unstructured name + '1.2.840.113549.1.9.7' => 'pkcs-9-at-challengePassword', // Challenge password for certificate revocations + '1.2.840.113549.1.9.14' => 'pkcs-9-at-extensionRequest' // Certificate extension request + ); + } + + /** + * Load X.509 certificate + * + * Returns an associative array describing the X.509 cert or a false if the cert failed to load + * + * @param string $cert + * @param int $mode + * @access public + * @return mixed + */ + function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT) + { + if (is_array($cert) && isset($cert['tbsCertificate'])) { + unset($this->currentCert); + unset($this->currentKeyIdentifier); + $this->dn = $cert['tbsCertificate']['subject']; + if (!isset($this->dn)) { + return false; + } + $this->currentCert = $cert; + + $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier'); + $this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : null; + + unset($this->signatureSubject); + + return $cert; + } + + $asn1 = new ASN1(); + + if ($mode != self::FORMAT_DER) { + $newcert = $this->_extractBER($cert); + if ($mode == self::FORMAT_PEM && $cert == $newcert) { + return false; + } + $cert = $newcert; + } + + if ($cert === false) { + $this->currentCert = false; + return false; + } + + $asn1->loadOIDs($this->oids); + $decoded = $asn1->decodeBER($cert); + + if (!empty($decoded)) { + $x509 = $asn1->asn1map($decoded[0], $this->Certificate); + } + if (!isset($x509) || $x509 === false) { + $this->currentCert = false; + return false; + } + + $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); + + if ($this->_isSubArrayValid($x509, 'tbsCertificate/extensions')) { + $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1); + } + $this->_mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence', $asn1); + $this->_mapInDNs($x509, 'tbsCertificate/subject/rdnSequence', $asn1); + + $key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']; + $key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key); + + $this->currentCert = $x509; + $this->dn = $x509['tbsCertificate']['subject']; + + $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier'); + $this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : null; + + return $x509; + } + + /** + * Save X.509 certificate + * + * @param array $cert + * @param int $format optional + * @access public + * @return string + */ + function saveX509($cert, $format = self::FORMAT_PEM) + { + if (!is_array($cert) || !isset($cert['tbsCertificate'])) { + return false; + } + + switch (true) { + // "case !$a: case !$b: break; default: whatever();" is the same thing as "if ($a && $b) whatever()" + case !($algorithm = $this->_subArray($cert, 'tbsCertificate/subjectPublicKeyInfo/algorithm/algorithm')): + case is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']): + break; + default: + switch ($algorithm) { + case 'rsaEncryption': + $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] + = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']))); + /* "[For RSA keys] the parameters field MUST have ASN.1 type NULL for this algorithm identifier." + -- https://tools.ietf.org/html/rfc3279#section-2.3.1 + + given that and the fact that RSA keys appear ot be the only key type for which the parameters field can be blank, + it seems like perhaps the ASN.1 description ought not say the parameters field is OPTIONAL, but whatever. + */ + $cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = null; + // https://tools.ietf.org/html/rfc3279#section-2.2.1 + $cert['signatureAlgorithm']['parameters'] = null; + $cert['tbsCertificate']['signature']['parameters'] = null; + } + } + + $asn1 = new ASN1(); + $asn1->loadOIDs($this->oids); + + $filters = array(); + $type_utf8_string = array('type' => ASN1::TYPE_UTF8_STRING); + $filters['tbsCertificate']['signature']['parameters'] = $type_utf8_string; + $filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] = $type_utf8_string; + $filters['tbsCertificate']['issuer']['rdnSequence']['value'] = $type_utf8_string; + $filters['tbsCertificate']['subject']['rdnSequence']['value'] = $type_utf8_string; + $filters['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = $type_utf8_string; + $filters['signatureAlgorithm']['parameters'] = $type_utf8_string; + $filters['authorityCertIssuer']['directoryName']['rdnSequence']['value'] = $type_utf8_string; + //$filters['policyQualifiers']['qualifier'] = $type_utf8_string; + $filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] = $type_utf8_string; + $filters['directoryName']['rdnSequence']['value'] = $type_utf8_string; + + /* in the case of policyQualifiers/qualifier, the type has to be \phpseclib\File\ASN1::TYPE_IA5_STRING. + \phpseclib\File\ASN1::TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random + characters. + */ + $filters['policyQualifiers']['qualifier'] + = array('type' => ASN1::TYPE_IA5_STRING); + + $asn1->loadFilters($filters); + + $this->_mapOutExtensions($cert, 'tbsCertificate/extensions', $asn1); + $this->_mapOutDNs($cert, 'tbsCertificate/issuer/rdnSequence', $asn1); + $this->_mapOutDNs($cert, 'tbsCertificate/subject/rdnSequence', $asn1); + + $cert = $asn1->encodeDER($cert, $this->Certificate); + + switch ($format) { + case self::FORMAT_DER: + return $cert; + // case self::FORMAT_PEM: + default: + return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(base64_encode($cert), 64) . '-----END CERTIFICATE-----'; + } + } + + /** + * Map extension values from octet string to extension-specific internal + * format. + * + * @param array $root (by reference) + * @param string $path + * @param object $asn1 + * @access private + */ + function _mapInExtensions(&$root, $path, $asn1) + { + $extensions = &$this->_subArrayUnchecked($root, $path); + + if ($extensions) { + for ($i = 0; $i < count($extensions); $i++) { + $id = $extensions[$i]['extnId']; + $value = &$extensions[$i]['extnValue']; + $value = base64_decode($value); + /* [extnValue] contains the DER encoding of an ASN.1 value + corresponding to the extension type identified by extnID */ + $map = $this->_getMapping($id); + if (!is_bool($map)) { + $decoder = $id == 'id-ce-nameConstraints' ? + array($this, '_decodeNameConstraintIP') : + array($this, '_decodeIP'); + $decoded = $asn1->decodeBER($value); + $mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => $decoder)); + $value = $mapped === false ? $decoded[0] : $mapped; + + if ($id == 'id-ce-certificatePolicies') { + for ($j = 0; $j < count($value); $j++) { + if (!isset($value[$j]['policyQualifiers'])) { + continue; + } + for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) { + $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId']; + $map = $this->_getMapping($subid); + $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier']; + if ($map !== false) { + $decoded = $asn1->decodeBER($subvalue); + $mapped = $asn1->asn1map($decoded[0], $map); + $subvalue = $mapped === false ? $decoded[0] : $mapped; + } + } + } + } + } else { + $value = base64_encode($value); + } + } + } + } + + /** + * Map extension values from extension-specific internal format to + * octet string. + * + * @param array $root (by reference) + * @param string $path + * @param object $asn1 + * @access private + */ + function _mapOutExtensions(&$root, $path, $asn1) + { + $extensions = &$this->_subArray($root, $path); + + if (is_array($extensions)) { + $size = count($extensions); + for ($i = 0; $i < $size; $i++) { + if ($extensions[$i] instanceof Element) { + continue; + } + + $id = $extensions[$i]['extnId']; + $value = &$extensions[$i]['extnValue']; + + switch ($id) { + case 'id-ce-certificatePolicies': + for ($j = 0; $j < count($value); $j++) { + if (!isset($value[$j]['policyQualifiers'])) { + continue; + } + for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) { + $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId']; + $map = $this->_getMapping($subid); + $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier']; + if ($map !== false) { + // by default \phpseclib\File\ASN1 will try to render qualifier as a \phpseclib\File\ASN1::TYPE_IA5_STRING since it's + // actual type is \phpseclib\File\ASN1::TYPE_ANY + $subvalue = new Element($asn1->encodeDER($subvalue, $map)); + } + } + } + break; + case 'id-ce-authorityKeyIdentifier': // use 00 as the serial number instead of an empty string + if (isset($value['authorityCertSerialNumber'])) { + if ($value['authorityCertSerialNumber']->toBytes() == '') { + $temp = chr((ASN1::CLASS_CONTEXT_SPECIFIC << 6) | 2) . "\1\0"; + $value['authorityCertSerialNumber'] = new Element($temp); + } + } + } + + /* [extnValue] contains the DER encoding of an ASN.1 value + corresponding to the extension type identified by extnID */ + $map = $this->_getMapping($id); + if (is_bool($map)) { + if (!$map) { + user_error($id . ' is not a currently supported extension'); + unset($extensions[$i]); + } + } else { + $temp = $asn1->encodeDER($value, $map, array('iPAddress' => array($this, '_encodeIP'))); + $value = base64_encode($temp); + } + } + } + } + + /** + * Map attribute values from ANY type to attribute-specific internal + * format. + * + * @param array $root (by reference) + * @param string $path + * @param object $asn1 + * @access private + */ + function _mapInAttributes(&$root, $path, $asn1) + { + $attributes = &$this->_subArray($root, $path); + + if (is_array($attributes)) { + for ($i = 0; $i < count($attributes); $i++) { + $id = $attributes[$i]['type']; + /* $value contains the DER encoding of an ASN.1 value + corresponding to the attribute type identified by type */ + $map = $this->_getMapping($id); + if (is_array($attributes[$i]['value'])) { + $values = &$attributes[$i]['value']; + for ($j = 0; $j < count($values); $j++) { + $value = $asn1->encodeDER($values[$j], $this->AttributeValue); + $decoded = $asn1->decodeBER($value); + if (!is_bool($map)) { + $mapped = $asn1->asn1map($decoded[0], $map); + if ($mapped !== false) { + $values[$j] = $mapped; + } + if ($id == 'pkcs-9-at-extensionRequest' && $this->_isSubArrayValid($values, $j)) { + $this->_mapInExtensions($values, $j, $asn1); + } + } elseif ($map) { + $values[$j] = base64_encode($value); + } + } + } + } + } + } + + /** + * Map attribute values from attribute-specific internal format to + * ANY type. + * + * @param array $root (by reference) + * @param string $path + * @param object $asn1 + * @access private + */ + function _mapOutAttributes(&$root, $path, $asn1) + { + $attributes = &$this->_subArray($root, $path); + + if (is_array($attributes)) { + $size = count($attributes); + for ($i = 0; $i < $size; $i++) { + /* [value] contains the DER encoding of an ASN.1 value + corresponding to the attribute type identified by type */ + $id = $attributes[$i]['type']; + $map = $this->_getMapping($id); + if ($map === false) { + user_error($id . ' is not a currently supported attribute', E_USER_NOTICE); + unset($attributes[$i]); + } elseif (is_array($attributes[$i]['value'])) { + $values = &$attributes[$i]['value']; + for ($j = 0; $j < count($values); $j++) { + switch ($id) { + case 'pkcs-9-at-extensionRequest': + $this->_mapOutExtensions($values, $j, $asn1); + break; + } + + if (!is_bool($map)) { + $temp = $asn1->encodeDER($values[$j], $map); + $decoded = $asn1->decodeBER($temp); + $values[$j] = $asn1->asn1map($decoded[0], $this->AttributeValue); + } + } + } + } + } + } + + /** + * Map DN values from ANY type to DN-specific internal + * format. + * + * @param array $root (by reference) + * @param string $path + * @param object $asn1 + * @access private + */ + function _mapInDNs(&$root, $path, $asn1) + { + $dns = &$this->_subArray($root, $path); + + if (is_array($dns)) { + for ($i = 0; $i < count($dns); $i++) { + for ($j = 0; $j < count($dns[$i]); $j++) { + $type = $dns[$i][$j]['type']; + $value = &$dns[$i][$j]['value']; + if (is_object($value) && $value instanceof Element) { + $map = $this->_getMapping($type); + if (!is_bool($map)) { + $decoded = $asn1->decodeBER($value); + $value = $asn1->asn1map($decoded[0], $map); + } + } + } + } + } + } + + /** + * Map DN values from DN-specific internal format to + * ANY type. + * + * @param array $root (by reference) + * @param string $path + * @param object $asn1 + * @access private + */ + function _mapOutDNs(&$root, $path, $asn1) + { + $dns = &$this->_subArray($root, $path); + + if (is_array($dns)) { + $size = count($dns); + for ($i = 0; $i < $size; $i++) { + for ($j = 0; $j < count($dns[$i]); $j++) { + $type = $dns[$i][$j]['type']; + $value = &$dns[$i][$j]['value']; + if (is_object($value) && $value instanceof Element) { + continue; + } + + $map = $this->_getMapping($type); + if (!is_bool($map)) { + $value = new Element($asn1->encodeDER($value, $map)); + } + } + } + } + } + + /** + * Associate an extension ID to an extension mapping + * + * @param string $extnId + * @access private + * @return mixed + */ + function _getMapping($extnId) + { + if (!is_string($extnId)) { // eg. if it's a \phpseclib\File\ASN1\Element object + return true; + } + + switch ($extnId) { + case 'id-ce-keyUsage': + return $this->KeyUsage; + case 'id-ce-basicConstraints': + return $this->BasicConstraints; + case 'id-ce-subjectKeyIdentifier': + return $this->KeyIdentifier; + case 'id-ce-cRLDistributionPoints': + return $this->CRLDistributionPoints; + case 'id-ce-authorityKeyIdentifier': + return $this->AuthorityKeyIdentifier; + case 'id-ce-certificatePolicies': + return $this->CertificatePolicies; + case 'id-ce-extKeyUsage': + return $this->ExtKeyUsageSyntax; + case 'id-pe-authorityInfoAccess': + return $this->AuthorityInfoAccessSyntax; + case 'id-pe-subjectInfoAccess': + return $this->SubjectInfoAccessSyntax; + case 'id-ce-subjectAltName': + return $this->SubjectAltName; + case 'id-ce-subjectDirectoryAttributes': + return $this->SubjectDirectoryAttributes; + case 'id-ce-privateKeyUsagePeriod': + return $this->PrivateKeyUsagePeriod; + case 'id-ce-issuerAltName': + return $this->IssuerAltName; + case 'id-ce-policyMappings': + return $this->PolicyMappings; + case 'id-ce-nameConstraints': + return $this->NameConstraints; + + case 'netscape-cert-type': + return $this->netscape_cert_type; + case 'netscape-comment': + return $this->netscape_comment; + case 'netscape-ca-policy-url': + return $this->netscape_ca_policy_url; + + // since id-qt-cps isn't a constructed type it will have already been decoded as a string by the time it gets + // back around to asn1map() and we don't want it decoded again. + //case 'id-qt-cps': + // return $this->CPSuri; + case 'id-qt-unotice': + return $this->UserNotice; + + // the following OIDs are unsupported but we don't want them to give notices when calling saveX509(). + case 'id-pe-logotype': // http://www.ietf.org/rfc/rfc3709.txt + case 'entrustVersInfo': + // http://support.microsoft.com/kb/287547 + case '1.3.6.1.4.1.311.20.2': // szOID_ENROLL_CERTTYPE_EXTENSION + case '1.3.6.1.4.1.311.21.1': // szOID_CERTSRV_CA_VERSION + // "SET Secure Electronic Transaction Specification" + // http://www.maithean.com/docs/set_bk3.pdf + case '2.23.42.7.0': // id-set-hashedRootKey + // "Certificate Transparency" + // https://tools.ietf.org/html/rfc6962 + case '1.3.6.1.4.1.11129.2.4.2': + // "Qualified Certificate statements" + // https://tools.ietf.org/html/rfc3739#section-3.2.6 + case '1.3.6.1.5.5.7.1.3': + return true; + + // CSR attributes + case 'pkcs-9-at-unstructuredName': + return $this->PKCS9String; + case 'pkcs-9-at-challengePassword': + return $this->DirectoryString; + case 'pkcs-9-at-extensionRequest': + return $this->Extensions; + + // CRL extensions. + case 'id-ce-cRLNumber': + return $this->CRLNumber; + case 'id-ce-deltaCRLIndicator': + return $this->CRLNumber; + case 'id-ce-issuingDistributionPoint': + return $this->IssuingDistributionPoint; + case 'id-ce-freshestCRL': + return $this->CRLDistributionPoints; + case 'id-ce-cRLReasons': + return $this->CRLReason; + case 'id-ce-invalidityDate': + return $this->InvalidityDate; + case 'id-ce-certificateIssuer': + return $this->CertificateIssuer; + case 'id-ce-holdInstructionCode': + return $this->HoldInstructionCode; + case 'id-at-postalAddress': + return $this->PostalAddress; + } + + return false; + } + + /** + * Load an X.509 certificate as a certificate authority + * + * @param string $cert + * @access public + * @return bool + */ + function loadCA($cert) + { + $olddn = $this->dn; + $oldcert = $this->currentCert; + $oldsigsubj = $this->signatureSubject; + $oldkeyid = $this->currentKeyIdentifier; + + $cert = $this->loadX509($cert); + if (!$cert) { + $this->dn = $olddn; + $this->currentCert = $oldcert; + $this->signatureSubject = $oldsigsubj; + $this->currentKeyIdentifier = $oldkeyid; + + return false; + } + + /* From RFC5280 "PKIX Certificate and CRL Profile": + + If the keyUsage extension is present, then the subject public key + MUST NOT be used to verify signatures on certificates or CRLs unless + the corresponding keyCertSign or cRLSign bit is set. */ + //$keyUsage = $this->getExtension('id-ce-keyUsage'); + //if ($keyUsage && !in_array('keyCertSign', $keyUsage)) { + // return false; + //} + + /* From RFC5280 "PKIX Certificate and CRL Profile": + + The cA boolean indicates whether the certified public key may be used + to verify certificate signatures. If the cA boolean is not asserted, + then the keyCertSign bit in the key usage extension MUST NOT be + asserted. If the basic constraints extension is not present in a + version 3 certificate, or the extension is present but the cA boolean + is not asserted, then the certified public key MUST NOT be used to + verify certificate signatures. */ + //$basicConstraints = $this->getExtension('id-ce-basicConstraints'); + //if (!$basicConstraints || !$basicConstraints['cA']) { + // return false; + //} + + $this->CAs[] = $cert; + + $this->dn = $olddn; + $this->currentCert = $oldcert; + $this->signatureSubject = $oldsigsubj; + + return true; + } + + /** + * Validate an X.509 certificate against a URL + * + * From RFC2818 "HTTP over TLS": + * + * Matching is performed using the matching rules specified by + * [RFC2459]. If more than one identity of a given type is present in + * the certificate (e.g., more than one dNSName name, a match in any one + * of the set is considered acceptable.) Names may contain the wildcard + * character * which is considered to match any single domain name + * component or component fragment. E.g., *.a.com matches foo.a.com but + * not bar.foo.a.com. f*.com matches foo.com but not bar.com. + * + * @param string $url + * @access public + * @return bool + */ + function validateURL($url) + { + if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { + return false; + } + + $components = parse_url($url); + if (!isset($components['host'])) { + return false; + } + + if ($names = $this->getExtension('id-ce-subjectAltName')) { + foreach ($names as $name) { + foreach ($name as $key => $value) { + $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value); + switch ($key) { + case 'dNSName': + /* From RFC2818 "HTTP over TLS": + + If a subjectAltName extension of type dNSName is present, that MUST + be used as the identity. Otherwise, the (most specific) Common Name + field in the Subject field of the certificate MUST be used. Although + the use of the Common Name is existing practice, it is deprecated and + Certification Authorities are encouraged to use the dNSName instead. */ + if (preg_match('#^' . $value . '$#', $components['host'])) { + return true; + } + break; + case 'iPAddress': + /* From RFC2818 "HTTP over TLS": + + In some cases, the URI is specified as an IP address rather than a + hostname. In this case, the iPAddress subjectAltName must be present + in the certificate and must exactly match the IP in the URI. */ + if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) { + return true; + } + } + } + } + return false; + } + + if ($value = $this->getDNProp('id-at-commonName')) { + $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value[0]); + return preg_match('#^' . $value . '$#', $components['host']); + } + + return false; + } + + /** + * Validate a date + * + * If $date isn't defined it is assumed to be the current date. + * + * @param \DateTime|string $date optional + * @access public + */ + function validateDate($date = null) + { + if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { + return false; + } + + if (!isset($date)) { + $date = new DateTime(null, new DateTimeZone(@date_default_timezone_get())); + } + + $notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore']; + $notBefore = isset($notBefore['generalTime']) ? $notBefore['generalTime'] : $notBefore['utcTime']; + + $notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter']; + $notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime']; + + if (is_string($date)) { + $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get())); + } + + $notBefore = new DateTime($notBefore, new DateTimeZone(@date_default_timezone_get())); + $notAfter = new DateTime($notAfter, new DateTimeZone(@date_default_timezone_get())); + + switch (true) { + case $date < $notBefore: + case $date > $notAfter: + return false; + } + + return true; + } + + /** + * Fetches a URL + * + * @param string $url + * @access private + * @return bool|string + */ + static function _fetchURL($url) + { + if (self::$disable_url_fetch) { + return false; + } + + $parts = parse_url($url); + $data = ''; + switch ($parts['scheme']) { + case 'http': + $fsock = @fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80); + if (!$fsock) { + return false; + } + $path = $parts['path']; + if (isset($parts['query'])) { + $path.= '?' . $parts['query']; + } + fputs($fsock, "GET $path HTTP/1.0\r\n"); + fputs($fsock, "Host: $parts[host]\r\n\r\n"); + $line = fgets($fsock, 1024); + if (strlen($line) < 3) { + return false; + } + preg_match('#HTTP/1.\d (\d{3})#', $line, $temp); + if ($temp[1] != '200') { + return false; + } + + // skip the rest of the headers in the http response + while (!feof($fsock) && fgets($fsock, 1024) != "\r\n") { + } + + while (!feof($fsock)) { + $temp = fread($fsock, 1024); + if ($temp === false) { + return false; + } + $data.= $temp; + } + + break; + //case 'ftp': + //case 'ldap': + //default: + } + + return $data; + } + + /** + * Validates an intermediate cert as identified via authority info access extension + * + * See https://tools.ietf.org/html/rfc4325 for more info + * + * @param bool $caonly + * @param int $count + * @access private + * @return bool + */ + function _testForIntermediate($caonly, $count) + { + $opts = $this->getExtension('id-pe-authorityInfoAccess'); + if (!is_array($opts)) { + return false; + } + foreach ($opts as $opt) { + if ($opt['accessMethod'] == 'id-ad-caIssuers') { + // accessLocation is a GeneralName. GeneralName fields support stuff like email addresses, IP addresses, LDAP, + // etc, but we're only supporting URI's. URI's and LDAP are the only thing https://tools.ietf.org/html/rfc4325 + // discusses + if (isset($opt['accessLocation']['uniformResourceIdentifier'])) { + $url = $opt['accessLocation']['uniformResourceIdentifier']; + break; + } + } + } + + if (!isset($url)) { + return false; + } + + $cert = static::_fetchURL($url); + if (!is_string($cert)) { + return false; + } + + $parent = new static(); + $parent->CAs = $this->CAs; + /* + "Conforming applications that support HTTP or FTP for accessing + certificates MUST be able to accept .cer files and SHOULD be able + to accept .p7c files." -- https://tools.ietf.org/html/rfc4325 + + A .p7c file is 'a "certs-only" CMS message as specified in RFC 2797" + + These are currently unsupported + */ + if (!is_array($parent->loadX509($cert))) { + return false; + } + + if (!$parent->_validateSignatureCountable($caonly, ++$count)) { + return false; + } + + $this->CAs[] = $parent->currentCert; + //$this->loadCA($cert); + + return true; + } + + /** + * Validate a signature + * + * Works on X.509 certs, CSR's and CRL's. + * Returns true if the signature is verified, false if it is not correct or null on error + * + * By default returns false for self-signed certs. Call validateSignature(false) to make this support + * self-signed. + * + * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}. + * + * @param bool $caonly optional + * @access public + * @return mixed + */ + function validateSignature($caonly = true) + { + return $this->_validateSignatureCountable($caonly, 0); + } + + /** + * Validate a signature + * + * Performs said validation whilst keeping track of how many times validation method is called + * + * @param bool $caonly + * @param int $count + * @access private + * @return mixed + */ + function _validateSignatureCountable($caonly, $count) + { + if (!is_array($this->currentCert) || !isset($this->signatureSubject)) { + return null; + } + + if ($count == self::$recur_limit) { + return false; + } + + /* TODO: + "emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")." + -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6 + + implement pathLenConstraint in the id-ce-basicConstraints extension */ + + switch (true) { + case isset($this->currentCert['tbsCertificate']): + // self-signed cert + switch (true) { + case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']: + case defined('FILE_X509_IGNORE_TYPE') && $this->getIssuerDN(self::DN_STRING) === $this->getDN(self::DN_STRING): + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier'); + switch (true) { + case !is_array($authorityKey): + case !$subjectKeyID: + case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + $signingCert = $this->currentCert; // working cert + } + } + + if (!empty($this->CAs)) { + for ($i = 0; $i < count($this->CAs); $i++) { + // even if the cert is a self-signed one we still want to see if it's a CA; + // if not, we'll conditionally return an error + $ca = $this->CAs[$i]; + switch (true) { + case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']: + case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(self::DN_STRING, $this->currentCert['tbsCertificate']['issuer']) === $this->getDN(self::DN_STRING, $ca['tbsCertificate']['subject']): + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); + switch (true) { + case !is_array($authorityKey): + case !$subjectKeyID: + case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) { + break 2; // serial mismatch - check other ca + } + $signingCert = $ca; // working cert + break 3; + } + } + } + if (count($this->CAs) == $i && $caonly) { + return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly); + } + } elseif (!isset($signingCert) || $caonly) { + return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly); + } + return $this->_validateSignature( + $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], + $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], + $this->currentCert['signatureAlgorithm']['algorithm'], + substr(base64_decode($this->currentCert['signature']), 1), + $this->signatureSubject + ); + case isset($this->currentCert['certificationRequestInfo']): + return $this->_validateSignature( + $this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'], + $this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], + $this->currentCert['signatureAlgorithm']['algorithm'], + substr(base64_decode($this->currentCert['signature']), 1), + $this->signatureSubject + ); + case isset($this->currentCert['publicKeyAndChallenge']): + return $this->_validateSignature( + $this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'], + $this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'], + $this->currentCert['signatureAlgorithm']['algorithm'], + substr(base64_decode($this->currentCert['signature']), 1), + $this->signatureSubject + ); + case isset($this->currentCert['tbsCertList']): + if (!empty($this->CAs)) { + for ($i = 0; $i < count($this->CAs); $i++) { + $ca = $this->CAs[$i]; + switch (true) { + case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']: + case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(self::DN_STRING, $this->currentCert['tbsCertList']['issuer']) === $this->getDN(self::DN_STRING, $ca['tbsCertificate']['subject']): + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); + switch (true) { + case !is_array($authorityKey): + case !$subjectKeyID: + case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) { + break 2; // serial mismatch - check other ca + } + $signingCert = $ca; // working cert + break 3; + } + } + } + } + if (!isset($signingCert)) { + return false; + } + return $this->_validateSignature( + $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], + $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], + $this->currentCert['signatureAlgorithm']['algorithm'], + substr(base64_decode($this->currentCert['signature']), 1), + $this->signatureSubject + ); + default: + return false; + } + } + + /** + * Validates a signature + * + * Returns true if the signature is verified, false if it is not correct or null on error + * + * @param string $publicKeyAlgorithm + * @param string $publicKey + * @param string $signatureAlgorithm + * @param string $signature + * @param string $signatureSubject + * @access private + * @return int + */ + function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject) + { + switch ($publicKeyAlgorithm) { + case 'rsaEncryption': + $rsa = new RSA(); + $rsa->loadKey($publicKey); + + switch ($signatureAlgorithm) { + case 'md2WithRSAEncryption': + case 'md5WithRSAEncryption': + case 'sha1WithRSAEncryption': + case 'sha224WithRSAEncryption': + case 'sha256WithRSAEncryption': + case 'sha384WithRSAEncryption': + case 'sha512WithRSAEncryption': + $rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); + $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1); + if (!@$rsa->verify($signatureSubject, $signature)) { + return false; + } + break; + default: + return null; + } + break; + default: + return null; + } + + return true; + } + + /** + * Sets the recursion limit + * + * When validating a signature it may be necessary to download intermediate certs from URI's. + * An intermediate cert that linked to itself would result in an infinite loop so to prevent + * that we set a recursion limit. A negative number means that there is no recursion limit. + * + * @param int $count + * @access public + */ + static function setRecurLimit($count) + { + self::$recur_limit = $count; + } + + /** + * Prevents URIs from being automatically retrieved + * + * @access public + */ + static function disableURLFetch() + { + self::$disable_url_fetch = true; + } + + /** + * Allows URIs to be automatically retrieved + * + * @access public + */ + static function enableURLFetch() + { + self::$disable_url_fetch = false; + } + + /** + * Reformat public keys + * + * Reformats a public key to a format supported by phpseclib (if applicable) + * + * @param string $algorithm + * @param string $key + * @access private + * @return string + */ + function _reformatKey($algorithm, $key) + { + switch ($algorithm) { + case 'rsaEncryption': + return + "-----BEGIN RSA PUBLIC KEY-----\r\n" . + // subjectPublicKey is stored as a bit string in X.509 certs. the first byte of a bit string represents how many bits + // in the last byte should be ignored. the following only supports non-zero stuff but as none of the X.509 certs Firefox + // uses as a cert authority actually use a non-zero bit I think it's safe to assume that none do. + chunk_split(base64_encode(substr(base64_decode($key), 1)), 64) . + '-----END RSA PUBLIC KEY-----'; + default: + return $key; + } + } + + /** + * Decodes an IP address + * + * Takes in a base64 encoded "blob" and returns a human readable IP address + * + * @param string $ip + * @access private + * @return string + */ + function _decodeIP($ip) + { + return inet_ntop(base64_decode($ip)); + } + + /** + * Decodes an IP address in a name constraints extension + * + * Takes in a base64 encoded "blob" and returns a human readable IP address / mask + * + * @param string $ip + * @access private + * @return array + */ + function _decodeNameConstraintIP($ip) + { + $ip = base64_decode($ip); + $size = strlen($ip) >> 1; + $mask = substr($ip, $size); + $ip = substr($ip, 0, $size); + return array(inet_ntop($ip), inet_ntop($mask)); + } + + /** + * Encodes an IP address + * + * Takes a human readable IP address into a base64-encoded "blob" + * + * @param string|array $ip + * @access private + * @return string + */ + function _encodeIP($ip) + { + return is_string($ip) ? + base64_encode(inet_pton($ip)) : + base64_encode(inet_pton($ip[0]) . inet_pton($ip[1])); + } + + /** + * "Normalizes" a Distinguished Name property + * + * @param string $propName + * @access private + * @return mixed + */ + function _translateDNProp($propName) + { + switch (strtolower($propName)) { + case 'id-at-countryname': + case 'countryname': + case 'c': + return 'id-at-countryName'; + case 'id-at-organizationname': + case 'organizationname': + case 'o': + return 'id-at-organizationName'; + case 'id-at-dnqualifier': + case 'dnqualifier': + return 'id-at-dnQualifier'; + case 'id-at-commonname': + case 'commonname': + case 'cn': + return 'id-at-commonName'; + case 'id-at-stateorprovincename': + case 'stateorprovincename': + case 'state': + case 'province': + case 'provincename': + case 'st': + return 'id-at-stateOrProvinceName'; + case 'id-at-localityname': + case 'localityname': + case 'l': + return 'id-at-localityName'; + case 'id-emailaddress': + case 'emailaddress': + return 'pkcs-9-at-emailAddress'; + case 'id-at-serialnumber': + case 'serialnumber': + return 'id-at-serialNumber'; + case 'id-at-postalcode': + case 'postalcode': + return 'id-at-postalCode'; + case 'id-at-streetaddress': + case 'streetaddress': + return 'id-at-streetAddress'; + case 'id-at-name': + case 'name': + return 'id-at-name'; + case 'id-at-givenname': + case 'givenname': + return 'id-at-givenName'; + case 'id-at-surname': + case 'surname': + case 'sn': + return 'id-at-surname'; + case 'id-at-initials': + case 'initials': + return 'id-at-initials'; + case 'id-at-generationqualifier': + case 'generationqualifier': + return 'id-at-generationQualifier'; + case 'id-at-organizationalunitname': + case 'organizationalunitname': + case 'ou': + return 'id-at-organizationalUnitName'; + case 'id-at-pseudonym': + case 'pseudonym': + return 'id-at-pseudonym'; + case 'id-at-title': + case 'title': + return 'id-at-title'; + case 'id-at-description': + case 'description': + return 'id-at-description'; + case 'id-at-role': + case 'role': + return 'id-at-role'; + case 'id-at-uniqueidentifier': + case 'uniqueidentifier': + case 'x500uniqueidentifier': + return 'id-at-uniqueIdentifier'; + case 'postaladdress': + case 'id-at-postaladdress': + return 'id-at-postalAddress'; + default: + return false; + } + } + + /** + * Set a Distinguished Name property + * + * @param string $propName + * @param mixed $propValue + * @param string $type optional + * @access public + * @return bool + */ + function setDNProp($propName, $propValue, $type = 'utf8String') + { + if (empty($this->dn)) { + $this->dn = array('rdnSequence' => array()); + } + + if (($propName = $this->_translateDNProp($propName)) === false) { + return false; + } + + foreach ((array) $propValue as $v) { + if (!is_array($v) && isset($type)) { + $v = array($type => $v); + } + $this->dn['rdnSequence'][] = array( + array( + 'type' => $propName, + 'value'=> $v + ) + ); + } + + return true; + } + + /** + * Remove Distinguished Name properties + * + * @param string $propName + * @access public + */ + function removeDNProp($propName) + { + if (empty($this->dn)) { + return; + } + + if (($propName = $this->_translateDNProp($propName)) === false) { + return; + } + + $dn = &$this->dn['rdnSequence']; + $size = count($dn); + for ($i = 0; $i < $size; $i++) { + if ($dn[$i][0]['type'] == $propName) { + unset($dn[$i]); + } + } + + $dn = array_values($dn); + // fix for https://bugs.php.net/75433 affecting PHP 7.2 + if (!isset($dn[0])) { + $dn = array_splice($dn, 0, 0); + } + } + + /** + * Get Distinguished Name properties + * + * @param string $propName + * @param array $dn optional + * @param bool $withType optional + * @return mixed + * @access public + */ + function getDNProp($propName, $dn = null, $withType = false) + { + if (!isset($dn)) { + $dn = $this->dn; + } + + if (empty($dn)) { + return false; + } + + if (($propName = $this->_translateDNProp($propName)) === false) { + return false; + } + + $asn1 = new ASN1(); + $asn1->loadOIDs($this->oids); + $filters = array(); + $filters['value'] = array('type' => ASN1::TYPE_UTF8_STRING); + $asn1->loadFilters($filters); + $this->_mapOutDNs($dn, 'rdnSequence', $asn1); + $dn = $dn['rdnSequence']; + $result = array(); + for ($i = 0; $i < count($dn); $i++) { + if ($dn[$i][0]['type'] == $propName) { + $v = $dn[$i][0]['value']; + if (!$withType) { + if (is_array($v)) { + foreach ($v as $type => $s) { + $type = array_search($type, $asn1->ANYmap, true); + if ($type !== false && isset($asn1->stringTypeSize[$type])) { + $s = $asn1->convert($s, $type); + if ($s !== false) { + $v = $s; + break; + } + } + } + if (is_array($v)) { + $v = array_pop($v); // Always strip data type. + } + } elseif (is_object($v) && $v instanceof Element) { + $map = $this->_getMapping($propName); + if (!is_bool($map)) { + $decoded = $asn1->decodeBER($v); + $v = $asn1->asn1map($decoded[0], $map); + } + } + } + $result[] = $v; + } + } + + return $result; + } + + /** + * Set a Distinguished Name + * + * @param mixed $dn + * @param bool $merge optional + * @param string $type optional + * @access public + * @return bool + */ + function setDN($dn, $merge = false, $type = 'utf8String') + { + if (!$merge) { + $this->dn = null; + } + + if (is_array($dn)) { + if (isset($dn['rdnSequence'])) { + $this->dn = $dn; // No merge here. + return true; + } + + // handles stuff generated by openssl_x509_parse() + foreach ($dn as $prop => $value) { + if (!$this->setDNProp($prop, $value, $type)) { + return false; + } + } + return true; + } + + // handles everything else + $results = preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=|postalAddress=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE); + for ($i = 1; $i < count($results); $i+=2) { + $prop = trim($results[$i], ', =/'); + $value = $results[$i + 1]; + if (!$this->setDNProp($prop, $value, $type)) { + return false; + } + } + + return true; + } + + /** + * Get the Distinguished Name for a certificates subject + * + * @param mixed $format optional + * @param array $dn optional + * @access public + * @return bool + */ + function getDN($format = self::DN_ARRAY, $dn = null) + { + if (!isset($dn)) { + $dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn; + } + + switch ((int) $format) { + case self::DN_ARRAY: + return $dn; + case self::DN_ASN1: + $asn1 = new ASN1(); + $asn1->loadOIDs($this->oids); + $filters = array(); + $filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING); + $asn1->loadFilters($filters); + $this->_mapOutDNs($dn, 'rdnSequence', $asn1); + return $asn1->encodeDER($dn, $this->Name); + case self::DN_CANON: + // No SEQUENCE around RDNs and all string values normalized as + // trimmed lowercase UTF-8 with all spacing as one blank. + // constructed RDNs will not be canonicalized + $asn1 = new ASN1(); + $asn1->loadOIDs($this->oids); + $filters = array(); + $filters['value'] = array('type' => ASN1::TYPE_UTF8_STRING); + $asn1->loadFilters($filters); + $result = ''; + $this->_mapOutDNs($dn, 'rdnSequence', $asn1); + foreach ($dn['rdnSequence'] as $rdn) { + foreach ($rdn as $i => $attr) { + $attr = &$rdn[$i]; + if (is_array($attr['value'])) { + foreach ($attr['value'] as $type => $v) { + $type = array_search($type, $asn1->ANYmap, true); + if ($type !== false && isset($asn1->stringTypeSize[$type])) { + $v = $asn1->convert($v, $type); + if ($v !== false) { + $v = preg_replace('/\s+/', ' ', $v); + $attr['value'] = strtolower(trim($v)); + break; + } + } + } + } + } + $result .= $asn1->encodeDER($rdn, $this->RelativeDistinguishedName); + } + return $result; + case self::DN_HASH: + $dn = $this->getDN(self::DN_CANON, $dn); + $hash = new Hash('sha1'); + $hash = $hash->hash($dn); + extract(unpack('Vhash', $hash)); + return strtolower(bin2hex(pack('N', $hash))); + } + + // Default is to return a string. + $start = true; + $output = ''; + + $result = array(); + $asn1 = new ASN1(); + $asn1->loadOIDs($this->oids); + $filters = array(); + $filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING); + $asn1->loadFilters($filters); + $this->_mapOutDNs($dn, 'rdnSequence', $asn1); + + foreach ($dn['rdnSequence'] as $field) { + $prop = $field[0]['type']; + $value = $field[0]['value']; + + $delim = ', '; + switch ($prop) { + case 'id-at-countryName': + $desc = 'C'; + break; + case 'id-at-stateOrProvinceName': + $desc = 'ST'; + break; + case 'id-at-organizationName': + $desc = 'O'; + break; + case 'id-at-organizationalUnitName': + $desc = 'OU'; + break; + case 'id-at-commonName': + $desc = 'CN'; + break; + case 'id-at-localityName': + $desc = 'L'; + break; + case 'id-at-surname': + $desc = 'SN'; + break; + case 'id-at-uniqueIdentifier': + $delim = '/'; + $desc = 'x500UniqueIdentifier'; + break; + case 'id-at-postalAddress': + $delim = '/'; + $desc = 'postalAddress'; + break; + default: + $delim = '/'; + $desc = preg_replace('#.+-([^-]+)$#', '$1', $prop); + } + + if (!$start) { + $output.= $delim; + } + if (is_array($value)) { + foreach ($value as $type => $v) { + $type = array_search($type, $asn1->ANYmap, true); + if ($type !== false && isset($asn1->stringTypeSize[$type])) { + $v = $asn1->convert($v, $type); + if ($v !== false) { + $value = $v; + break; + } + } + } + if (is_array($value)) { + $value = array_pop($value); // Always strip data type. + } + } elseif (is_object($value) && $value instanceof Element) { + $callback = function ($x) { + return "\x" . bin2hex($x[0]); + }; + $value = strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback, $value->element)); + } + $output.= $desc . '=' . $value; + $result[$desc] = isset($result[$desc]) ? + array_merge((array) $result[$desc], array($value)) : + $value; + $start = false; + } + + return $format == self::DN_OPENSSL ? $result : $output; + } + + /** + * Get the Distinguished Name for a certificate/crl issuer + * + * @param int $format optional + * @access public + * @return mixed + */ + function getIssuerDN($format = self::DN_ARRAY) + { + switch (true) { + case !isset($this->currentCert) || !is_array($this->currentCert): + break; + case isset($this->currentCert['tbsCertificate']): + return $this->getDN($format, $this->currentCert['tbsCertificate']['issuer']); + case isset($this->currentCert['tbsCertList']): + return $this->getDN($format, $this->currentCert['tbsCertList']['issuer']); + } + + return false; + } + + /** + * Get the Distinguished Name for a certificate/csr subject + * Alias of getDN() + * + * @param int $format optional + * @access public + * @return mixed + */ + function getSubjectDN($format = self::DN_ARRAY) + { + switch (true) { + case !empty($this->dn): + return $this->getDN($format); + case !isset($this->currentCert) || !is_array($this->currentCert): + break; + case isset($this->currentCert['tbsCertificate']): + return $this->getDN($format, $this->currentCert['tbsCertificate']['subject']); + case isset($this->currentCert['certificationRequestInfo']): + return $this->getDN($format, $this->currentCert['certificationRequestInfo']['subject']); + } + + return false; + } + + /** + * Get an individual Distinguished Name property for a certificate/crl issuer + * + * @param string $propName + * @param bool $withType optional + * @access public + * @return mixed + */ + function getIssuerDNProp($propName, $withType = false) + { + switch (true) { + case !isset($this->currentCert) || !is_array($this->currentCert): + break; + case isset($this->currentCert['tbsCertificate']): + return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['issuer'], $withType); + case isset($this->currentCert['tbsCertList']): + return $this->getDNProp($propName, $this->currentCert['tbsCertList']['issuer'], $withType); + } + + return false; + } + + /** + * Get an individual Distinguished Name property for a certificate/csr subject + * + * @param string $propName + * @param bool $withType optional + * @access public + * @return mixed + */ + function getSubjectDNProp($propName, $withType = false) + { + switch (true) { + case !empty($this->dn): + return $this->getDNProp($propName, null, $withType); + case !isset($this->currentCert) || !is_array($this->currentCert): + break; + case isset($this->currentCert['tbsCertificate']): + return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['subject'], $withType); + case isset($this->currentCert['certificationRequestInfo']): + return $this->getDNProp($propName, $this->currentCert['certificationRequestInfo']['subject'], $withType); + } + + return false; + } + + /** + * Get the certificate chain for the current cert + * + * @access public + * @return mixed + */ + function getChain() + { + $chain = array($this->currentCert); + + if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { + return false; + } + if (empty($this->CAs)) { + return $chain; + } + while (true) { + $currentCert = $chain[count($chain) - 1]; + for ($i = 0; $i < count($this->CAs); $i++) { + $ca = $this->CAs[$i]; + if ($currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) { + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier', $currentCert); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); + switch (true) { + case !is_array($authorityKey): + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + if ($currentCert === $ca) { + break 3; + } + $chain[] = $ca; + break 2; + } + } + } + if ($i == count($this->CAs)) { + break; + } + } + foreach ($chain as $key => $value) { + $chain[$key] = new X509(); + $chain[$key]->loadX509($value); + } + return $chain; + } + + /** + * Set public key + * + * Key needs to be a \phpseclib\Crypt\RSA object + * + * @param object $key + * @access public + * @return bool + */ + function setPublicKey($key) + { + $key->setPublicKey(); + $this->publicKey = $key; + } + + /** + * Set private key + * + * Key needs to be a \phpseclib\Crypt\RSA object + * + * @param object $key + * @access public + */ + function setPrivateKey($key) + { + $this->privateKey = $key; + } + + /** + * Set challenge + * + * Used for SPKAC CSR's + * + * @param string $challenge + * @access public + */ + function setChallenge($challenge) + { + $this->challenge = $challenge; + } + + /** + * Gets the public key + * + * Returns a \phpseclib\Crypt\RSA object or a false. + * + * @access public + * @return mixed + */ + function getPublicKey() + { + if (isset($this->publicKey)) { + return $this->publicKey; + } + + if (isset($this->currentCert) && is_array($this->currentCert)) { + foreach (array('tbsCertificate/subjectPublicKeyInfo', 'certificationRequestInfo/subjectPKInfo') as $path) { + $keyinfo = $this->_subArray($this->currentCert, $path); + if (!empty($keyinfo)) { + break; + } + } + } + if (empty($keyinfo)) { + return false; + } + + $key = $keyinfo['subjectPublicKey']; + + switch ($keyinfo['algorithm']['algorithm']) { + case 'rsaEncryption': + $publicKey = new RSA(); + $publicKey->loadKey($key); + $publicKey->setPublicKey(); + break; + default: + return false; + } + + return $publicKey; + } + + /** + * Load a Certificate Signing Request + * + * @param string|array $csr + * @param int $mode + * @access public + * @return mixed + */ + function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT) + { + if (is_array($csr) && isset($csr['certificationRequestInfo'])) { + unset($this->currentCert); + unset($this->currentKeyIdentifier); + unset($this->signatureSubject); + $this->dn = $csr['certificationRequestInfo']['subject']; + if (!isset($this->dn)) { + return false; + } + + $this->currentCert = $csr; + return $csr; + } + + // see http://tools.ietf.org/html/rfc2986 + + $asn1 = new ASN1(); + + if ($mode != self::FORMAT_DER) { + $newcsr = $this->_extractBER($csr); + if ($mode == self::FORMAT_PEM && $csr == $newcsr) { + return false; + } + $csr = $newcsr; + } + $orig = $csr; + + if ($csr === false) { + $this->currentCert = false; + return false; + } + + $asn1->loadOIDs($this->oids); + $decoded = $asn1->decodeBER($csr); + + if (empty($decoded)) { + $this->currentCert = false; + return false; + } + + $csr = $asn1->asn1map($decoded[0], $this->CertificationRequest); + if (!isset($csr) || $csr === false) { + $this->currentCert = false; + return false; + } + + $this->_mapInAttributes($csr, 'certificationRequestInfo/attributes', $asn1); + $this->_mapInDNs($csr, 'certificationRequestInfo/subject/rdnSequence', $asn1); + + $this->dn = $csr['certificationRequestInfo']['subject']; + + $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); + + $algorithm = &$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm']; + $key = &$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']; + $key = $this->_reformatKey($algorithm, $key); + + switch ($algorithm) { + case 'rsaEncryption': + $this->publicKey = new RSA(); + $this->publicKey->loadKey($key); + $this->publicKey->setPublicKey(); + break; + default: + $this->publicKey = null; + } + + $this->currentKeyIdentifier = null; + $this->currentCert = $csr; + + return $csr; + } + + /** + * Save CSR request + * + * @param array $csr + * @param int $format optional + * @access public + * @return string + */ + function saveCSR($csr, $format = self::FORMAT_PEM) + { + if (!is_array($csr) || !isset($csr['certificationRequestInfo'])) { + return false; + } + + switch (true) { + case !($algorithm = $this->_subArray($csr, 'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')): + case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']): + break; + default: + switch ($algorithm) { + case 'rsaEncryption': + $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] + = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']))); + $csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['parameters'] = null; + $csr['signatureAlgorithm']['parameters'] = null; + $csr['certificationRequestInfo']['signature']['parameters'] = null; + } + } + + $asn1 = new ASN1(); + + $asn1->loadOIDs($this->oids); + + $filters = array(); + $filters['certificationRequestInfo']['subject']['rdnSequence']['value'] + = array('type' => ASN1::TYPE_UTF8_STRING); + + $asn1->loadFilters($filters); + + $this->_mapOutDNs($csr, 'certificationRequestInfo/subject/rdnSequence', $asn1); + $this->_mapOutAttributes($csr, 'certificationRequestInfo/attributes', $asn1); + $csr = $asn1->encodeDER($csr, $this->CertificationRequest); + + switch ($format) { + case self::FORMAT_DER: + return $csr; + // case self::FORMAT_PEM: + default: + return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----'; + } + } + + /** + * Load a SPKAC CSR + * + * SPKAC's are produced by the HTML5 keygen element: + * + * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen + * + * @param string|array $spkac + * @access public + * @return mixed + */ + function loadSPKAC($spkac) + { + if (is_array($spkac) && isset($spkac['publicKeyAndChallenge'])) { + unset($this->currentCert); + unset($this->currentKeyIdentifier); + unset($this->signatureSubject); + $this->currentCert = $spkac; + return $spkac; + } + + // see http://www.w3.org/html/wg/drafts/html/master/forms.html#signedpublickeyandchallenge + + $asn1 = new ASN1(); + + // OpenSSL produces SPKAC's that are preceded by the string SPKAC= + $temp = preg_replace('#(?:SPKAC=)|[ \r\n\\\]#', '', $spkac); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + if ($temp != false) { + $spkac = $temp; + } + $orig = $spkac; + + if ($spkac === false) { + $this->currentCert = false; + return false; + } + + $asn1->loadOIDs($this->oids); + $decoded = $asn1->decodeBER($spkac); + + if (empty($decoded)) { + $this->currentCert = false; + return false; + } + + $spkac = $asn1->asn1map($decoded[0], $this->SignedPublicKeyAndChallenge); + + if (!isset($spkac) || $spkac === false) { + $this->currentCert = false; + return false; + } + + $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); + + $algorithm = &$spkac['publicKeyAndChallenge']['spki']['algorithm']['algorithm']; + $key = &$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']; + $key = $this->_reformatKey($algorithm, $key); + + switch ($algorithm) { + case 'rsaEncryption': + $this->publicKey = new RSA(); + $this->publicKey->loadKey($key); + $this->publicKey->setPublicKey(); + break; + default: + $this->publicKey = null; + } + + $this->currentKeyIdentifier = null; + $this->currentCert = $spkac; + + return $spkac; + } + + /** + * Save a SPKAC CSR request + * + * @param string|array $spkac + * @param int $format optional + * @access public + * @return string + */ + function saveSPKAC($spkac, $format = self::FORMAT_PEM) + { + if (!is_array($spkac) || !isset($spkac['publicKeyAndChallenge'])) { + return false; + } + + $algorithm = $this->_subArray($spkac, 'publicKeyAndChallenge/spki/algorithm/algorithm'); + switch (true) { + case !$algorithm: + case is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']): + break; + default: + switch ($algorithm) { + case 'rsaEncryption': + $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'] + = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']))); + } + } + + $asn1 = new ASN1(); + + $asn1->loadOIDs($this->oids); + $spkac = $asn1->encodeDER($spkac, $this->SignedPublicKeyAndChallenge); + + switch ($format) { + case self::FORMAT_DER: + return $spkac; + // case self::FORMAT_PEM: + default: + // OpenSSL's implementation of SPKAC requires the SPKAC be preceded by SPKAC= and since there are pretty much + // no other SPKAC decoders phpseclib will use that same format + return 'SPKAC=' . base64_encode($spkac); + } + } + + /** + * Load a Certificate Revocation List + * + * @param string $crl + * @param int $mode + * @access public + * @return mixed + */ + function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT) + { + if (is_array($crl) && isset($crl['tbsCertList'])) { + $this->currentCert = $crl; + unset($this->signatureSubject); + return $crl; + } + + $asn1 = new ASN1(); + + if ($mode != self::FORMAT_DER) { + $newcrl = $this->_extractBER($crl); + if ($mode == self::FORMAT_PEM && $crl == $newcrl) { + return false; + } + $crl = $newcrl; + } + $orig = $crl; + + if ($crl === false) { + $this->currentCert = false; + return false; + } + + $asn1->loadOIDs($this->oids); + $decoded = $asn1->decodeBER($crl); + + if (empty($decoded)) { + $this->currentCert = false; + return false; + } + + $crl = $asn1->asn1map($decoded[0], $this->CertificateList); + if (!isset($crl) || $crl === false) { + $this->currentCert = false; + return false; + } + + $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); + + $this->_mapInDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1); + if ($this->_isSubArrayValid($crl, 'tbsCertList/crlExtensions')) { + $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1); + } + if ($this->_isSubArrayValid($crl, 'tbsCertList/revokedCertificates')) { + $rclist_ref = &$this->_subArrayUnchecked($crl, 'tbsCertList/revokedCertificates'); + if ($rclist_ref) { + $rclist = $crl['tbsCertList']['revokedCertificates']; + foreach ($rclist as $i => $extension) { + if ($this->_isSubArrayValid($rclist, "$i/crlEntryExtensions", $asn1)) { + $this->_mapInExtensions($rclist_ref, "$i/crlEntryExtensions", $asn1); + } + } + } + } + + $this->currentKeyIdentifier = null; + $this->currentCert = $crl; + + return $crl; + } + + /** + * Save Certificate Revocation List. + * + * @param array $crl + * @param int $format optional + * @access public + * @return string + */ + function saveCRL($crl, $format = self::FORMAT_PEM) + { + if (!is_array($crl) || !isset($crl['tbsCertList'])) { + return false; + } + + $asn1 = new ASN1(); + + $asn1->loadOIDs($this->oids); + + $filters = array(); + $filters['tbsCertList']['issuer']['rdnSequence']['value'] + = array('type' => ASN1::TYPE_UTF8_STRING); + $filters['tbsCertList']['signature']['parameters'] + = array('type' => ASN1::TYPE_UTF8_STRING); + $filters['signatureAlgorithm']['parameters'] + = array('type' => ASN1::TYPE_UTF8_STRING); + + if (empty($crl['tbsCertList']['signature']['parameters'])) { + $filters['tbsCertList']['signature']['parameters'] + = array('type' => ASN1::TYPE_NULL); + } + + if (empty($crl['signatureAlgorithm']['parameters'])) { + $filters['signatureAlgorithm']['parameters'] + = array('type' => ASN1::TYPE_NULL); + } + + $asn1->loadFilters($filters); + + $this->_mapOutDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1); + $this->_mapOutExtensions($crl, 'tbsCertList/crlExtensions', $asn1); + $rclist = &$this->_subArray($crl, 'tbsCertList/revokedCertificates'); + if (is_array($rclist)) { + foreach ($rclist as $i => $extension) { + $this->_mapOutExtensions($rclist, "$i/crlEntryExtensions", $asn1); + } + } + + $crl = $asn1->encodeDER($crl, $this->CertificateList); + + switch ($format) { + case self::FORMAT_DER: + return $crl; + // case self::FORMAT_PEM: + default: + return "-----BEGIN X509 CRL-----\r\n" . chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----'; + } + } + + /** + * Helper function to build a time field according to RFC 3280 section + * - 4.1.2.5 Validity + * - 5.1.2.4 This Update + * - 5.1.2.5 Next Update + * - 5.1.2.6 Revoked Certificates + * by choosing utcTime iff year of date given is before 2050 and generalTime else. + * + * @param string $date in format date('D, d M Y H:i:s O') + * @access private + * @return array + */ + function _timeField($date) + { + if ($date instanceof Element) { + return $date; + } + $dateObj = new DateTime($date, new DateTimeZone('GMT')); + $year = $dateObj->format('Y'); // the same way ASN1.php parses this + if ($year < 2050) { + return array('utcTime' => $date); + } else { + return array('generalTime' => $date); + } + } + + /** + * Sign an X.509 certificate + * + * $issuer's private key needs to be loaded. + * $subject can be either an existing X.509 cert (if you want to resign it), + * a CSR or something with the DN and public key explicitly set. + * + * @param \phpseclib\File\X509 $issuer + * @param \phpseclib\File\X509 $subject + * @param string $signatureAlgorithm optional + * @access public + * @return mixed + */ + function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') + { + if (!is_object($issuer->privateKey) || empty($issuer->dn)) { + return false; + } + + if (isset($subject->publicKey) && !($subjectPublicKey = $subject->_formatSubjectPublicKey())) { + return false; + } + + $currentCert = isset($this->currentCert) ? $this->currentCert : null; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null; + + if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) { + $this->currentCert = $subject->currentCert; + $this->currentCert['tbsCertificate']['signature']['algorithm'] = $signatureAlgorithm; + $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; + + if (!empty($this->startDate)) { + $this->currentCert['tbsCertificate']['validity']['notBefore'] = $this->_timeField($this->startDate); + } + if (!empty($this->endDate)) { + $this->currentCert['tbsCertificate']['validity']['notAfter'] = $this->_timeField($this->endDate); + } + if (!empty($this->serialNumber)) { + $this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber; + } + if (!empty($subject->dn)) { + $this->currentCert['tbsCertificate']['subject'] = $subject->dn; + } + if (!empty($subject->publicKey)) { + $this->currentCert['tbsCertificate']['subjectPublicKeyInfo'] = $subjectPublicKey; + } + $this->removeExtension('id-ce-authorityKeyIdentifier'); + if (isset($subject->domains)) { + $this->removeExtension('id-ce-subjectAltName'); + } + } elseif (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) { + return false; + } else { + if (!isset($subject->publicKey)) { + return false; + } + + $startDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get())); + $startDate = !empty($this->startDate) ? $this->startDate : $startDate->format('D, d M Y H:i:s O'); + + $endDate = new DateTime('+1 year', new DateTimeZone(@date_default_timezone_get())); + $endDate = !empty($this->endDate) ? $this->endDate : $endDate->format('D, d M Y H:i:s O'); + + /* "The serial number MUST be a positive integer" + "Conforming CAs MUST NOT use serialNumber values longer than 20 octets." + -- https://tools.ietf.org/html/rfc5280#section-4.1.2.2 + + for the integer to be positive the leading bit needs to be 0 hence the + application of a bitmap + */ + $serialNumber = !empty($this->serialNumber) ? + $this->serialNumber : + new BigInteger(Random::string(20) & ("\x7F" . str_repeat("\xFF", 19)), 256); + + $this->currentCert = array( + 'tbsCertificate' => + array( + 'version' => 'v3', + 'serialNumber' => $serialNumber, // $this->setSerialNumber() + 'signature' => array('algorithm' => $signatureAlgorithm), + 'issuer' => false, // this is going to be overwritten later + 'validity' => array( + 'notBefore' => $this->_timeField($startDate), // $this->setStartDate() + 'notAfter' => $this->_timeField($endDate) // $this->setEndDate() + ), + 'subject' => $subject->dn, + 'subjectPublicKeyInfo' => $subjectPublicKey + ), + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later + ); + + // Copy extensions from CSR. + $csrexts = $subject->getAttribute('pkcs-9-at-extensionRequest', 0); + + if (!empty($csrexts)) { + $this->currentCert['tbsCertificate']['extensions'] = $csrexts; + } + } + + $this->currentCert['tbsCertificate']['issuer'] = $issuer->dn; + + if (isset($issuer->currentKeyIdentifier)) { + $this->setExtension('id-ce-authorityKeyIdentifier', array( + //'authorityCertIssuer' => array( + // array( + // 'directoryName' => $issuer->dn + // ) + //), + 'keyIdentifier' => $issuer->currentKeyIdentifier + )); + //$extensions = &$this->currentCert['tbsCertificate']['extensions']; + //if (isset($issuer->serialNumber)) { + // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber; + //} + //unset($extensions); + } + + if (isset($subject->currentKeyIdentifier)) { + $this->setExtension('id-ce-subjectKeyIdentifier', $subject->currentKeyIdentifier); + } + + $altName = array(); + + if (isset($subject->domains) && count($subject->domains)) { + $altName = array_map(array('\phpseclib\File\X509', '_dnsName'), $subject->domains); + } + + if (isset($subject->ipAddresses) && count($subject->ipAddresses)) { + // should an IP address appear as the CN if no domain name is specified? idk + //$ips = count($subject->domains) ? $subject->ipAddresses : array_slice($subject->ipAddresses, 1); + $ipAddresses = array(); + foreach ($subject->ipAddresses as $ipAddress) { + $encoded = $subject->_ipAddress($ipAddress); + if ($encoded !== false) { + $ipAddresses[] = $encoded; + } + } + if (count($ipAddresses)) { + $altName = array_merge($altName, $ipAddresses); + } + } + + if (!empty($altName)) { + $this->setExtension('id-ce-subjectAltName', $altName); + } + + if ($this->caFlag) { + $keyUsage = $this->getExtension('id-ce-keyUsage'); + if (!$keyUsage) { + $keyUsage = array(); + } + + $this->setExtension( + 'id-ce-keyUsage', + array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign')))) + ); + + $basicConstraints = $this->getExtension('id-ce-basicConstraints'); + if (!$basicConstraints) { + $basicConstraints = array(); + } + + $this->setExtension( + 'id-ce-basicConstraints', + array_unique(array_merge(array('cA' => true), $basicConstraints)), + true + ); + + if (!isset($subject->currentKeyIdentifier)) { + $this->setExtension('id-ce-subjectKeyIdentifier', base64_encode($this->computeKeyIdentifier($this->currentCert)), false, false); + } + } + + // resync $this->signatureSubject + // save $tbsCertificate in case there are any \phpseclib\File\ASN1\Element objects in it + $tbsCertificate = $this->currentCert['tbsCertificate']; + $this->loadX509($this->saveX509($this->currentCert)); + + $result = $this->_sign($issuer->privateKey, $signatureAlgorithm); + $result['tbsCertificate'] = $tbsCertificate; + + $this->currentCert = $currentCert; + $this->signatureSubject = $signatureSubject; + + return $result; + } + + /** + * Sign a CSR + * + * @access public + * @return mixed + */ + function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption') + { + if (!is_object($this->privateKey) || empty($this->dn)) { + return false; + } + + $origPublicKey = $this->publicKey; + $class = get_class($this->privateKey); + $this->publicKey = new $class(); + $this->publicKey->loadKey($this->privateKey->getPublicKey()); + $this->publicKey->setPublicKey(); + if (!($publicKey = $this->_formatSubjectPublicKey())) { + return false; + } + $this->publicKey = $origPublicKey; + + $currentCert = isset($this->currentCert) ? $this->currentCert : null; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null; + + if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) { + $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; + if (!empty($this->dn)) { + $this->currentCert['certificationRequestInfo']['subject'] = $this->dn; + } + $this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey; + } else { + $this->currentCert = array( + 'certificationRequestInfo' => + array( + 'version' => 'v1', + 'subject' => $this->dn, + 'subjectPKInfo' => $publicKey + ), + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later + ); + } + + // resync $this->signatureSubject + // save $certificationRequestInfo in case there are any \phpseclib\File\ASN1\Element objects in it + $certificationRequestInfo = $this->currentCert['certificationRequestInfo']; + $this->loadCSR($this->saveCSR($this->currentCert)); + + $result = $this->_sign($this->privateKey, $signatureAlgorithm); + $result['certificationRequestInfo'] = $certificationRequestInfo; + + $this->currentCert = $currentCert; + $this->signatureSubject = $signatureSubject; + + return $result; + } + + /** + * Sign a SPKAC + * + * @access public + * @return mixed + */ + function signSPKAC($signatureAlgorithm = 'sha1WithRSAEncryption') + { + if (!is_object($this->privateKey)) { + return false; + } + + $origPublicKey = $this->publicKey; + $class = get_class($this->privateKey); + $this->publicKey = new $class(); + $this->publicKey->loadKey($this->privateKey->getPublicKey()); + $this->publicKey->setPublicKey(); + $publicKey = $this->_formatSubjectPublicKey(); + if (!$publicKey) { + return false; + } + $this->publicKey = $origPublicKey; + + $currentCert = isset($this->currentCert) ? $this->currentCert : null; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null; + + // re-signing a SPKAC seems silly but since everything else supports re-signing why not? + if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['publicKeyAndChallenge'])) { + $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; + $this->currentCert['publicKeyAndChallenge']['spki'] = $publicKey; + if (!empty($this->challenge)) { + // the bitwise AND ensures that the output is a valid IA5String + $this->currentCert['publicKeyAndChallenge']['challenge'] = $this->challenge & str_repeat("\x7F", strlen($this->challenge)); + } + } else { + $this->currentCert = array( + 'publicKeyAndChallenge' => + array( + 'spki' => $publicKey, + // quoting , + // "A challenge string that is submitted along with the public key. Defaults to an empty string if not specified." + // both Firefox and OpenSSL ("openssl spkac -key private.key") behave this way + // we could alternatively do this instead if we ignored the specs: + // Random::string(8) & str_repeat("\x7F", 8) + 'challenge' => !empty($this->challenge) ? $this->challenge : '' + ), + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later + ); + } + + // resync $this->signatureSubject + // save $publicKeyAndChallenge in case there are any \phpseclib\File\ASN1\Element objects in it + $publicKeyAndChallenge = $this->currentCert['publicKeyAndChallenge']; + $this->loadSPKAC($this->saveSPKAC($this->currentCert)); + + $result = $this->_sign($this->privateKey, $signatureAlgorithm); + $result['publicKeyAndChallenge'] = $publicKeyAndChallenge; + + $this->currentCert = $currentCert; + $this->signatureSubject = $signatureSubject; + + return $result; + } + + /** + * Sign a CRL + * + * $issuer's private key needs to be loaded. + * + * @param \phpseclib\File\X509 $issuer + * @param \phpseclib\File\X509 $crl + * @param string $signatureAlgorithm optional + * @access public + * @return mixed + */ + function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') + { + if (!is_object($issuer->privateKey) || empty($issuer->dn)) { + return false; + } + + $currentCert = isset($this->currentCert) ? $this->currentCert : null; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; + + $thisUpdate = new DateTime('now', new DateTimeZone(@date_default_timezone_get())); + $thisUpdate = !empty($this->startDate) ? $this->startDate : $thisUpdate->format('D, d M Y H:i:s O'); + + if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) { + $this->currentCert = $crl->currentCert; + $this->currentCert['tbsCertList']['signature']['algorithm'] = $signatureAlgorithm; + $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; + } else { + $this->currentCert = array( + 'tbsCertList' => + array( + 'version' => 'v2', + 'signature' => array('algorithm' => $signatureAlgorithm), + 'issuer' => false, // this is going to be overwritten later + 'thisUpdate' => $this->_timeField($thisUpdate) // $this->setStartDate() + ), + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later + ); + } + + $tbsCertList = &$this->currentCert['tbsCertList']; + $tbsCertList['issuer'] = $issuer->dn; + $tbsCertList['thisUpdate'] = $this->_timeField($thisUpdate); + + if (!empty($this->endDate)) { + $tbsCertList['nextUpdate'] = $this->_timeField($this->endDate); // $this->setEndDate() + } else { + unset($tbsCertList['nextUpdate']); + } + + if (!empty($this->serialNumber)) { + $crlNumber = $this->serialNumber; + } else { + $crlNumber = $this->getExtension('id-ce-cRLNumber'); + // "The CRL number is a non-critical CRL extension that conveys a + // monotonically increasing sequence number for a given CRL scope and + // CRL issuer. This extension allows users to easily determine when a + // particular CRL supersedes another CRL." + // -- https://tools.ietf.org/html/rfc5280#section-5.2.3 + $crlNumber = $crlNumber !== false ? $crlNumber->add(new BigInteger(1)) : null; + } + + $this->removeExtension('id-ce-authorityKeyIdentifier'); + $this->removeExtension('id-ce-issuerAltName'); + + // Be sure version >= v2 if some extension found. + $version = isset($tbsCertList['version']) ? $tbsCertList['version'] : 0; + if (!$version) { + if (!empty($tbsCertList['crlExtensions'])) { + $version = 1; // v2. + } elseif (!empty($tbsCertList['revokedCertificates'])) { + foreach ($tbsCertList['revokedCertificates'] as $cert) { + if (!empty($cert['crlEntryExtensions'])) { + $version = 1; // v2. + } + } + } + + if ($version) { + $tbsCertList['version'] = $version; + } + } + + // Store additional extensions. + if (!empty($tbsCertList['version'])) { // At least v2. + if (!empty($crlNumber)) { + $this->setExtension('id-ce-cRLNumber', $crlNumber); + } + + if (isset($issuer->currentKeyIdentifier)) { + $this->setExtension('id-ce-authorityKeyIdentifier', array( + //'authorityCertIssuer' => array( + // array( + // 'directoryName' => $issuer->dn + // ) + //), + 'keyIdentifier' => $issuer->currentKeyIdentifier + )); + //$extensions = &$tbsCertList['crlExtensions']; + //if (isset($issuer->serialNumber)) { + // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber; + //} + //unset($extensions); + } + + $issuerAltName = $this->getExtension('id-ce-subjectAltName', $issuer->currentCert); + + if ($issuerAltName !== false) { + $this->setExtension('id-ce-issuerAltName', $issuerAltName); + } + } + + if (empty($tbsCertList['revokedCertificates'])) { + unset($tbsCertList['revokedCertificates']); + } + + unset($tbsCertList); + + // resync $this->signatureSubject + // save $tbsCertList in case there are any \phpseclib\File\ASN1\Element objects in it + $tbsCertList = $this->currentCert['tbsCertList']; + $this->loadCRL($this->saveCRL($this->currentCert)); + + $result = $this->_sign($issuer->privateKey, $signatureAlgorithm); + $result['tbsCertList'] = $tbsCertList; + + $this->currentCert = $currentCert; + $this->signatureSubject = $signatureSubject; + + return $result; + } + + /** + * X.509 certificate signing helper function. + * + * @param \phpseclib\File\X509 $key + * @param string $signatureAlgorithm + * @access public + * @return mixed + */ + function _sign($key, $signatureAlgorithm) + { + if ($key instanceof RSA) { + switch ($signatureAlgorithm) { + case 'md2WithRSAEncryption': + case 'md5WithRSAEncryption': + case 'sha1WithRSAEncryption': + case 'sha224WithRSAEncryption': + case 'sha256WithRSAEncryption': + case 'sha384WithRSAEncryption': + case 'sha512WithRSAEncryption': + $key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); + $key->setSignatureMode(RSA::SIGNATURE_PKCS1); + + $this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject)); + return $this->currentCert; + } + } + + return false; + } + + /** + * Set certificate start date + * + * @param string $date + * @access public + */ + function setStartDate($date) + { + if (!is_object($date) || !is_a($date, 'DateTime')) { + $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get())); + } + + $this->startDate = $date->format('D, d M Y H:i:s O'); + } + + /** + * Set certificate end date + * + * @param string $date + * @access public + */ + function setEndDate($date) + { + /* + To indicate that a certificate has no well-defined expiration date, + the notAfter SHOULD be assigned the GeneralizedTime value of + 99991231235959Z. + + -- http://tools.ietf.org/html/rfc5280#section-4.1.2.5 + */ + if (strtolower($date) == 'lifetime') { + $temp = '99991231235959Z'; + $asn1 = new ASN1(); + $temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp; + $this->endDate = new Element($temp); + } else { + if (!is_object($date) || !is_a($date, 'DateTime')) { + $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get())); + } + + $this->endDate = $date->format('D, d M Y H:i:s O'); + } + } + + /** + * Set Serial Number + * + * @param string $serial + * @param int $base optional + * @access public + */ + function setSerialNumber($serial, $base = -256) + { + $this->serialNumber = new BigInteger($serial, $base); + } + + /** + * Turns the certificate into a certificate authority + * + * @access public + */ + function makeCA() + { + $this->caFlag = true; + } + + /** + * Check for validity of subarray + * + * This is intended for use in conjunction with _subArrayUnchecked(), + * implementing the checks included in _subArray() but without copying + * a potentially large array by passing its reference by-value to is_array(). + * + * @param array $root + * @param string $path + * @return boolean + * @access private + */ + function _isSubArrayValid($root, $path) + { + if (!is_array($root)) { + return false; + } + + foreach (explode('/', $path) as $i) { + if (!is_array($root)) { + return false; + } + + if (!isset($root[$i])) { + return true; + } + + $root = $root[$i]; + } + + return true; + } + + /** + * Get a reference to a subarray + * + * This variant of _subArray() does no is_array() checking, + * so $root should be checked with _isSubArrayValid() first. + * + * This is here for performance reasons: + * Passing a reference (i.e. $root) by-value (i.e. to is_array()) + * creates a copy. If $root is an especially large array, this is expensive. + * + * @param array $root + * @param string $path absolute path with / as component separator + * @param bool $create optional + * @access private + * @return array|false + */ + function &_subArrayUnchecked(&$root, $path, $create = false) + { + $false = false; + + foreach (explode('/', $path) as $i) { + if (!isset($root[$i])) { + if (!$create) { + return $false; + } + + $root[$i] = array(); + } + + $root = &$root[$i]; + } + + return $root; + } + + /** + * Get a reference to a subarray + * + * @param array $root + * @param string $path absolute path with / as component separator + * @param bool $create optional + * @access private + * @return array|false + */ + function &_subArray(&$root, $path, $create = false) + { + $false = false; + + if (!is_array($root)) { + return $false; + } + + foreach (explode('/', $path) as $i) { + if (!is_array($root)) { + return $false; + } + + if (!isset($root[$i])) { + if (!$create) { + return $false; + } + + $root[$i] = array(); + } + + $root = &$root[$i]; + } + + return $root; + } + + /** + * Get a reference to an extension subarray + * + * @param array $root + * @param string $path optional absolute path with / as component separator + * @param bool $create optional + * @access private + * @return array|false + */ + function &_extensions(&$root, $path = null, $create = false) + { + if (!isset($root)) { + $root = $this->currentCert; + } + + switch (true) { + case !empty($path): + case !is_array($root): + break; + case isset($root['tbsCertificate']): + $path = 'tbsCertificate/extensions'; + break; + case isset($root['tbsCertList']): + $path = 'tbsCertList/crlExtensions'; + break; + case isset($root['certificationRequestInfo']): + $pth = 'certificationRequestInfo/attributes'; + $attributes = &$this->_subArray($root, $pth, $create); + + if (is_array($attributes)) { + foreach ($attributes as $key => $value) { + if ($value['type'] == 'pkcs-9-at-extensionRequest') { + $path = "$pth/$key/value/0"; + break 2; + } + } + if ($create) { + $key = count($attributes); + $attributes[] = array('type' => 'pkcs-9-at-extensionRequest', 'value' => array()); + $path = "$pth/$key/value/0"; + } + } + break; + } + + $extensions = &$this->_subArray($root, $path, $create); + + if (!is_array($extensions)) { + $false = false; + return $false; + } + + return $extensions; + } + + /** + * Remove an Extension + * + * @param string $id + * @param string $path optional + * @access private + * @return bool + */ + function _removeExtension($id, $path = null) + { + $extensions = &$this->_extensions($this->currentCert, $path); + + if (!is_array($extensions)) { + return false; + } + + $result = false; + foreach ($extensions as $key => $value) { + if ($value['extnId'] == $id) { + unset($extensions[$key]); + $result = true; + } + } + + $extensions = array_values($extensions); + // fix for https://bugs.php.net/75433 affecting PHP 7.2 + if (!isset($extensions[0])) { + $extensions = array_splice($extensions, 0, 0); + } + return $result; + } + + /** + * Get an Extension + * + * Returns the extension if it exists and false if not + * + * @param string $id + * @param array $cert optional + * @param string $path optional + * @access private + * @return mixed + */ + function _getExtension($id, $cert = null, $path = null) + { + $extensions = $this->_extensions($cert, $path); + + if (!is_array($extensions)) { + return false; + } + + foreach ($extensions as $key => $value) { + if ($value['extnId'] == $id) { + return $value['extnValue']; + } + } + + return false; + } + + /** + * Returns a list of all extensions in use + * + * @param array $cert optional + * @param string $path optional + * @access private + * @return array + */ + function _getExtensions($cert = null, $path = null) + { + $exts = $this->_extensions($cert, $path); + $extensions = array(); + + if (is_array($exts)) { + foreach ($exts as $extension) { + $extensions[] = $extension['extnId']; + } + } + + return $extensions; + } + + /** + * Set an Extension + * + * @param string $id + * @param mixed $value + * @param bool $critical optional + * @param bool $replace optional + * @param string $path optional + * @access private + * @return bool + */ + function _setExtension($id, $value, $critical = false, $replace = true, $path = null) + { + $extensions = &$this->_extensions($this->currentCert, $path, true); + + if (!is_array($extensions)) { + return false; + } + + $newext = array('extnId' => $id, 'critical' => $critical, 'extnValue' => $value); + + foreach ($extensions as $key => $value) { + if ($value['extnId'] == $id) { + if (!$replace) { + return false; + } + + $extensions[$key] = $newext; + return true; + } + } + + $extensions[] = $newext; + return true; + } + + /** + * Remove a certificate, CSR or CRL Extension + * + * @param string $id + * @access public + * @return bool + */ + function removeExtension($id) + { + return $this->_removeExtension($id); + } + + /** + * Get a certificate, CSR or CRL Extension + * + * Returns the extension if it exists and false if not + * + * @param string $id + * @param array $cert optional + * @access public + * @return mixed + */ + function getExtension($id, $cert = null) + { + return $this->_getExtension($id, $cert); + } + + /** + * Returns a list of all extensions in use in certificate, CSR or CRL + * + * @param array $cert optional + * @access public + * @return array + */ + function getExtensions($cert = null) + { + return $this->_getExtensions($cert); + } + + /** + * Set a certificate, CSR or CRL Extension + * + * @param string $id + * @param mixed $value + * @param bool $critical optional + * @param bool $replace optional + * @access public + * @return bool + */ + function setExtension($id, $value, $critical = false, $replace = true) + { + return $this->_setExtension($id, $value, $critical, $replace); + } + + /** + * Remove a CSR attribute. + * + * @param string $id + * @param int $disposition optional + * @access public + * @return bool + */ + function removeAttribute($id, $disposition = self::ATTR_ALL) + { + $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes'); + + if (!is_array($attributes)) { + return false; + } + + $result = false; + foreach ($attributes as $key => $attribute) { + if ($attribute['type'] == $id) { + $n = count($attribute['value']); + switch (true) { + case $disposition == self::ATTR_APPEND: + case $disposition == self::ATTR_REPLACE: + return false; + case $disposition >= $n: + $disposition -= $n; + break; + case $disposition == self::ATTR_ALL: + case $n == 1: + unset($attributes[$key]); + $result = true; + break; + default: + unset($attributes[$key]['value'][$disposition]); + $attributes[$key]['value'] = array_values($attributes[$key]['value']); + $result = true; + break; + } + if ($result && $disposition != self::ATTR_ALL) { + break; + } + } + } + + $attributes = array_values($attributes); + return $result; + } + + /** + * Get a CSR attribute + * + * Returns the attribute if it exists and false if not + * + * @param string $id + * @param int $disposition optional + * @param array $csr optional + * @access public + * @return mixed + */ + function getAttribute($id, $disposition = self::ATTR_ALL, $csr = null) + { + if (empty($csr)) { + $csr = $this->currentCert; + } + + $attributes = $this->_subArray($csr, 'certificationRequestInfo/attributes'); + + if (!is_array($attributes)) { + return false; + } + + foreach ($attributes as $key => $attribute) { + if ($attribute['type'] == $id) { + $n = count($attribute['value']); + switch (true) { + case $disposition == self::ATTR_APPEND: + case $disposition == self::ATTR_REPLACE: + return false; + case $disposition == self::ATTR_ALL: + return $attribute['value']; + case $disposition >= $n: + $disposition -= $n; + break; + default: + return $attribute['value'][$disposition]; + } + } + } + + return false; + } + + /** + * Returns a list of all CSR attributes in use + * + * @param array $csr optional + * @access public + * @return array + */ + function getAttributes($csr = null) + { + if (empty($csr)) { + $csr = $this->currentCert; + } + + $attributes = $this->_subArray($csr, 'certificationRequestInfo/attributes'); + $attrs = array(); + + if (is_array($attributes)) { + foreach ($attributes as $attribute) { + $attrs[] = $attribute['type']; + } + } + + return $attrs; + } + + /** + * Set a CSR attribute + * + * @param string $id + * @param mixed $value + * @param bool $disposition optional + * @access public + * @return bool + */ + function setAttribute($id, $value, $disposition = self::ATTR_ALL) + { + $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes', true); + + if (!is_array($attributes)) { + return false; + } + + switch ($disposition) { + case self::ATTR_REPLACE: + $disposition = self::ATTR_APPEND; + case self::ATTR_ALL: + $this->removeAttribute($id); + break; + } + + foreach ($attributes as $key => $attribute) { + if ($attribute['type'] == $id) { + $n = count($attribute['value']); + switch (true) { + case $disposition == self::ATTR_APPEND: + $last = $key; + break; + case $disposition >= $n: + $disposition -= $n; + break; + default: + $attributes[$key]['value'][$disposition] = $value; + return true; + } + } + } + + switch (true) { + case $disposition >= 0: + return false; + case isset($last): + $attributes[$last]['value'][] = $value; + break; + default: + $attributes[] = array('type' => $id, 'value' => $disposition == self::ATTR_ALL ? $value: array($value)); + break; + } + + return true; + } + + /** + * Sets the subject key identifier + * + * This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions. + * + * @param string $value + * @access public + */ + function setKeyIdentifier($value) + { + if (empty($value)) { + unset($this->currentKeyIdentifier); + } else { + $this->currentKeyIdentifier = base64_encode($value); + } + } + + /** + * Compute a public key identifier. + * + * Although key identifiers may be set to any unique value, this function + * computes key identifiers from public key according to the two + * recommended methods (4.2.1.2 RFC 3280). + * Highly polymorphic: try to accept all possible forms of key: + * - Key object + * - \phpseclib\File\X509 object with public or private key defined + * - Certificate or CSR array + * - \phpseclib\File\ASN1\Element object + * - PEM or DER string + * + * @param mixed $key optional + * @param int $method optional + * @access public + * @return string binary key identifier + */ + function computeKeyIdentifier($key = null, $method = 1) + { + if (is_null($key)) { + $key = $this; + } + + switch (true) { + case is_string($key): + break; + case is_array($key) && isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']): + return $this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $method); + case is_array($key) && isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']): + return $this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method); + case !is_object($key): + return false; + case $key instanceof Element: + // Assume the element is a bitstring-packed key. + $asn1 = new ASN1(); + $decoded = $asn1->decodeBER($key->element); + if (empty($decoded)) { + return false; + } + $raw = $asn1->asn1map($decoded[0], array('type' => ASN1::TYPE_BIT_STRING)); + if (empty($raw)) { + return false; + } + $raw = base64_decode($raw); + // If the key is private, compute identifier from its corresponding public key. + $key = new RSA(); + if (!$key->loadKey($raw)) { + return false; // Not an unencrypted RSA key. + } + if ($key->getPrivateKey() !== false) { // If private. + return $this->computeKeyIdentifier($key, $method); + } + $key = $raw; // Is a public key. + break; + case $key instanceof X509: + if (isset($key->publicKey)) { + return $this->computeKeyIdentifier($key->publicKey, $method); + } + if (isset($key->privateKey)) { + return $this->computeKeyIdentifier($key->privateKey, $method); + } + if (isset($key->currentCert['tbsCertificate']) || isset($key->currentCert['certificationRequestInfo'])) { + return $this->computeKeyIdentifier($key->currentCert, $method); + } + return false; + default: // Should be a key object (i.e.: \phpseclib\Crypt\RSA). + $key = $key->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1); + break; + } + + // If in PEM format, convert to binary. + $key = $this->_extractBER($key); + + // Now we have the key string: compute its sha-1 sum. + $hash = new Hash('sha1'); + $hash = $hash->hash($key); + + if ($method == 2) { + $hash = substr($hash, -8); + $hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40); + } + + return $hash; + } + + /** + * Format a public key as appropriate + * + * @access private + * @return array + */ + function _formatSubjectPublicKey() + { + if ($this->publicKey instanceof RSA) { + // the following two return statements do the same thing. i dunno.. i just prefer the later for some reason. + // the former is a good example of how to do fuzzing on the public key + //return new Element(base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey()))); + return array( + 'algorithm' => array('algorithm' => 'rsaEncryption'), + 'subjectPublicKey' => $this->publicKey->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1) + ); + } + + return false; + } + + /** + * Set the domain name's which the cert is to be valid for + * + * @access public + * @return array + */ + function setDomain() + { + $this->domains = func_get_args(); + $this->removeDNProp('id-at-commonName'); + $this->setDNProp('id-at-commonName', $this->domains[0]); + } + + /** + * Set the IP Addresses's which the cert is to be valid for + * + * @access public + */ + function setIPAddress() + { + $this->ipAddresses = func_get_args(); + /* + if (!isset($this->domains)) { + $this->removeDNProp('id-at-commonName'); + $this->setDNProp('id-at-commonName', $this->ipAddresses[0]); + } + */ + } + + /** + * Helper function to build domain array + * + * @access private + * @param string $domain + * @return array + */ + function _dnsName($domain) + { + return array('dNSName' => $domain); + } + + /** + * Helper function to build IP Address array + * + * (IPv6 is not currently supported) + * + * @access private + * @param string $address + * @return array + */ + function _iPAddress($address) + { + return array('iPAddress' => $address); + } + + /** + * Get the index of a revoked certificate. + * + * @param array $rclist + * @param string $serial + * @param bool $create optional + * @access private + * @return int|false + */ + function _revokedCertificate(&$rclist, $serial, $create = false) + { + $serial = new BigInteger($serial); + + foreach ($rclist as $i => $rc) { + if (!($serial->compare($rc['userCertificate']))) { + return $i; + } + } + + if (!$create) { + return false; + } + + $i = count($rclist); + $revocationDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get())); + $rclist[] = array('userCertificate' => $serial, + 'revocationDate' => $this->_timeField($revocationDate->format('D, d M Y H:i:s O'))); + return $i; + } + + /** + * Revoke a certificate. + * + * @param string $serial + * @param string $date optional + * @access public + * @return bool + */ + function revoke($serial, $date = null) + { + if (isset($this->currentCert['tbsCertList'])) { + if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) { + if ($this->_revokedCertificate($rclist, $serial) === false) { // If not yet revoked + if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) { + if (!empty($date)) { + $rclist[$i]['revocationDate'] = $this->_timeField($date); + } + + return true; + } + } + } + } + + return false; + } + + /** + * Unrevoke a certificate. + * + * @param string $serial + * @access public + * @return bool + */ + function unrevoke($serial) + { + if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + unset($rclist[$i]); + $rclist = array_values($rclist); + return true; + } + } + + return false; + } + + /** + * Get a revoked certificate. + * + * @param string $serial + * @access public + * @return mixed + */ + function getRevoked($serial) + { + if (is_array($rclist = $this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + return $rclist[$i]; + } + } + + return false; + } + + /** + * List revoked certificates + * + * @param array $crl optional + * @access public + * @return array + */ + function listRevoked($crl = null) + { + if (!isset($crl)) { + $crl = $this->currentCert; + } + + if (!isset($crl['tbsCertList'])) { + return false; + } + + $result = array(); + + if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) { + foreach ($rclist as $rc) { + $result[] = $rc['userCertificate']->toString(); + } + } + + return $result; + } + + /** + * Remove a Revoked Certificate Extension + * + * @param string $serial + * @param string $id + * @access public + * @return bool + */ + function removeRevokedCertificateExtension($serial, $id) + { + if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + return $this->_removeExtension($id, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); + } + } + + return false; + } + + /** + * Get a Revoked Certificate Extension + * + * Returns the extension if it exists and false if not + * + * @param string $serial + * @param string $id + * @param array $crl optional + * @access public + * @return mixed + */ + function getRevokedCertificateExtension($serial, $id, $crl = null) + { + if (!isset($crl)) { + $crl = $this->currentCert; + } + + if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + return $this->_getExtension($id, $crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); + } + } + + return false; + } + + /** + * Returns a list of all extensions in use for a given revoked certificate + * + * @param string $serial + * @param array $crl optional + * @access public + * @return array + */ + function getRevokedCertificateExtensions($serial, $crl = null) + { + if (!isset($crl)) { + $crl = $this->currentCert; + } + + if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + return $this->_getExtensions($crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); + } + } + + return false; + } + + /** + * Set a Revoked Certificate Extension + * + * @param string $serial + * @param string $id + * @param mixed $value + * @param bool $critical optional + * @param bool $replace optional + * @access public + * @return bool + */ + function setRevokedCertificateExtension($serial, $id, $value, $critical = false, $replace = true) + { + if (isset($this->currentCert['tbsCertList'])) { + if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) { + if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) { + return $this->_setExtension($id, $value, $critical, $replace, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); + } + } + } + + return false; + } + + /** + * Extract raw BER from Base64 encoding + * + * @access private + * @param string $str + * @return string + */ + function _extractBER($str) + { + /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them + * above and beyond the ceritificate. + * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line: + * + * Bag Attributes + * localKeyID: 01 00 00 00 + * subject=/O=organization/OU=org unit/CN=common name + * issuer=/O=organization/CN=common name + */ + if (strlen($str) > ini_get('pcre.backtrack_limit')) { + $temp = $str; + } else { + $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); + $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $temp, 1); + } + // remove new lines + $temp = str_replace(array("\r", "\n", ' '), '', $temp); + // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff + $temp = preg_replace('#^-+[^-]+-+|-+[^-]+-+$#', '', $temp); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + return $temp != false ? $temp : $str; + } + + /** + * Returns the OID corresponding to a name + * + * What's returned in the associative array returned by loadX509() (or load*()) is either a name or an OID if + * no OID to name mapping is available. The problem with this is that what may be an unmapped OID in one version + * of phpseclib may not be unmapped in the next version, so apps that are looking at this OID may not be able + * to work from version to version. + * + * This method will return the OID if a name is passed to it and if no mapping is avialable it'll assume that + * what's being passed to it already is an OID and return that instead. A few examples. + * + * getOID('2.16.840.1.101.3.4.2.1') == '2.16.840.1.101.3.4.2.1' + * getOID('id-sha256') == '2.16.840.1.101.3.4.2.1' + * getOID('zzz') == 'zzz' + * + * @access public + * @return string + */ + function getOID($name) + { + static $reverseMap; + if (!isset($reverseMap)) { + $reverseMap = array_flip($this->oids); + } + return isset($reverseMap[$name]) ? $reverseMap[$name] : $name; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php new file mode 100644 index 0000000..f230fde --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php @@ -0,0 +1,3787 @@ +> and << cannot be used, nor can the modulo operator %, + * which only supports integers. Although this fact will slow this library down, the fact that such a high + * base is being used should more than compensate. + * + * Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie. + * (new \phpseclib\Math\BigInteger(pow(2, 26)))->value = array(0, 1) + * + * Useful resources are as follows: + * + * - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf Handbook of Applied Cryptography (HAC)} + * - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision Math (MPM)} + * - Java's BigInteger classes. See /j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip + * + * Here's an example of how to use this library: + * + * add($b); + * + * echo $c->toString(); // outputs 5 + * ?> + * + * + * @category Math + * @package BigInteger + * @author Jim Wigginton + * @copyright 2006 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +namespace phpseclib\Math; + +use phpseclib\Crypt\Random; + +/** + * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256 + * numbers. + * + * @package BigInteger + * @author Jim Wigginton + * @access public + */ +class BigInteger +{ + /**#@+ + * Reduction constants + * + * @access private + * @see BigInteger::_reduce() + */ + /** + * @see BigInteger::_montgomery() + * @see BigInteger::_prepMontgomery() + */ + const MONTGOMERY = 0; + /** + * @see BigInteger::_barrett() + */ + const BARRETT = 1; + /** + * @see BigInteger::_mod2() + */ + const POWEROF2 = 2; + /** + * @see BigInteger::_remainder() + */ + const CLASSIC = 3; + /** + * @see BigInteger::__clone() + */ + const NONE = 4; + /**#@-*/ + + /**#@+ + * Array constants + * + * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and + * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them. + * + * @access private + */ + /** + * $result[self::VALUE] contains the value. + */ + const VALUE = 0; + /** + * $result[self::SIGN] contains the sign. + */ + const SIGN = 1; + /**#@-*/ + + /**#@+ + * @access private + * @see BigInteger::_montgomery() + * @see BigInteger::_barrett() + */ + /** + * Cache constants + * + * $cache[self::VARIABLE] tells us whether or not the cached data is still valid. + */ + const VARIABLE = 0; + /** + * $cache[self::DATA] contains the cached data. + */ + const DATA = 1; + /**#@-*/ + + /**#@+ + * Mode constants. + * + * @access private + * @see BigInteger::__construct() + */ + /** + * To use the pure-PHP implementation + */ + const MODE_INTERNAL = 1; + /** + * To use the BCMath library + * + * (if enabled; otherwise, the internal implementation will be used) + */ + const MODE_BCMATH = 2; + /** + * To use the GMP library + * + * (if present; otherwise, either the BCMath or the internal implementation will be used) + */ + const MODE_GMP = 3; + /**#@-*/ + + /** + * Karatsuba Cutoff + * + * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication? + * + * @access private + */ + const KARATSUBA_CUTOFF = 25; + + /**#@+ + * Static properties used by the pure-PHP implementation. + * + * @see __construct() + */ + protected static $base; + protected static $baseFull; + protected static $maxDigit; + protected static $msb; + + /** + * $max10 in greatest $max10Len satisfying + * $max10 = 10**$max10Len <= 2**$base. + */ + protected static $max10; + + /** + * $max10Len in greatest $max10Len satisfying + * $max10 = 10**$max10Len <= 2**$base. + */ + protected static $max10Len; + protected static $maxDigit2; + /**#@-*/ + + /** + * Holds the BigInteger's value. + * + * @var array + * @access private + */ + var $value; + + /** + * Holds the BigInteger's magnitude. + * + * @var bool + * @access private + */ + var $is_negative = false; + + /** + * Precision + * + * @see self::setPrecision() + * @access private + */ + var $precision = -1; + + /** + * Precision Bitmask + * + * @see self::setPrecision() + * @access private + */ + var $bitmask = false; + + /** + * Mode independent value used for serialization. + * + * If the bcmath or gmp extensions are installed $this->value will be a non-serializable resource, hence the need for + * a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value, + * however, $this->hex is only calculated when $this->__sleep() is called. + * + * @see self::__sleep() + * @see self::__wakeup() + * @var string + * @access private + */ + var $hex; + + /** + * Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers. + * + * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using + * two's compliment. The sole exception to this is -10, which is treated the same as 10 is. + * + * Here's an example: + * + * toString(); // outputs 50 + * ?> + * + * + * @param int|string|resource $x base-10 number or base-$base number if $base set. + * @param int $base + * @return \phpseclib\Math\BigInteger + * @access public + */ + function __construct($x = 0, $base = 10) + { + if (!defined('MATH_BIGINTEGER_MODE')) { + switch (true) { + case extension_loaded('gmp'): + define('MATH_BIGINTEGER_MODE', self::MODE_GMP); + break; + case extension_loaded('bcmath'): + define('MATH_BIGINTEGER_MODE', self::MODE_BCMATH); + break; + default: + define('MATH_BIGINTEGER_MODE', self::MODE_INTERNAL); + } + } + + if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { + // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work + $versions = array(); + + // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems) + if (function_exists('phpinfo')) { + ob_start(); + @phpinfo(); + $content = ob_get_contents(); + ob_end_clean(); + + preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); + + if (!empty($matches[1])) { + for ($i = 0; $i < count($matches[1]); $i++) { + $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); + + // Remove letter part in OpenSSL version + if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { + $versions[$matches[1][$i]] = $fullVersion; + } else { + $versions[$matches[1][$i]] = $m[0]; + } + } + } + } + + // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ + switch (true) { + case !isset($versions['Header']): + case !isset($versions['Library']): + case $versions['Header'] == $versions['Library']: + case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0: + define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); + break; + default: + define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); + } + } + + if (!defined('PHP_INT_SIZE')) { + define('PHP_INT_SIZE', 4); + } + + if (empty(self::$base) && MATH_BIGINTEGER_MODE == self::MODE_INTERNAL) { + switch (PHP_INT_SIZE) { + case 8: // use 64-bit integers if int size is 8 bytes + self::$base = 31; + self::$baseFull = 0x80000000; + self::$maxDigit = 0x7FFFFFFF; + self::$msb = 0x40000000; + self::$max10 = 1000000000; + self::$max10Len = 9; + self::$maxDigit2 = pow(2, 62); + break; + //case 4: // use 64-bit floats if int size is 4 bytes + default: + self::$base = 26; + self::$baseFull = 0x4000000; + self::$maxDigit = 0x3FFFFFF; + self::$msb = 0x2000000; + self::$max10 = 10000000; + self::$max10Len = 7; + self::$maxDigit2 = pow(2, 52); // pow() prevents truncation + } + } + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + switch (true) { + case is_resource($x) && get_resource_type($x) == 'GMP integer': + // PHP 5.6 switched GMP from using resources to objects + case $x instanceof \GMP: + $this->value = $x; + return; + } + $this->value = gmp_init(0); + break; + case self::MODE_BCMATH: + $this->value = '0'; + break; + default: + $this->value = array(); + } + + // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48 + // '0' is the only value like this per http://php.net/empty + if (empty($x) && (abs($base) != 256 || $x !== '0')) { + return; + } + + switch ($base) { + case -256: + if (ord($x[0]) & 0x80) { + $x = ~$x; + $this->is_negative = true; + } + case 256: + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $this->value = function_exists('gmp_import') ? + gmp_import($x) : + gmp_init('0x' . bin2hex($x)); + if ($this->is_negative) { + $this->value = gmp_neg($this->value); + } + break; + case self::MODE_BCMATH: + // round $len to the nearest 4 (thanks, DavidMJ!) + $len = (strlen($x) + 3) & ~3; + + $x = str_pad($x, $len, chr(0), STR_PAD_LEFT); + + for ($i = 0; $i < $len; $i+= 4) { + $this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32 + $this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0); + } + + if ($this->is_negative) { + $this->value = '-' . $this->value; + } + + break; + // converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb) + default: + while (strlen($x)) { + $this->value[] = $this->_bytes2int($this->_base256_rshift($x, self::$base)); + } + } + + if ($this->is_negative) { + if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { + $this->is_negative = false; + } + $temp = $this->add(new static('-1')); + $this->value = $temp->value; + } + break; + case 16: + case -16: + if ($base > 0 && $x[0] == '-') { + $this->is_negative = true; + $x = substr($x, 1); + } + + $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x); + + $is_negative = false; + if ($base < 0 && hexdec($x[0]) >= 8) { + $this->is_negative = $is_negative = true; + $x = bin2hex(~pack('H*', $x)); + } + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = $this->is_negative ? '-0x' . $x : '0x' . $x; + $this->value = gmp_init($temp); + $this->is_negative = false; + break; + case self::MODE_BCMATH: + $x = (strlen($x) & 1) ? '0' . $x : $x; + $temp = new static(pack('H*', $x), 256); + $this->value = $this->is_negative ? '-' . $temp->value : $temp->value; + $this->is_negative = false; + break; + default: + $x = (strlen($x) & 1) ? '0' . $x : $x; + $temp = new static(pack('H*', $x), 256); + $this->value = $temp->value; + } + + if ($is_negative) { + $temp = $this->add(new static('-1')); + $this->value = $temp->value; + } + break; + case 10: + case -10: + // (?value = gmp_init($x); + break; + case self::MODE_BCMATH: + // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different + // results then doing it on '-1' does (modInverse does $x[0]) + $this->value = $x === '-' ? '0' : (string) $x; + break; + default: + $temp = new static(); + + $multiplier = new static(); + $multiplier->value = array(self::$max10); + + if ($x[0] == '-') { + $this->is_negative = true; + $x = substr($x, 1); + } + + $x = str_pad($x, strlen($x) + ((self::$max10Len - 1) * strlen($x)) % self::$max10Len, 0, STR_PAD_LEFT); + while (strlen($x)) { + $temp = $temp->multiply($multiplier); + $temp = $temp->add(new static($this->_int2bytes(substr($x, 0, self::$max10Len)), 256)); + $x = substr($x, self::$max10Len); + } + + $this->value = $temp->value; + } + break; + case 2: // base-2 support originally implemented by Lluis Pamies - thanks! + case -2: + if ($base > 0 && $x[0] == '-') { + $this->is_negative = true; + $x = substr($x, 1); + } + + $x = preg_replace('#^([01]*).*#', '$1', $x); + $x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT); + + $str = '0x'; + while (strlen($x)) { + $part = substr($x, 0, 4); + $str.= dechex(bindec($part)); + $x = substr($x, 4); + } + + if ($this->is_negative) { + $str = '-' . $str; + } + + $temp = new static($str, 8 * $base); // ie. either -16 or +16 + $this->value = $temp->value; + $this->is_negative = $temp->is_negative; + + break; + default: + // base not supported, so we'll let $this == 0 + } + } + + /** + * Converts a BigInteger to a byte string (eg. base-256). + * + * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're + * saved as two's compliment. + * + * Here's an example: + * + * toBytes(); // outputs chr(65) + * ?> + * + * + * @param bool $twos_compliment + * @return string + * @access public + * @internal Converts a base-2**26 number to base-2**8 + */ + function toBytes($twos_compliment = false) + { + if ($twos_compliment) { + $comparison = $this->compare(new static()); + if ($comparison == 0) { + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; + } + + $temp = $comparison < 0 ? $this->add(new static(1)) : $this->copy(); + $bytes = $temp->toBytes(); + + if (!strlen($bytes)) { // eg. if the number we're trying to convert is -1 + $bytes = chr(0); + } + + if ($this->precision <= 0 && (ord($bytes[0]) & 0x80)) { + $bytes = chr(0) . $bytes; + } + + return $comparison < 0 ? ~$bytes : $bytes; + } + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + if (gmp_cmp($this->value, gmp_init(0)) == 0) { + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; + } + + if (function_exists('gmp_export')) { + $temp = gmp_export($this->value); + } else { + $temp = gmp_strval(gmp_abs($this->value), 16); + $temp = (strlen($temp) & 1) ? '0' . $temp : $temp; + $temp = pack('H*', $temp); + } + + return $this->precision > 0 ? + substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : + ltrim($temp, chr(0)); + case self::MODE_BCMATH: + if ($this->value === '0') { + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; + } + + $value = ''; + $current = $this->value; + + if ($current[0] == '-') { + $current = substr($current, 1); + } + + while (bccomp($current, '0', 0) > 0) { + $temp = bcmod($current, '16777216'); + $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value; + $current = bcdiv($current, '16777216', 0); + } + + return $this->precision > 0 ? + substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : + ltrim($value, chr(0)); + } + + if (!count($this->value)) { + return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; + } + $result = $this->_int2bytes($this->value[count($this->value) - 1]); + + $temp = $this->copy(); + + for ($i = count($temp->value) - 2; $i >= 0; --$i) { + $temp->_base256_lshift($result, self::$base); + $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); + } + + return $this->precision > 0 ? + str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) : + $result; + } + + /** + * Converts a BigInteger to a hex string (eg. base-16)). + * + * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're + * saved as two's compliment. + * + * Here's an example: + * + * toHex(); // outputs '41' + * ?> + * + * + * @param bool $twos_compliment + * @return string + * @access public + * @internal Converts a base-2**26 number to base-2**8 + */ + function toHex($twos_compliment = false) + { + return bin2hex($this->toBytes($twos_compliment)); + } + + /** + * Converts a BigInteger to a bit string (eg. base-2). + * + * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're + * saved as two's compliment. + * + * Here's an example: + * + * toBits(); // outputs '1000001' + * ?> + * + * + * @param bool $twos_compliment + * @return string + * @access public + * @internal Converts a base-2**26 number to base-2**2 + */ + function toBits($twos_compliment = false) + { + $hex = $this->toHex($twos_compliment); + $bits = ''; + for ($i = strlen($hex) - 6, $start = strlen($hex) % 6; $i >= $start; $i-=6) { + $bits = str_pad(decbin(hexdec(substr($hex, $i, 6))), 24, '0', STR_PAD_LEFT) . $bits; + } + if ($start) { // hexdec('') == 0 + $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8 * $start, '0', STR_PAD_LEFT) . $bits; + } + $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0'); + + if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) { + return '0' . $result; + } + + return $result; + } + + /** + * Converts a BigInteger to a base-10 number. + * + * Here's an example: + * + * toString(); // outputs 50 + * ?> + * + * + * @return string + * @access public + * @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10) + */ + function toString() + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + return gmp_strval($this->value); + case self::MODE_BCMATH: + if ($this->value === '0') { + return '0'; + } + + return ltrim($this->value, '0'); + } + + if (!count($this->value)) { + return '0'; + } + + $temp = $this->copy(); + $temp->bitmask = false; + $temp->is_negative = false; + + $divisor = new static(); + $divisor->value = array(self::$max10); + $result = ''; + while (count($temp->value)) { + list($temp, $mod) = $temp->divide($divisor); + $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', self::$max10Len, '0', STR_PAD_LEFT) . $result; + } + $result = ltrim($result, '0'); + if (empty($result)) { + $result = '0'; + } + + if ($this->is_negative) { + $result = '-' . $result; + } + + return $result; + } + + /** + * Copy an object + * + * PHP5 passes objects by reference while PHP4 passes by value. As such, we need a function to guarantee + * that all objects are passed by value, when appropriate. More information can be found here: + * + * {@link http://php.net/language.oop5.basic#51624} + * + * @access public + * @see self::__clone() + * @return \phpseclib\Math\BigInteger + */ + function copy() + { + $temp = new static(); + $temp->value = $this->value; + $temp->is_negative = $this->is_negative; + $temp->precision = $this->precision; + $temp->bitmask = $this->bitmask; + return $temp; + } + + /** + * __toString() magic method + * + * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call + * toString(). + * + * @access public + * @internal Implemented per a suggestion by Techie-Michael - thanks! + */ + function __toString() + { + return $this->toString(); + } + + /** + * __clone() magic method + * + * Although you can call BigInteger::__toString() directly in PHP5, you cannot call BigInteger::__clone() directly + * in PHP5. You can in PHP4 since it's not a magic method, but in PHP5, you have to call it by using the PHP5 + * only syntax of $y = clone $x. As such, if you're trying to write an application that works on both PHP4 and + * PHP5, call BigInteger::copy(), instead. + * + * @access public + * @see self::copy() + * @return \phpseclib\Math\BigInteger + */ + function __clone() + { + return $this->copy(); + } + + /** + * __sleep() magic method + * + * Will be called, automatically, when serialize() is called on a BigInteger object. + * + * @see self::__wakeup() + * @access public + */ + function __sleep() + { + $this->hex = $this->toHex(true); + $vars = array('hex'); + if ($this->precision > 0) { + $vars[] = 'precision'; + } + return $vars; + } + + /** + * __wakeup() magic method + * + * Will be called, automatically, when unserialize() is called on a BigInteger object. + * + * @see self::__sleep() + * @access public + */ + function __wakeup() + { + $temp = new static($this->hex, -16); + $this->value = $temp->value; + $this->is_negative = $temp->is_negative; + if ($this->precision > 0) { + // recalculate $this->bitmask + $this->setPrecision($this->precision); + } + } + + /** + * __debugInfo() magic method + * + * Will be called, automatically, when print_r() or var_dump() are called + * + * @access public + */ + function __debugInfo() + { + $opts = array(); + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $engine = 'gmp'; + break; + case self::MODE_BCMATH: + $engine = 'bcmath'; + break; + case self::MODE_INTERNAL: + $engine = 'internal'; + $opts[] = PHP_INT_SIZE == 8 ? '64-bit' : '32-bit'; + } + if (MATH_BIGINTEGER_MODE != self::MODE_GMP && defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { + $opts[] = 'OpenSSL'; + } + if (!empty($opts)) { + $engine.= ' (' . implode('.', $opts) . ')'; + } + return array( + 'value' => '0x' . $this->toHex(true), + 'engine' => $engine + ); + } + + /** + * Adds two BigIntegers. + * + * Here's an example: + * + * add($b); + * + * echo $c->toString(); // outputs 30 + * ?> + * + * + * @param \phpseclib\Math\BigInteger $y + * @return \phpseclib\Math\BigInteger + * @access public + * @internal Performs base-2**52 addition + */ + function add($y) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); + $temp->value = gmp_add($this->value, $y->value); + + return $this->_normalize($temp); + case self::MODE_BCMATH: + $temp = new static(); + $temp->value = bcadd($this->value, $y->value, 0); + + return $this->_normalize($temp); + } + + $temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative); + + $result = new static(); + $result->value = $temp[self::VALUE]; + $result->is_negative = $temp[self::SIGN]; + + return $this->_normalize($result); + } + + /** + * Performs addition. + * + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return array + * @access private + */ + function _add($x_value, $x_negative, $y_value, $y_negative) + { + $x_size = count($x_value); + $y_size = count($y_value); + + if ($x_size == 0) { + return array( + self::VALUE => $y_value, + self::SIGN => $y_negative + ); + } elseif ($y_size == 0) { + return array( + self::VALUE => $x_value, + self::SIGN => $x_negative + ); + } + + // subtract, if appropriate + if ($x_negative != $y_negative) { + if ($x_value == $y_value) { + return array( + self::VALUE => array(), + self::SIGN => false + ); + } + + $temp = $this->_subtract($x_value, false, $y_value, false); + $temp[self::SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ? + $x_negative : $y_negative; + + return $temp; + } + + if ($x_size < $y_size) { + $size = $x_size; + $value = $y_value; + } else { + $size = $y_size; + $value = $x_value; + } + + $value[count($value)] = 0; // just in case the carry adds an extra digit + + $carry = 0; + for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) { + $sum = $x_value[$j] * self::$baseFull + $x_value[$i] + $y_value[$j] * self::$baseFull + $y_value[$i] + $carry; + $carry = $sum >= self::$maxDigit2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 + $sum = $carry ? $sum - self::$maxDigit2 : $sum; + + $temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31); + + $value[$i] = (int) ($sum - self::$baseFull * $temp); // eg. a faster alternative to fmod($sum, 0x4000000) + $value[$j] = $temp; + } + + if ($j == $size) { // ie. if $y_size is odd + $sum = $x_value[$i] + $y_value[$i] + $carry; + $carry = $sum >= self::$baseFull; + $value[$i] = $carry ? $sum - self::$baseFull : $sum; + ++$i; // ie. let $i = $j since we've just done $value[$i] + } + + if ($carry) { + for (; $value[$i] == self::$maxDigit; ++$i) { + $value[$i] = 0; + } + ++$value[$i]; + } + + return array( + self::VALUE => $this->_trim($value), + self::SIGN => $x_negative + ); + } + + /** + * Subtracts two BigIntegers. + * + * Here's an example: + * + * subtract($b); + * + * echo $c->toString(); // outputs -10 + * ?> + * + * + * @param \phpseclib\Math\BigInteger $y + * @return \phpseclib\Math\BigInteger + * @access public + * @internal Performs base-2**52 subtraction + */ + function subtract($y) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); + $temp->value = gmp_sub($this->value, $y->value); + + return $this->_normalize($temp); + case self::MODE_BCMATH: + $temp = new static(); + $temp->value = bcsub($this->value, $y->value, 0); + + return $this->_normalize($temp); + } + + $temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative); + + $result = new static(); + $result->value = $temp[self::VALUE]; + $result->is_negative = $temp[self::SIGN]; + + return $this->_normalize($result); + } + + /** + * Performs subtraction. + * + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return array + * @access private + */ + function _subtract($x_value, $x_negative, $y_value, $y_negative) + { + $x_size = count($x_value); + $y_size = count($y_value); + + if ($x_size == 0) { + return array( + self::VALUE => $y_value, + self::SIGN => !$y_negative + ); + } elseif ($y_size == 0) { + return array( + self::VALUE => $x_value, + self::SIGN => $x_negative + ); + } + + // add, if appropriate (ie. -$x - +$y or +$x - -$y) + if ($x_negative != $y_negative) { + $temp = $this->_add($x_value, false, $y_value, false); + $temp[self::SIGN] = $x_negative; + + return $temp; + } + + $diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative); + + if (!$diff) { + return array( + self::VALUE => array(), + self::SIGN => false + ); + } + + // switch $x and $y around, if appropriate. + if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) { + $temp = $x_value; + $x_value = $y_value; + $y_value = $temp; + + $x_negative = !$x_negative; + + $x_size = count($x_value); + $y_size = count($y_value); + } + + // at this point, $x_value should be at least as big as - if not bigger than - $y_value + + $carry = 0; + for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) { + $sum = $x_value[$j] * self::$baseFull + $x_value[$i] - $y_value[$j] * self::$baseFull - $y_value[$i] - $carry; + $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 + $sum = $carry ? $sum + self::$maxDigit2 : $sum; + + $temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31); + + $x_value[$i] = (int) ($sum - self::$baseFull * $temp); + $x_value[$j] = $temp; + } + + if ($j == $y_size) { // ie. if $y_size is odd + $sum = $x_value[$i] - $y_value[$i] - $carry; + $carry = $sum < 0; + $x_value[$i] = $carry ? $sum + self::$baseFull : $sum; + ++$i; + } + + if ($carry) { + for (; !$x_value[$i]; ++$i) { + $x_value[$i] = self::$maxDigit; + } + --$x_value[$i]; + } + + return array( + self::VALUE => $this->_trim($x_value), + self::SIGN => $x_negative + ); + } + + /** + * Multiplies two BigIntegers + * + * Here's an example: + * + * multiply($b); + * + * echo $c->toString(); // outputs 200 + * ?> + * + * + * @param \phpseclib\Math\BigInteger $x + * @return \phpseclib\Math\BigInteger + * @access public + */ + function multiply($x) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); + $temp->value = gmp_mul($this->value, $x->value); + + return $this->_normalize($temp); + case self::MODE_BCMATH: + $temp = new static(); + $temp->value = bcmul($this->value, $x->value, 0); + + return $this->_normalize($temp); + } + + $temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative); + + $product = new static(); + $product->value = $temp[self::VALUE]; + $product->is_negative = $temp[self::SIGN]; + + return $this->_normalize($product); + } + + /** + * Performs multiplication. + * + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return array + * @access private + */ + function _multiply($x_value, $x_negative, $y_value, $y_negative) + { + //if ( $x_value == $y_value ) { + // return array( + // self::VALUE => $this->_square($x_value), + // self::SIGN => $x_sign != $y_value + // ); + //} + + $x_length = count($x_value); + $y_length = count($y_value); + + if (!$x_length || !$y_length) { // a 0 is being multiplied + return array( + self::VALUE => array(), + self::SIGN => false + ); + } + + return array( + self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ? + $this->_trim($this->_regularMultiply($x_value, $y_value)) : + $this->_trim($this->_karatsuba($x_value, $y_value)), + self::SIGN => $x_negative != $y_negative + ); + } + + /** + * Performs long multiplication on two BigIntegers + * + * Modeled after 'multiply' in MutableBigInteger.java. + * + * @param array $x_value + * @param array $y_value + * @return array + * @access private + */ + function _regularMultiply($x_value, $y_value) + { + $x_length = count($x_value); + $y_length = count($y_value); + + if (!$x_length || !$y_length) { // a 0 is being multiplied + return array(); + } + + if ($x_length < $y_length) { + $temp = $x_value; + $x_value = $y_value; + $y_value = $temp; + + $x_length = count($x_value); + $y_length = count($y_value); + } + + $product_value = $this->_array_repeat(0, $x_length + $y_length); + + // the following for loop could be removed if the for loop following it + // (the one with nested for loops) initially set $i to 0, but + // doing so would also make the result in one set of unnecessary adds, + // since on the outermost loops first pass, $product->value[$k] is going + // to always be 0 + + $carry = 0; + + for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0 + $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $product_value[$j] = (int) ($temp - self::$baseFull * $carry); + } + + $product_value[$j] = $carry; + + // the above for loop is what the previous comment was talking about. the + // following for loop is the "one with nested for loops" + for ($i = 1; $i < $y_length; ++$i) { + $carry = 0; + + for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) { + $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $product_value[$k] = (int) ($temp - self::$baseFull * $carry); + } + + $product_value[$k] = $carry; + } + + return $product_value; + } + + /** + * Performs Karatsuba multiplication on two BigIntegers + * + * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}. + * + * @param array $x_value + * @param array $y_value + * @return array + * @access private + */ + function _karatsuba($x_value, $y_value) + { + $m = min(count($x_value) >> 1, count($y_value) >> 1); + + if ($m < self::KARATSUBA_CUTOFF) { + return $this->_regularMultiply($x_value, $y_value); + } + + $x1 = array_slice($x_value, $m); + $x0 = array_slice($x_value, 0, $m); + $y1 = array_slice($y_value, $m); + $y0 = array_slice($y_value, 0, $m); + + $z2 = $this->_karatsuba($x1, $y1); + $z0 = $this->_karatsuba($x0, $y0); + + $z1 = $this->_add($x1, false, $x0, false); + $temp = $this->_add($y1, false, $y0, false); + $z1 = $this->_karatsuba($z1[self::VALUE], $temp[self::VALUE]); + $temp = $this->_add($z2, false, $z0, false); + $z1 = $this->_subtract($z1, false, $temp[self::VALUE], false); + + $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); + $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); + + $xy = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); + $xy = $this->_add($xy[self::VALUE], $xy[self::SIGN], $z0, false); + + return $xy[self::VALUE]; + } + + /** + * Performs squaring + * + * @param array $x + * @return array + * @access private + */ + function _square($x = false) + { + return count($x) < 2 * self::KARATSUBA_CUTOFF ? + $this->_trim($this->_baseSquare($x)) : + $this->_trim($this->_karatsubaSquare($x)); + } + + /** + * Performs traditional squaring on two BigIntegers + * + * Squaring can be done faster than multiplying a number by itself can be. See + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} / + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information. + * + * @param array $value + * @return array + * @access private + */ + function _baseSquare($value) + { + if (empty($value)) { + return array(); + } + $square_value = $this->_array_repeat(0, 2 * count($value)); + + for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) { + $i2 = $i << 1; + + $temp = $square_value[$i2] + $value[$i] * $value[$i]; + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $square_value[$i2] = (int) ($temp - self::$baseFull * $carry); + + // note how we start from $i+1 instead of 0 as we do in multiplication. + for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) { + $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry; + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $square_value[$k] = (int) ($temp - self::$baseFull * $carry); + } + + // the following line can yield values larger 2**15. at this point, PHP should switch + // over to floats. + $square_value[$i + $max_index + 1] = $carry; + } + + return $square_value; + } + + /** + * Performs Karatsuba "squaring" on two BigIntegers + * + * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}. + * + * @param array $value + * @return array + * @access private + */ + function _karatsubaSquare($value) + { + $m = count($value) >> 1; + + if ($m < self::KARATSUBA_CUTOFF) { + return $this->_baseSquare($value); + } + + $x1 = array_slice($value, $m); + $x0 = array_slice($value, 0, $m); + + $z2 = $this->_karatsubaSquare($x1); + $z0 = $this->_karatsubaSquare($x0); + + $z1 = $this->_add($x1, false, $x0, false); + $z1 = $this->_karatsubaSquare($z1[self::VALUE]); + $temp = $this->_add($z2, false, $z0, false); + $z1 = $this->_subtract($z1, false, $temp[self::VALUE], false); + + $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); + $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); + + $xx = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); + $xx = $this->_add($xx[self::VALUE], $xx[self::SIGN], $z0, false); + + return $xx[self::VALUE]; + } + + /** + * Divides two BigIntegers. + * + * Returns an array whose first element contains the quotient and whose second element contains the + * "common residue". If the remainder would be positive, the "common residue" and the remainder are the + * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder + * and the divisor (basically, the "common residue" is the first positive modulo). + * + * Here's an example: + * + * divide($b); + * + * echo $quotient->toString(); // outputs 0 + * echo "\r\n"; + * echo $remainder->toString(); // outputs 10 + * ?> + * + * + * @param \phpseclib\Math\BigInteger $y + * @return array + * @access public + * @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}. + */ + function divide($y) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $quotient = new static(); + $remainder = new static(); + + list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value); + + if (gmp_sign($remainder->value) < 0) { + $remainder->value = gmp_add($remainder->value, gmp_abs($y->value)); + } + + return array($this->_normalize($quotient), $this->_normalize($remainder)); + case self::MODE_BCMATH: + $quotient = new static(); + $remainder = new static(); + + $quotient->value = bcdiv($this->value, $y->value, 0); + $remainder->value = bcmod($this->value, $y->value); + + if ($remainder->value[0] == '-') { + $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0); + } + + return array($this->_normalize($quotient), $this->_normalize($remainder)); + } + + if (count($y->value) == 1) { + list($q, $r) = $this->_divide_digit($this->value, $y->value[0]); + $quotient = new static(); + $remainder = new static(); + $quotient->value = $q; + $remainder->value = array($r); + $quotient->is_negative = $this->is_negative != $y->is_negative; + return array($this->_normalize($quotient), $this->_normalize($remainder)); + } + + static $zero; + if (!isset($zero)) { + $zero = new static(); + } + + $x = $this->copy(); + $y = $y->copy(); + + $x_sign = $x->is_negative; + $y_sign = $y->is_negative; + + $x->is_negative = $y->is_negative = false; + + $diff = $x->compare($y); + + if (!$diff) { + $temp = new static(); + $temp->value = array(1); + $temp->is_negative = $x_sign != $y_sign; + return array($this->_normalize($temp), $this->_normalize(new static())); + } + + if ($diff < 0) { + // if $x is negative, "add" $y. + if ($x_sign) { + $x = $y->subtract($x); + } + return array($this->_normalize(new static()), $this->_normalize($x)); + } + + // normalize $x and $y as described in HAC 14.23 / 14.24 + $msb = $y->value[count($y->value) - 1]; + for ($shift = 0; !($msb & self::$msb); ++$shift) { + $msb <<= 1; + } + $x->_lshift($shift); + $y->_lshift($shift); + $y_value = &$y->value; + + $x_max = count($x->value) - 1; + $y_max = count($y->value) - 1; + + $quotient = new static(); + $quotient_value = &$quotient->value; + $quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1); + + static $temp, $lhs, $rhs; + if (!isset($temp)) { + $temp = new static(); + $lhs = new static(); + $rhs = new static(); + } + $temp_value = &$temp->value; + $rhs_value = &$rhs->value; + + // $temp = $y << ($x_max - $y_max-1) in base 2**26 + $temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value); + + while ($x->compare($temp) >= 0) { + // calculate the "common residue" + ++$quotient_value[$x_max - $y_max]; + $x = $x->subtract($temp); + $x_max = count($x->value) - 1; + } + + for ($i = $x_max; $i >= $y_max + 1; --$i) { + $x_value = &$x->value; + $x_window = array( + isset($x_value[$i]) ? $x_value[$i] : 0, + isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0, + isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0 + ); + $y_window = array( + $y_value[$y_max], + ($y_max > 0) ? $y_value[$y_max - 1] : 0 + ); + + $q_index = $i - $y_max - 1; + if ($x_window[0] == $y_window[0]) { + $quotient_value[$q_index] = self::$maxDigit; + } else { + $quotient_value[$q_index] = $this->_safe_divide( + $x_window[0] * self::$baseFull + $x_window[1], + $y_window[0] + ); + } + + $temp_value = array($y_window[1], $y_window[0]); + + $lhs->value = array($quotient_value[$q_index]); + $lhs = $lhs->multiply($temp); + + $rhs_value = array($x_window[2], $x_window[1], $x_window[0]); + + while ($lhs->compare($rhs) > 0) { + --$quotient_value[$q_index]; + + $lhs->value = array($quotient_value[$q_index]); + $lhs = $lhs->multiply($temp); + } + + $adjust = $this->_array_repeat(0, $q_index); + $temp_value = array($quotient_value[$q_index]); + $temp = $temp->multiply($y); + $temp_value = &$temp->value; + if (count($temp_value)) { + $temp_value = array_merge($adjust, $temp_value); + } + + $x = $x->subtract($temp); + + if ($x->compare($zero) < 0) { + $temp_value = array_merge($adjust, $y_value); + $x = $x->add($temp); + + --$quotient_value[$q_index]; + } + + $x_max = count($x_value) - 1; + } + + // unnormalize the remainder + $x->_rshift($shift); + + $quotient->is_negative = $x_sign != $y_sign; + + // calculate the "common residue", if appropriate + if ($x_sign) { + $y->_rshift($shift); + $x = $y->subtract($x); + } + + return array($this->_normalize($quotient), $this->_normalize($x)); + } + + /** + * Divides a BigInteger by a regular integer + * + * abc / x = a00 / x + b0 / x + c / x + * + * @param array $dividend + * @param array $divisor + * @return array + * @access private + */ + function _divide_digit($dividend, $divisor) + { + $carry = 0; + $result = array(); + + for ($i = count($dividend) - 1; $i >= 0; --$i) { + $temp = self::$baseFull * $carry + $dividend[$i]; + $result[$i] = $this->_safe_divide($temp, $divisor); + $carry = (int) ($temp - $divisor * $result[$i]); + } + + return array($result, $carry); + } + + /** + * Performs modular exponentiation. + * + * Here's an example: + * + * modPow($b, $c); + * + * echo $c->toString(); // outputs 10 + * ?> + * + * + * @param \phpseclib\Math\BigInteger $e + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger + * @access public + * @internal The most naive approach to modular exponentiation has very unreasonable requirements, and + * and although the approach involving repeated squaring does vastly better, it, too, is impractical + * for our purposes. The reason being that division - by far the most complicated and time-consuming + * of the basic operations (eg. +,-,*,/) - occurs multiple times within it. + * + * Modular reductions resolve this issue. Although an individual modular reduction takes more time + * then an individual division, when performed in succession (with the same modulo), they're a lot faster. + * + * The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction, + * although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the + * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because + * the product of two odd numbers is odd), but what about when RSA isn't used? + * + * In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a + * Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the + * modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however, + * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and + * the other, a power of two - and recombine them, later. This is the method that this modPow function uses. + * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates. + */ + function modPow($e, $n) + { + $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs(); + + if ($e->compare(new static()) < 0) { + $e = $e->abs(); + + $temp = $this->modInverse($n); + if ($temp === false) { + return false; + } + + return $this->_normalize($temp->modPow($e, $n)); + } + + if (MATH_BIGINTEGER_MODE == self::MODE_GMP) { + $temp = new static(); + $temp->value = gmp_powm($this->value, $e->value, $n->value); + + return $this->_normalize($temp); + } + + if ($this->compare(new static()) < 0 || $this->compare($n) > 0) { + list(, $temp) = $this->divide($n); + return $temp->modPow($e, $n); + } + + if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { + $components = array( + 'modulus' => $n->toBytes(true), + 'publicExponent' => $e->toBytes(true) + ); + + $components = array( + 'modulus' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['modulus'])), $components['modulus']), + 'publicExponent' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent']) + ); + + $RSAPublicKey = pack( + 'Ca*a*a*', + 48, + $this->_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])), + $components['modulus'], + $components['publicExponent'] + ); + + $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA + $RSAPublicKey = chr(0) . $RSAPublicKey; + $RSAPublicKey = chr(3) . $this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey; + + $encapsulated = pack( + 'Ca*a*', + 48, + $this->_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)), + $rsaOID . $RSAPublicKey + ); + + $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . + chunk_split(base64_encode($encapsulated)) . + '-----END PUBLIC KEY-----'; + + $plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT); + + if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) { + return new static($result, 256); + } + } + + if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { + $temp = new static(); + $temp->value = bcpowmod($this->value, $e->value, $n->value, 0); + + return $this->_normalize($temp); + } + + if (empty($e->value)) { + $temp = new static(); + $temp->value = array(1); + return $this->_normalize($temp); + } + + if ($e->value == array(1)) { + list(, $temp) = $this->divide($n); + return $this->_normalize($temp); + } + + if ($e->value == array(2)) { + $temp = new static(); + $temp->value = $this->_square($this->value); + list(, $temp) = $temp->divide($n); + return $this->_normalize($temp); + } + + return $this->_normalize($this->_slidingWindow($e, $n, self::BARRETT)); + + // the following code, although not callable, can be run independently of the above code + // although the above code performed better in my benchmarks the following could might + // perform better under different circumstances. in lieu of deleting it it's just been + // made uncallable + + // is the modulo odd? + if ($n->value[0] & 1) { + return $this->_normalize($this->_slidingWindow($e, $n, self::MONTGOMERY)); + } + // if it's not, it's even + + // find the lowest set bit (eg. the max pow of 2 that divides $n) + for ($i = 0; $i < count($n->value); ++$i) { + if ($n->value[$i]) { + $temp = decbin($n->value[$i]); + $j = strlen($temp) - strrpos($temp, '1') - 1; + $j+= 26 * $i; + break; + } + } + // at this point, 2^$j * $n/(2^$j) == $n + + $mod1 = $n->copy(); + $mod1->_rshift($j); + $mod2 = new static(); + $mod2->value = array(1); + $mod2->_lshift($j); + + $part1 = ($mod1->value != array(1)) ? $this->_slidingWindow($e, $mod1, self::MONTGOMERY) : new static(); + $part2 = $this->_slidingWindow($e, $mod2, self::POWEROF2); + + $y1 = $mod2->modInverse($mod1); + $y2 = $mod1->modInverse($mod2); + + $result = $part1->multiply($mod2); + $result = $result->multiply($y1); + + $temp = $part2->multiply($mod1); + $temp = $temp->multiply($y2); + + $result = $result->add($temp); + list(, $result) = $result->divide($n); + + return $this->_normalize($result); + } + + /** + * Performs modular exponentiation. + * + * Alias for modPow(). + * + * @param \phpseclib\Math\BigInteger $e + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger + * @access public + */ + function powMod($e, $n) + { + return $this->modPow($e, $n); + } + + /** + * Sliding Window k-ary Modular Exponentiation + * + * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} / + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims, + * however, this function performs a modular reduction after every multiplication and squaring operation. + * As such, this function has the same preconditions that the reductions being used do. + * + * @param \phpseclib\Math\BigInteger $e + * @param \phpseclib\Math\BigInteger $n + * @param int $mode + * @return \phpseclib\Math\BigInteger + * @access private + */ + function _slidingWindow($e, $n, $mode) + { + static $window_ranges = array(7, 25, 81, 241, 673, 1793); // from BigInteger.java's oddModPow function + //static $window_ranges = array(0, 7, 36, 140, 450, 1303, 3529); // from MPM 7.3.1 + + $e_value = $e->value; + $e_length = count($e_value) - 1; + $e_bits = decbin($e_value[$e_length]); + for ($i = $e_length - 1; $i >= 0; --$i) { + $e_bits.= str_pad(decbin($e_value[$i]), self::$base, '0', STR_PAD_LEFT); + } + + $e_length = strlen($e_bits); + + // calculate the appropriate window size. + // $window_size == 3 if $window_ranges is between 25 and 81, for example. + for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) { + } + + $n_value = $n->value; + + // precompute $this^0 through $this^$window_size + $powers = array(); + $powers[1] = $this->_prepareReduce($this->value, $n_value, $mode); + $powers[2] = $this->_squareReduce($powers[1], $n_value, $mode); + + // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end + // in a 1. ie. it's supposed to be odd. + $temp = 1 << ($window_size - 1); + for ($i = 1; $i < $temp; ++$i) { + $i2 = $i << 1; + $powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode); + } + + $result = array(1); + $result = $this->_prepareReduce($result, $n_value, $mode); + + for ($i = 0; $i < $e_length;) { + if (!$e_bits[$i]) { + $result = $this->_squareReduce($result, $n_value, $mode); + ++$i; + } else { + for ($j = $window_size - 1; $j > 0; --$j) { + if (!empty($e_bits[$i + $j])) { + break; + } + } + + // eg. the length of substr($e_bits, $i, $j + 1) + for ($k = 0; $k <= $j; ++$k) { + $result = $this->_squareReduce($result, $n_value, $mode); + } + + $result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode); + + $i += $j + 1; + } + } + + $temp = new static(); + $temp->value = $this->_reduce($result, $n_value, $mode); + + return $temp; + } + + /** + * Modular reduction + * + * For most $modes this will return the remainder. + * + * @see self::_slidingWindow() + * @access private + * @param array $x + * @param array $n + * @param int $mode + * @return array + */ + function _reduce($x, $n, $mode) + { + switch ($mode) { + case self::MONTGOMERY: + return $this->_montgomery($x, $n); + case self::BARRETT: + return $this->_barrett($x, $n); + case self::POWEROF2: + $lhs = new static(); + $lhs->value = $x; + $rhs = new static(); + $rhs->value = $n; + return $x->_mod2($n); + case self::CLASSIC: + $lhs = new static(); + $lhs->value = $x; + $rhs = new static(); + $rhs->value = $n; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + case self::NONE: + return $x; + default: + // an invalid $mode was provided + } + } + + /** + * Modular reduction preperation + * + * @see self::_slidingWindow() + * @access private + * @param array $x + * @param array $n + * @param int $mode + * @return array + */ + function _prepareReduce($x, $n, $mode) + { + if ($mode == self::MONTGOMERY) { + return $this->_prepMontgomery($x, $n); + } + return $this->_reduce($x, $n, $mode); + } + + /** + * Modular multiply + * + * @see self::_slidingWindow() + * @access private + * @param array $x + * @param array $y + * @param array $n + * @param int $mode + * @return array + */ + function _multiplyReduce($x, $y, $n, $mode) + { + if ($mode == self::MONTGOMERY) { + return $this->_montgomeryMultiply($x, $y, $n); + } + $temp = $this->_multiply($x, false, $y, false); + return $this->_reduce($temp[self::VALUE], $n, $mode); + } + + /** + * Modular square + * + * @see self::_slidingWindow() + * @access private + * @param array $x + * @param array $n + * @param int $mode + * @return array + */ + function _squareReduce($x, $n, $mode) + { + if ($mode == self::MONTGOMERY) { + return $this->_montgomeryMultiply($x, $x, $n); + } + return $this->_reduce($this->_square($x), $n, $mode); + } + + /** + * Modulos for Powers of Two + * + * Calculates $x%$n, where $n = 2**$e, for some $e. Since this is basically the same as doing $x & ($n-1), + * we'll just use this function as a wrapper for doing that. + * + * @see self::_slidingWindow() + * @access private + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger + */ + function _mod2($n) + { + $temp = new static(); + $temp->value = array(1); + return $this->bitwise_and($n->subtract($temp)); + } + + /** + * Barrett Modular Reduction + * + * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} / + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly, + * so as not to require negative numbers (initially, this script didn't support negative numbers). + * + * Employs "folding", as described at + * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from + * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x." + * + * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that + * usable on account of (1) its not using reasonable radix points as discussed in + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable + * radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that + * (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line + * comments for details. + * + * @see self::_slidingWindow() + * @access private + * @param array $n + * @param array $m + * @return array + */ + function _barrett($n, $m) + { + static $cache = array( + self::VARIABLE => array(), + self::DATA => array() + ); + + $m_length = count($m); + + // if ($this->_compare($n, $this->_square($m)) >= 0) { + if (count($n) > 2 * $m_length) { + $lhs = new static(); + $rhs = new static(); + $lhs->value = $n; + $rhs->value = $m; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + } + + // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced + if ($m_length < 5) { + return $this->_regularBarrett($n, $m); + } + + // n = 2 * m.length + + if (($key = array_search($m, $cache[self::VARIABLE])) === false) { + $key = count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $m; + + $lhs = new static(); + $lhs_value = &$lhs->value; + $lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1)); + $lhs_value[] = 1; + $rhs = new static(); + $rhs->value = $m; + + list($u, $m1) = $lhs->divide($rhs); + $u = $u->value; + $m1 = $m1->value; + + $cache[self::DATA][] = array( + 'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1) + 'm1'=> $m1 // m.length + ); + } else { + extract($cache[self::DATA][$key]); + } + + $cutoff = $m_length + ($m_length >> 1); + $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1) + $msd = array_slice($n, $cutoff); // m.length >> 1 + $lsd = $this->_trim($lsd); + $temp = $this->_multiply($msd, false, $m1, false); + $n = $this->_add($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1 + + if ($m_length & 1) { + return $this->_regularBarrett($n[self::VALUE], $m); + } + + // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2 + $temp = array_slice($n[self::VALUE], $m_length - 1); + // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2 + // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1 + $temp = $this->_multiply($temp, false, $u, false); + // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1 + // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + $temp = array_slice($temp[self::VALUE], ($m_length >> 1) + 1); + // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1 + // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1) + $temp = $this->_multiply($temp, false, $m, false); + + // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit + // number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop + // following this comment would loop a lot (hence our calling _regularBarrett() in that situation). + + $result = $this->_subtract($n[self::VALUE], false, $temp[self::VALUE], false); + + while ($this->_compare($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) { + $result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $m, false); + } + + return $result[self::VALUE]; + } + + /** + * (Regular) Barrett Modular Reduction + * + * For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this + * is that this function does not fold the denominator into a smaller form. + * + * @see self::_slidingWindow() + * @access private + * @param array $x + * @param array $n + * @return array + */ + function _regularBarrett($x, $n) + { + static $cache = array( + self::VARIABLE => array(), + self::DATA => array() + ); + + $n_length = count($n); + + if (count($x) > 2 * $n_length) { + $lhs = new static(); + $rhs = new static(); + $lhs->value = $x; + $rhs->value = $n; + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + } + + if (($key = array_search($n, $cache[self::VARIABLE])) === false) { + $key = count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $n; + $lhs = new static(); + $lhs_value = &$lhs->value; + $lhs_value = $this->_array_repeat(0, 2 * $n_length); + $lhs_value[] = 1; + $rhs = new static(); + $rhs->value = $n; + list($temp, ) = $lhs->divide($rhs); // m.length + $cache[self::DATA][] = $temp->value; + } + + // 2 * m.length - (m.length - 1) = m.length + 1 + $temp = array_slice($x, $n_length - 1); + // (m.length + 1) + m.length = 2 * m.length + 1 + $temp = $this->_multiply($temp, false, $cache[self::DATA][$key], false); + // (2 * m.length + 1) - (m.length - 1) = m.length + 2 + $temp = array_slice($temp[self::VALUE], $n_length + 1); + + // m.length + 1 + $result = array_slice($x, 0, $n_length + 1); + // m.length + 1 + $temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1); + // $temp == array_slice($temp->_multiply($temp, false, $n, false)->value, 0, $n_length + 1) + + if ($this->_compare($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) { + $corrector_value = $this->_array_repeat(0, $n_length + 1); + $corrector_value[count($corrector_value)] = 1; + $result = $this->_add($result, false, $corrector_value, false); + $result = $result[self::VALUE]; + } + + // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits + $result = $this->_subtract($result, false, $temp[self::VALUE], $temp[self::SIGN]); + while ($this->_compare($result[self::VALUE], $result[self::SIGN], $n, false) > 0) { + $result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $n, false); + } + + return $result[self::VALUE]; + } + + /** + * Performs long multiplication up to $stop digits + * + * If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved. + * + * @see self::_regularBarrett() + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @param int $stop + * @return array + * @access private + */ + function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) + { + $x_length = count($x_value); + $y_length = count($y_value); + + if (!$x_length || !$y_length) { // a 0 is being multiplied + return array( + self::VALUE => array(), + self::SIGN => false + ); + } + + if ($x_length < $y_length) { + $temp = $x_value; + $x_value = $y_value; + $y_value = $temp; + + $x_length = count($x_value); + $y_length = count($y_value); + } + + $product_value = $this->_array_repeat(0, $x_length + $y_length); + + // the following for loop could be removed if the for loop following it + // (the one with nested for loops) initially set $i to 0, but + // doing so would also make the result in one set of unnecessary adds, + // since on the outermost loops first pass, $product->value[$k] is going + // to always be 0 + + $carry = 0; + + for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i + $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $product_value[$j] = (int) ($temp - self::$baseFull * $carry); + } + + if ($j < $stop) { + $product_value[$j] = $carry; + } + + // the above for loop is what the previous comment was talking about. the + // following for loop is the "one with nested for loops" + + for ($i = 1; $i < $y_length; ++$i) { + $carry = 0; + + for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) { + $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $product_value[$k] = (int) ($temp - self::$baseFull * $carry); + } + + if ($k < $stop) { + $product_value[$k] = $carry; + } + } + + return array( + self::VALUE => $this->_trim($product_value), + self::SIGN => $x_negative != $y_negative + ); + } + + /** + * Montgomery Modular Reduction + * + * ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n. + * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM 6.3} provides insights on how this can be + * improved upon (basically, by using the comba method). gcd($n, 2) must be equal to one for this function + * to work correctly. + * + * @see self::_prepMontgomery() + * @see self::_slidingWindow() + * @access private + * @param array $x + * @param array $n + * @return array + */ + function _montgomery($x, $n) + { + static $cache = array( + self::VARIABLE => array(), + self::DATA => array() + ); + + if (($key = array_search($n, $cache[self::VARIABLE])) === false) { + $key = count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $x; + $cache[self::DATA][] = $this->_modInverse67108864($n); + } + + $k = count($n); + + $result = array(self::VALUE => $x); + + for ($i = 0; $i < $k; ++$i) { + $temp = $result[self::VALUE][$i] * $cache[self::DATA][$key]; + $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); + $temp = $this->_regularMultiply(array($temp), $n); + $temp = array_merge($this->_array_repeat(0, $i), $temp); + $result = $this->_add($result[self::VALUE], false, $temp, false); + } + + $result[self::VALUE] = array_slice($result[self::VALUE], $k); + + if ($this->_compare($result, false, $n, false) >= 0) { + $result = $this->_subtract($result[self::VALUE], false, $n, false); + } + + return $result[self::VALUE]; + } + + /** + * Montgomery Multiply + * + * Interleaves the montgomery reduction and long multiplication algorithms together as described in + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36} + * + * @see self::_prepMontgomery() + * @see self::_montgomery() + * @access private + * @param array $x + * @param array $y + * @param array $m + * @return array + */ + function _montgomeryMultiply($x, $y, $m) + { + $temp = $this->_multiply($x, false, $y, false); + return $this->_montgomery($temp[self::VALUE], $m); + + // the following code, although not callable, can be run independently of the above code + // although the above code performed better in my benchmarks the following could might + // perform better under different circumstances. in lieu of deleting it it's just been + // made uncallable + + static $cache = array( + self::VARIABLE => array(), + self::DATA => array() + ); + + if (($key = array_search($m, $cache[self::VARIABLE])) === false) { + $key = count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $m; + $cache[self::DATA][] = $this->_modInverse67108864($m); + } + + $n = max(count($x), count($y), count($m)); + $x = array_pad($x, $n, 0); + $y = array_pad($y, $n, 0); + $m = array_pad($m, $n, 0); + $a = array(self::VALUE => $this->_array_repeat(0, $n + 1)); + for ($i = 0; $i < $n; ++$i) { + $temp = $a[self::VALUE][0] + $x[$i] * $y[0]; + $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); + $temp = $temp * $cache[self::DATA][$key]; + $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); + $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false); + $a = $this->_add($a[self::VALUE], false, $temp[self::VALUE], false); + $a[self::VALUE] = array_slice($a[self::VALUE], 1); + } + if ($this->_compare($a[self::VALUE], false, $m, false) >= 0) { + $a = $this->_subtract($a[self::VALUE], false, $m, false); + } + return $a[self::VALUE]; + } + + /** + * Prepare a number for use in Montgomery Modular Reductions + * + * @see self::_montgomery() + * @see self::_slidingWindow() + * @access private + * @param array $x + * @param array $n + * @return array + */ + function _prepMontgomery($x, $n) + { + $lhs = new static(); + $lhs->value = array_merge($this->_array_repeat(0, count($n)), $x); + $rhs = new static(); + $rhs->value = $n; + + list(, $temp) = $lhs->divide($rhs); + return $temp->value; + } + + /** + * Modular Inverse of a number mod 2**26 (eg. 67108864) + * + * Based off of the bnpInvDigit function implemented and justified in the following URL: + * + * {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js} + * + * The following URL provides more info: + * + * {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85} + * + * As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For + * instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields + * int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't + * auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that + * the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the + * maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to + * 40 bits, which only 64-bit floating points will support. + * + * Thanks to Pedro Gimeno Fortea for input! + * + * @see self::_montgomery() + * @access private + * @param array $x + * @return int + */ + function _modInverse67108864($x) // 2**26 == 67,108,864 + { + $x = -$x[0]; + $result = $x & 0x3; // x**-1 mod 2**2 + $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4 + $result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8 + $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16 + $result = fmod($result * (2 - fmod($x * $result, self::$baseFull)), self::$baseFull); // x**-1 mod 2**26 + return $result & self::$maxDigit; + } + + /** + * Calculates modular inverses. + * + * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses. + * + * Here's an example: + * + * modInverse($b); + * echo $c->toString(); // outputs 4 + * + * echo "\r\n"; + * + * $d = $a->multiply($c); + * list(, $d) = $d->divide($b); + * echo $d; // outputs 1 (as per the definition of modular inverse) + * ?> + * + * + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger|false + * @access public + * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information. + */ + function modInverse($n) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); + $temp->value = gmp_invert($this->value, $n->value); + + return ($temp->value === false) ? false : $this->_normalize($temp); + } + + static $zero, $one; + if (!isset($zero)) { + $zero = new static(); + $one = new static(1); + } + + // $x mod -$n == $x mod $n. + $n = $n->abs(); + + if ($this->compare($zero) < 0) { + $temp = $this->abs(); + $temp = $temp->modInverse($n); + return $this->_normalize($n->subtract($temp)); + } + + extract($this->extendedGCD($n)); + + if (!$gcd->equals($one)) { + return false; + } + + $x = $x->compare($zero) < 0 ? $x->add($n) : $x; + + return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x); + } + + /** + * Calculates the greatest common divisor and Bezout's identity. + * + * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that + * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which + * combination is returned is dependent upon which mode is in use. See + * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information. + * + * Here's an example: + * + * extendedGCD($b)); + * + * echo $gcd->toString() . "\r\n"; // outputs 21 + * echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21 + * ?> + * + * + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger + * @access public + * @internal Calculates the GCD using the binary xGCD algorithim described in + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes, + * the more traditional algorithim requires "relatively costly multiple-precision divisions". + */ + function extendedGCD($n) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + extract(gmp_gcdext($this->value, $n->value)); + + return array( + 'gcd' => $this->_normalize(new static($g)), + 'x' => $this->_normalize(new static($s)), + 'y' => $this->_normalize(new static($t)) + ); + case self::MODE_BCMATH: + // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works + // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is, + // the basic extended euclidean algorithim is what we're using. + + $u = $this->value; + $v = $n->value; + + $a = '1'; + $b = '0'; + $c = '0'; + $d = '1'; + + while (bccomp($v, '0', 0) != 0) { + $q = bcdiv($u, $v, 0); + + $temp = $u; + $u = $v; + $v = bcsub($temp, bcmul($v, $q, 0), 0); + + $temp = $a; + $a = $c; + $c = bcsub($temp, bcmul($a, $q, 0), 0); + + $temp = $b; + $b = $d; + $d = bcsub($temp, bcmul($b, $q, 0), 0); + } + + return array( + 'gcd' => $this->_normalize(new static($u)), + 'x' => $this->_normalize(new static($a)), + 'y' => $this->_normalize(new static($b)) + ); + } + + $y = $n->copy(); + $x = $this->copy(); + $g = new static(); + $g->value = array(1); + + while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) { + $x->_rshift(1); + $y->_rshift(1); + $g->_lshift(1); + } + + $u = $x->copy(); + $v = $y->copy(); + + $a = new static(); + $b = new static(); + $c = new static(); + $d = new static(); + + $a->value = $d->value = $g->value = array(1); + $b->value = $c->value = array(); + + while (!empty($u->value)) { + while (!($u->value[0] & 1)) { + $u->_rshift(1); + if ((!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1))) { + $a = $a->add($y); + $b = $b->subtract($x); + } + $a->_rshift(1); + $b->_rshift(1); + } + + while (!($v->value[0] & 1)) { + $v->_rshift(1); + if ((!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1))) { + $c = $c->add($y); + $d = $d->subtract($x); + } + $c->_rshift(1); + $d->_rshift(1); + } + + if ($u->compare($v) >= 0) { + $u = $u->subtract($v); + $a = $a->subtract($c); + $b = $b->subtract($d); + } else { + $v = $v->subtract($u); + $c = $c->subtract($a); + $d = $d->subtract($b); + } + } + + return array( + 'gcd' => $this->_normalize($g->multiply($v)), + 'x' => $this->_normalize($c), + 'y' => $this->_normalize($d) + ); + } + + /** + * Calculates the greatest common divisor + * + * Say you have 693 and 609. The GCD is 21. + * + * Here's an example: + * + * extendedGCD($b); + * + * echo $gcd->toString() . "\r\n"; // outputs 21 + * ?> + * + * + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger + * @access public + */ + function gcd($n) + { + extract($this->extendedGCD($n)); + return $gcd; + } + + /** + * Absolute value. + * + * @return \phpseclib\Math\BigInteger + * @access public + */ + function abs() + { + $temp = new static(); + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp->value = gmp_abs($this->value); + break; + case self::MODE_BCMATH: + $temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value; + break; + default: + $temp->value = $this->value; + } + + return $temp; + } + + /** + * Compares two numbers. + * + * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is + * demonstrated thusly: + * + * $x > $y: $x->compare($y) > 0 + * $x < $y: $x->compare($y) < 0 + * $x == $y: $x->compare($y) == 0 + * + * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). + * + * @param \phpseclib\Math\BigInteger $y + * @return int that is < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. + * @access public + * @see self::equals() + * @internal Could return $this->subtract($x), but that's not as fast as what we do do. + */ + function compare($y) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $r = gmp_cmp($this->value, $y->value); + if ($r < -1) { + $r = -1; + } + if ($r > 1) { + $r = 1; + } + return $r; + case self::MODE_BCMATH: + return bccomp($this->value, $y->value, 0); + } + + return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative); + } + + /** + * Compares two numbers. + * + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return int + * @see self::compare() + * @access private + */ + function _compare($x_value, $x_negative, $y_value, $y_negative) + { + if ($x_negative != $y_negative) { + return (!$x_negative && $y_negative) ? 1 : -1; + } + + $result = $x_negative ? -1 : 1; + + if (count($x_value) != count($y_value)) { + return (count($x_value) > count($y_value)) ? $result : -$result; + } + $size = max(count($x_value), count($y_value)); + + $x_value = array_pad($x_value, $size, 0); + $y_value = array_pad($y_value, $size, 0); + + for ($i = count($x_value) - 1; $i >= 0; --$i) { + if ($x_value[$i] != $y_value[$i]) { + return ($x_value[$i] > $y_value[$i]) ? $result : -$result; + } + } + + return 0; + } + + /** + * Tests the equality of two numbers. + * + * If you need to see if one number is greater than or less than another number, use BigInteger::compare() + * + * @param \phpseclib\Math\BigInteger $x + * @return bool + * @access public + * @see self::compare() + */ + function equals($x) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + return gmp_cmp($this->value, $x->value) == 0; + default: + return $this->value === $x->value && $this->is_negative == $x->is_negative; + } + } + + /** + * Set Precision + * + * Some bitwise operations give different results depending on the precision being used. Examples include left + * shift, not, and rotates. + * + * @param int $bits + * @access public + */ + function setPrecision($bits) + { + $this->precision = $bits; + if (MATH_BIGINTEGER_MODE != self::MODE_BCMATH) { + $this->bitmask = new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256); + } else { + $this->bitmask = new static(bcpow('2', $bits, 0)); + } + + $temp = $this->_normalize($this); + $this->value = $temp->value; + } + + /** + * Logical And + * + * @param \phpseclib\Math\BigInteger $x + * @access public + * @internal Implemented per a request by Lluis Pamies i Juarez + * @return \phpseclib\Math\BigInteger + */ + function bitwise_and($x) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); + $temp->value = gmp_and($this->value, $x->value); + + return $this->_normalize($temp); + case self::MODE_BCMATH: + $left = $this->toBytes(); + $right = $x->toBytes(); + + $length = max(strlen($left), strlen($right)); + + $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); + $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); + + return $this->_normalize(new static($left & $right, 256)); + } + + $result = $this->copy(); + + $length = min(count($x->value), count($this->value)); + + $result->value = array_slice($result->value, 0, $length); + + for ($i = 0; $i < $length; ++$i) { + $result->value[$i]&= $x->value[$i]; + } + + return $this->_normalize($result); + } + + /** + * Logical Or + * + * @param \phpseclib\Math\BigInteger $x + * @access public + * @internal Implemented per a request by Lluis Pamies i Juarez + * @return \phpseclib\Math\BigInteger + */ + function bitwise_or($x) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); + $temp->value = gmp_or($this->value, $x->value); + + return $this->_normalize($temp); + case self::MODE_BCMATH: + $left = $this->toBytes(); + $right = $x->toBytes(); + + $length = max(strlen($left), strlen($right)); + + $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); + $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); + + return $this->_normalize(new static($left | $right, 256)); + } + + $length = max(count($this->value), count($x->value)); + $result = $this->copy(); + $result->value = array_pad($result->value, $length, 0); + $x->value = array_pad($x->value, $length, 0); + + for ($i = 0; $i < $length; ++$i) { + $result->value[$i]|= $x->value[$i]; + } + + return $this->_normalize($result); + } + + /** + * Logical Exclusive-Or + * + * @param \phpseclib\Math\BigInteger $x + * @access public + * @internal Implemented per a request by Lluis Pamies i Juarez + * @return \phpseclib\Math\BigInteger + */ + function bitwise_xor($x) + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); + $temp->value = gmp_xor(gmp_abs($this->value), gmp_abs($x->value)); + return $this->_normalize($temp); + case self::MODE_BCMATH: + $left = $this->toBytes(); + $right = $x->toBytes(); + + $length = max(strlen($left), strlen($right)); + + $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); + $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); + + return $this->_normalize(new static($left ^ $right, 256)); + } + + $length = max(count($this->value), count($x->value)); + $result = $this->copy(); + $result->is_negative = false; + $result->value = array_pad($result->value, $length, 0); + $x->value = array_pad($x->value, $length, 0); + + for ($i = 0; $i < $length; ++$i) { + $result->value[$i]^= $x->value[$i]; + } + + return $this->_normalize($result); + } + + /** + * Logical Not + * + * @access public + * @internal Implemented per a request by Lluis Pamies i Juarez + * @return \phpseclib\Math\BigInteger + */ + function bitwise_not() + { + // calculuate "not" without regard to $this->precision + // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0) + $temp = $this->toBytes(); + if ($temp == '') { + return $this->_normalize(new static()); + } + $pre_msb = decbin(ord($temp[0])); + $temp = ~$temp; + $msb = decbin(ord($temp[0])); + if (strlen($msb) == 8) { + $msb = substr($msb, strpos($msb, '0')); + } + $temp[0] = chr(bindec($msb)); + + // see if we need to add extra leading 1's + $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8; + $new_bits = $this->precision - $current_bits; + if ($new_bits <= 0) { + return $this->_normalize(new static($temp, 256)); + } + + // generate as many leading 1's as we need to. + $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3); + $this->_base256_lshift($leading_ones, $current_bits); + + $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT); + + return $this->_normalize(new static($leading_ones | $temp, 256)); + } + + /** + * Logical Right Shift + * + * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. + * + * @param int $shift + * @return \phpseclib\Math\BigInteger + * @access public + * @internal The only version that yields any speed increases is the internal version. + */ + function bitwise_rightShift($shift) + { + $temp = new static(); + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + static $two; + + if (!isset($two)) { + $two = gmp_init('2'); + } + + $temp->value = gmp_div_q($this->value, gmp_pow($two, $shift)); + + break; + case self::MODE_BCMATH: + $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0); + + break; + default: // could just replace _lshift with this, but then all _lshift() calls would need to be rewritten + // and I don't want to do that... + $temp->value = $this->value; + $temp->_rshift($shift); + } + + return $this->_normalize($temp); + } + + /** + * Logical Left Shift + * + * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. + * + * @param int $shift + * @return \phpseclib\Math\BigInteger + * @access public + * @internal The only version that yields any speed increases is the internal version. + */ + function bitwise_leftShift($shift) + { + $temp = new static(); + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + static $two; + + if (!isset($two)) { + $two = gmp_init('2'); + } + + $temp->value = gmp_mul($this->value, gmp_pow($two, $shift)); + + break; + case self::MODE_BCMATH: + $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0); + + break; + default: // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten + // and I don't want to do that... + $temp->value = $this->value; + $temp->_lshift($shift); + } + + return $this->_normalize($temp); + } + + /** + * Logical Left Rotate + * + * Instead of the top x bits being dropped they're appended to the shifted bit string. + * + * @param int $shift + * @return \phpseclib\Math\BigInteger + * @access public + */ + function bitwise_leftRotate($shift) + { + $bits = $this->toBytes(); + + if ($this->precision > 0) { + $precision = $this->precision; + if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { + $mask = $this->bitmask->subtract(new static(1)); + $mask = $mask->toBytes(); + } else { + $mask = $this->bitmask->toBytes(); + } + } else { + $temp = ord($bits[0]); + for ($i = 0; $temp >> $i; ++$i) { + } + $precision = 8 * strlen($bits) - 8 + $i; + $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3); + } + + if ($shift < 0) { + $shift+= $precision; + } + $shift%= $precision; + + if (!$shift) { + return $this->copy(); + } + + $left = $this->bitwise_leftShift($shift); + $left = $left->bitwise_and(new static($mask, 256)); + $right = $this->bitwise_rightShift($precision - $shift); + $result = MATH_BIGINTEGER_MODE != self::MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right); + return $this->_normalize($result); + } + + /** + * Logical Right Rotate + * + * Instead of the bottom x bits being dropped they're prepended to the shifted bit string. + * + * @param int $shift + * @return \phpseclib\Math\BigInteger + * @access public + */ + function bitwise_rightRotate($shift) + { + return $this->bitwise_leftRotate(-$shift); + } + + /** + * Generates a random BigInteger + * + * Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not. + * + * @param int $size + * @return \phpseclib\Math\BigInteger + * @access private + */ + function _random_number_helper($size) + { + if (class_exists('\phpseclib\Crypt\Random')) { + $random = Random::string($size); + } else { + $random = ''; + + if ($size & 1) { + $random.= chr(mt_rand(0, 255)); + } + + $blocks = $size >> 1; + for ($i = 0; $i < $blocks; ++$i) { + // mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems + $random.= pack('n', mt_rand(0, 0xFFFF)); + } + } + + return new static($random, 256); + } + + /** + * Generate a random number + * + * Returns a random number between $min and $max where $min and $max + * can be defined using one of the two methods: + * + * $min->random($max) + * $max->random($min) + * + * @param \phpseclib\Math\BigInteger $arg1 + * @param \phpseclib\Math\BigInteger $arg2 + * @return \phpseclib\Math\BigInteger + * @access public + * @internal The API for creating random numbers used to be $a->random($min, $max), where $a was a BigInteger object. + * That method is still supported for BC purposes. + */ + function random($arg1, $arg2 = false) + { + if ($arg1 === false) { + return false; + } + + if ($arg2 === false) { + $max = $arg1; + $min = $this; + } else { + $min = $arg1; + $max = $arg2; + } + + $compare = $max->compare($min); + + if (!$compare) { + return $this->_normalize($min); + } elseif ($compare < 0) { + // if $min is bigger then $max, swap $min and $max + $temp = $max; + $max = $min; + $min = $temp; + } + + static $one; + if (!isset($one)) { + $one = new static(1); + } + + $max = $max->subtract($min->subtract($one)); + $size = strlen(ltrim($max->toBytes(), chr(0))); + + /* + doing $random % $max doesn't work because some numbers will be more likely to occur than others. + eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145 + would produce 5 whereas the only value of random that could produce 139 would be 139. ie. + not all numbers would be equally likely. some would be more likely than others. + + creating a whole new random number until you find one that is within the range doesn't work + because, for sufficiently small ranges, the likelihood that you'd get a number within that range + would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability + would be pretty high that $random would be greater than $max. + + phpseclib works around this using the technique described here: + + http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string + */ + $random_max = new static(chr(1) . str_repeat("\0", $size), 256); + $random = $this->_random_number_helper($size); + + list($max_multiple) = $random_max->divide($max); + $max_multiple = $max_multiple->multiply($max); + + while ($random->compare($max_multiple) >= 0) { + $random = $random->subtract($max_multiple); + $random_max = $random_max->subtract($max_multiple); + $random = $random->bitwise_leftShift(8); + $random = $random->add($this->_random_number_helper(1)); + $random_max = $random_max->bitwise_leftShift(8); + list($max_multiple) = $random_max->divide($max); + $max_multiple = $max_multiple->multiply($max); + } + list(, $random) = $random->divide($max); + + return $this->_normalize($random->add($min)); + } + + /** + * Generate a random prime number. + * + * If there's not a prime within the given range, false will be returned. + * If more than $timeout seconds have elapsed, give up and return false. + * + * @param \phpseclib\Math\BigInteger $arg1 + * @param \phpseclib\Math\BigInteger $arg2 + * @param int $timeout + * @return Math_BigInteger|false + * @access public + * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}. + */ + function randomPrime($arg1, $arg2 = false, $timeout = false) + { + if ($arg1 === false) { + return false; + } + + if ($arg2 === false) { + $max = $arg1; + $min = $this; + } else { + $min = $arg1; + $max = $arg2; + } + + $compare = $max->compare($min); + + if (!$compare) { + return $min->isPrime() ? $min : false; + } elseif ($compare < 0) { + // if $min is bigger then $max, swap $min and $max + $temp = $max; + $max = $min; + $min = $temp; + } + + static $one, $two; + if (!isset($one)) { + $one = new static(1); + $two = new static(2); + } + + $start = time(); + + $x = $this->random($min, $max); + + // gmp_nextprime() requires PHP 5 >= 5.2.0 per . + if (MATH_BIGINTEGER_MODE == self::MODE_GMP && extension_loaded('gmp')) { + $p = new static(); + $p->value = gmp_nextprime($x->value); + + if ($p->compare($max) <= 0) { + return $p; + } + + if (!$min->equals($x)) { + $x = $x->subtract($one); + } + + return $x->randomPrime($min, $x); + } + + if ($x->equals($two)) { + return $x; + } + + $x->_make_odd(); + if ($x->compare($max) > 0) { + // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range + if ($min->equals($max)) { + return false; + } + $x = $min->copy(); + $x->_make_odd(); + } + + $initial_x = $x->copy(); + + while (true) { + if ($timeout !== false && time() - $start > $timeout) { + return false; + } + + if ($x->isPrime()) { + return $x; + } + + $x = $x->add($two); + + if ($x->compare($max) > 0) { + $x = $min->copy(); + if ($x->equals($two)) { + return $x; + } + $x->_make_odd(); + } + + if ($x->equals($initial_x)) { + return false; + } + } + } + + /** + * Make the current number odd + * + * If the current number is odd it'll be unchanged. If it's even, one will be added to it. + * + * @see self::randomPrime() + * @access private + */ + function _make_odd() + { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + gmp_setbit($this->value, 0); + break; + case self::MODE_BCMATH: + if ($this->value[strlen($this->value) - 1] % 2 == 0) { + $this->value = bcadd($this->value, '1'); + } + break; + default: + $this->value[0] |= 1; + } + } + + /** + * Checks a numer to see if it's prime + * + * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the + * $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads + * on a website instead of just one. + * + * @param \phpseclib\Math\BigInteger $t + * @return bool + * @access public + * @internal Uses the + * {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. See + * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}. + */ + function isPrime($t = false) + { + $length = strlen($this->toBytes()); + + if (!$t) { + // see HAC 4.49 "Note (controlling the error probability)" + // @codingStandardsIgnoreStart + if ($length >= 163) { $t = 2; } // floor(1300 / 8) + else if ($length >= 106) { $t = 3; } // floor( 850 / 8) + else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8) + else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8) + else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8) + else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8) + else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8) + else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8) + else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8) + else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8) + else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8) + else { $t = 27; } + // @codingStandardsIgnoreEnd + } + + // ie. gmp_testbit($this, 0) + // ie. isEven() or !isOdd() + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + return gmp_prob_prime($this->value, $t) != 0; + case self::MODE_BCMATH: + if ($this->value === '2') { + return true; + } + if ($this->value[strlen($this->value) - 1] % 2 == 0) { + return false; + } + break; + default: + if ($this->value == array(2)) { + return true; + } + if (~$this->value[0] & 1) { + return false; + } + } + + static $primes, $zero, $one, $two; + + if (!isset($primes)) { + $primes = array( + 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, + 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, + 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, + 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, + 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, + 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, + 953, 967, 971, 977, 983, 991, 997 + ); + + if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { + for ($i = 0; $i < count($primes); ++$i) { + $primes[$i] = new static($primes[$i]); + } + } + + $zero = new static(); + $one = new static(1); + $two = new static(2); + } + + if ($this->equals($one)) { + return false; + } + + // see HAC 4.4.1 "Random search for probable primes" + if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { + foreach ($primes as $prime) { + list(, $r) = $this->divide($prime); + if ($r->equals($zero)) { + return $this->equals($prime); + } + } + } else { + $value = $this->value; + foreach ($primes as $prime) { + list(, $r) = $this->_divide_digit($value, $prime); + if (!$r) { + return count($value) == 1 && $value[0] == $prime; + } + } + } + + $n = $this->copy(); + $n_1 = $n->subtract($one); + $n_2 = $n->subtract($two); + + $r = $n_1->copy(); + $r_value = $r->value; + // ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); + if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { + $s = 0; + // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier + while ($r->value[strlen($r->value) - 1] % 2 == 0) { + $r->value = bcdiv($r->value, '2', 0); + ++$s; + } + } else { + for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) { + $temp = ~$r_value[$i] & 0xFFFFFF; + for ($j = 1; ($temp >> $j) & 1; ++$j) { + } + if ($j != 25) { + break; + } + } + $s = 26 * $i + $j; + $r->_rshift($s); + } + + for ($i = 0; $i < $t; ++$i) { + $a = $this->random($two, $n_2); + $y = $a->modPow($r, $n); + + if (!$y->equals($one) && !$y->equals($n_1)) { + for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) { + $y = $y->modPow($two, $n); + if ($y->equals($one)) { + return false; + } + } + + if (!$y->equals($n_1)) { + return false; + } + } + } + return true; + } + + /** + * Logical Left Shift + * + * Shifts BigInteger's by $shift bits. + * + * @param int $shift + * @access private + */ + function _lshift($shift) + { + if ($shift == 0) { + return; + } + + $num_digits = (int) ($shift / self::$base); + $shift %= self::$base; + $shift = 1 << $shift; + + $carry = 0; + + for ($i = 0; $i < count($this->value); ++$i) { + $temp = $this->value[$i] * $shift + $carry; + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $this->value[$i] = (int) ($temp - $carry * self::$baseFull); + } + + if ($carry) { + $this->value[count($this->value)] = $carry; + } + + while ($num_digits--) { + array_unshift($this->value, 0); + } + } + + /** + * Logical Right Shift + * + * Shifts BigInteger's by $shift bits. + * + * @param int $shift + * @access private + */ + function _rshift($shift) + { + if ($shift == 0) { + return; + } + + $num_digits = (int) ($shift / self::$base); + $shift %= self::$base; + $carry_shift = self::$base - $shift; + $carry_mask = (1 << $shift) - 1; + + if ($num_digits) { + $this->value = array_slice($this->value, $num_digits); + } + + $carry = 0; + + for ($i = count($this->value) - 1; $i >= 0; --$i) { + $temp = $this->value[$i] >> $shift | $carry; + $carry = ($this->value[$i] & $carry_mask) << $carry_shift; + $this->value[$i] = $temp; + } + + $this->value = $this->_trim($this->value); + } + + /** + * Normalize + * + * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision + * + * @param \phpseclib\Math\BigInteger $result + * @return \phpseclib\Math\BigInteger + * @see self::_trim() + * @access private + */ + function _normalize($result) + { + $result->precision = $this->precision; + $result->bitmask = $this->bitmask; + + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + if ($this->bitmask !== false) { + $flip = gmp_cmp($result->value, gmp_init(0)) < 0; + if ($flip) { + $result->value = gmp_neg($result->value); + } + $result->value = gmp_and($result->value, $result->bitmask->value); + if ($flip) { + $result->value = gmp_neg($result->value); + } + } + + return $result; + case self::MODE_BCMATH: + if (!empty($result->bitmask->value)) { + $result->value = bcmod($result->value, $result->bitmask->value); + } + + return $result; + } + + $value = &$result->value; + + if (!count($value)) { + $result->is_negative = false; + return $result; + } + + $value = $this->_trim($value); + + if (!empty($result->bitmask->value)) { + $length = min(count($value), count($this->bitmask->value)); + $value = array_slice($value, 0, $length); + + for ($i = 0; $i < $length; ++$i) { + $value[$i] = $value[$i] & $this->bitmask->value[$i]; + } + } + + return $result; + } + + /** + * Trim + * + * Removes leading zeros + * + * @param array $value + * @return \phpseclib\Math\BigInteger + * @access private + */ + function _trim($value) + { + for ($i = count($value) - 1; $i >= 0; --$i) { + if ($value[$i]) { + break; + } + unset($value[$i]); + } + + return $value; + } + + /** + * Array Repeat + * + * @param array $input + * @param mixed $multiplier + * @return array + * @access private + */ + function _array_repeat($input, $multiplier) + { + return ($multiplier) ? array_fill(0, $multiplier, $input) : array(); + } + + /** + * Logical Left Shift + * + * Shifts binary strings $shift bits, essentially multiplying by 2**$shift. + * + * @param string $x (by reference) + * @param int $shift + * @return string + * @access private + */ + function _base256_lshift(&$x, $shift) + { + if ($shift == 0) { + return; + } + + $num_bytes = $shift >> 3; // eg. floor($shift/8) + $shift &= 7; // eg. $shift % 8 + + $carry = 0; + for ($i = strlen($x) - 1; $i >= 0; --$i) { + $temp = ord($x[$i]) << $shift | $carry; + $x[$i] = chr($temp); + $carry = $temp >> 8; + } + $carry = ($carry != 0) ? chr($carry) : ''; + $x = $carry . $x . str_repeat(chr(0), $num_bytes); + } + + /** + * Logical Right Shift + * + * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder. + * + * @param string $x (by referenc) + * @param int $shift + * @return string + * @access private + */ + function _base256_rshift(&$x, $shift) + { + if ($shift == 0) { + $x = ltrim($x, chr(0)); + return ''; + } + + $num_bytes = $shift >> 3; // eg. floor($shift/8) + $shift &= 7; // eg. $shift % 8 + + $remainder = ''; + if ($num_bytes) { + $start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes; + $remainder = substr($x, $start); + $x = substr($x, 0, -$num_bytes); + } + + $carry = 0; + $carry_shift = 8 - $shift; + for ($i = 0; $i < strlen($x); ++$i) { + $temp = (ord($x[$i]) >> $shift) | $carry; + $carry = (ord($x[$i]) << $carry_shift) & 0xFF; + $x[$i] = chr($temp); + } + $x = ltrim($x, chr(0)); + + $remainder = chr($carry >> $carry_shift) . $remainder; + + return ltrim($remainder, chr(0)); + } + + // one quirk about how the following functions are implemented is that PHP defines N to be an unsigned long + // at 32-bits, while java's longs are 64-bits. + + /** + * Converts 32-bit integers to bytes. + * + * @param int $x + * @return string + * @access private + */ + function _int2bytes($x) + { + return ltrim(pack('N', $x), chr(0)); + } + + /** + * Converts bytes to 32-bit integers + * + * @param string $x + * @return int + * @access private + */ + function _bytes2int($x) + { + $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT)); + return $temp['int']; + } + + /** + * DER-encode an integer + * + * The ability to DER-encode integers is needed to create RSA public keys for use with OpenSSL + * + * @see self::modPow() + * @access private + * @param int $length + * @return string + */ + function _encodeASN1Length($length) + { + if ($length <= 0x7F) { + return chr($length); + } + + $temp = ltrim(pack('N', $length), chr(0)); + return pack('Ca*', 0x80 | strlen($temp), $temp); + } + + /** + * Single digit division + * + * Even if int64 is being used the division operator will return a float64 value + * if the dividend is not evenly divisible by the divisor. Since a float64 doesn't + * have the precision of int64 this is a problem so, when int64 is being used, + * we'll guarantee that the dividend is divisible by first subtracting the remainder. + * + * @access private + * @param int $x + * @param int $y + * @return int + */ + function _safe_divide($x, $y) + { + if (self::$base === 26) { + return (int) ($x / $y); + } + + // self::$base === 31 + return ($x - ($x % $y)) / $y; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php new file mode 100644 index 0000000..0eb29e9 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php @@ -0,0 +1,349 @@ + + * login('username', 'password')) { + * exit('bad login'); + * } + * $scp = new \phpseclib\Net\SCP($ssh); + * + * $scp->put('abcd', str_repeat('x', 1024*1024)); + * ?> + * + * + * @category Net + * @package SCP + * @author Jim Wigginton + * @copyright 2010 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Net; + +/** + * Pure-PHP implementations of SCP. + * + * @package SCP + * @author Jim Wigginton + * @access public + */ +class SCP +{ + /**#@+ + * @access public + * @see \phpseclib\Net\SCP::put() + */ + /** + * Reads data from a local file. + */ + const SOURCE_LOCAL_FILE = 1; + /** + * Reads data from a string. + */ + const SOURCE_STRING = 2; + /**#@-*/ + + /**#@+ + * @access private + * @see \phpseclib\Net\SCP::_send() + * @see \phpseclib\Net\SCP::_receive() + */ + /** + * SSH1 is being used. + */ + const MODE_SSH1 = 1; + /** + * SSH2 is being used. + */ + const MODE_SSH2 = 2; + /**#@-*/ + + /** + * SSH Object + * + * @var object + * @access private + */ + var $ssh; + + /** + * Packet Size + * + * @var int + * @access private + */ + var $packet_size; + + /** + * Mode + * + * @var int + * @access private + */ + var $mode; + + /** + * Default Constructor. + * + * Connects to an SSH server + * + * @param \phpseclib\Net\SSH1|\phpseclib\Net\SSH2 $ssh + * @return \phpseclib\Net\SCP + * @access public + */ + function __construct($ssh) + { + if ($ssh instanceof SSH2) { + $this->mode = self::MODE_SSH2; + } elseif ($ssh instanceof SSH1) { + $this->packet_size = 50000; + $this->mode = self::MODE_SSH1; + } else { + return; + } + + $this->ssh = $ssh; + } + + /** + * Uploads a file to the SCP server. + * + * By default, \phpseclib\Net\SCP::put() does not read from the local filesystem. $data is dumped directly into $remote_file. + * So, for example, if you set $data to 'filename.ext' and then do \phpseclib\Net\SCP::get(), you will get a file, twelve bytes + * long, containing 'filename.ext' as its contents. + * + * Setting $mode to self::SOURCE_LOCAL_FILE will change the above behavior. With self::SOURCE_LOCAL_FILE, $remote_file will + * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how + * large $remote_file will be, as well. + * + * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take + * care of that, yourself. + * + * @param string $remote_file + * @param string $data + * @param int $mode + * @param callable $callback + * @return bool + * @access public + */ + function put($remote_file, $data, $mode = self::SOURCE_STRING, $callback = null) + { + if (!isset($this->ssh)) { + return false; + } + + if (empty($remote_file)) { + user_error('remote_file cannot be blank', E_USER_NOTICE); + return false; + } + + if (!$this->ssh->exec('scp -t ' . escapeshellarg($remote_file), false)) { // -t = to + return false; + } + + $temp = $this->_receive(); + if ($temp !== chr(0)) { + return false; + } + + if ($this->mode == self::MODE_SSH2) { + $this->packet_size = $this->ssh->packet_size_client_to_server[SSH2::CHANNEL_EXEC] - 4; + } + + $remote_file = basename($remote_file); + + if ($mode == self::SOURCE_STRING) { + $size = strlen($data); + } else { + if (!is_file($data)) { + user_error("$data is not a valid file", E_USER_NOTICE); + return false; + } + + $fp = @fopen($data, 'rb'); + if (!$fp) { + return false; + } + $size = filesize($data); + } + + $this->_send('C0644 ' . $size . ' ' . $remote_file . "\n"); + + $temp = $this->_receive(); + if ($temp !== chr(0)) { + return false; + } + + $sent = 0; + while ($sent < $size) { + $temp = $mode & self::SOURCE_STRING ? substr($data, $sent, $this->packet_size) : fread($fp, $this->packet_size); + $this->_send($temp); + $sent+= strlen($temp); + + if (is_callable($callback)) { + call_user_func($callback, $sent); + } + } + $this->_close(); + + if ($mode != self::SOURCE_STRING) { + fclose($fp); + } + + return true; + } + + /** + * Downloads a file from the SCP server. + * + * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if + * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the + * operation + * + * @param string $remote_file + * @param string $local_file + * @return mixed + * @access public + */ + function get($remote_file, $local_file = false) + { + if (!isset($this->ssh)) { + return false; + } + + if (!$this->ssh->exec('scp -f ' . escapeshellarg($remote_file), false)) { // -f = from + return false; + } + + $this->_send("\0"); + + if (!preg_match('#(?[^ ]+) (?\d+) (?.+)#', rtrim($this->_receive()), $info)) { + return false; + } + + $this->_send("\0"); + + $size = 0; + + if ($local_file !== false) { + $fp = @fopen($local_file, 'wb'); + if (!$fp) { + return false; + } + } + + $content = ''; + while ($size < $info['size']) { + $data = $this->_receive(); + + // Terminate the loop in case the server repeatedly sends an empty response + if ($data === false) { + user_error('No data received from server', E_USER_NOTICE); + return false; + } + + // SCP usually seems to split stuff out into 16k chunks + $size+= strlen($data); + + if ($local_file === false) { + $content.= $data; + } else { + fputs($fp, $data); + } + } + + $this->_close(); + + if ($local_file !== false) { + fclose($fp); + return true; + } + + return $content; + } + + /** + * Sends a packet to an SSH server + * + * @param string $data + * @access private + */ + function _send($data) + { + switch ($this->mode) { + case self::MODE_SSH2: + $this->ssh->_send_channel_packet(SSH2::CHANNEL_EXEC, $data); + break; + case self::MODE_SSH1: + $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($data), $data); + $this->ssh->_send_binary_packet($data); + } + } + + /** + * Receives a packet from an SSH server + * + * @return string + * @access private + */ + function _receive() + { + switch ($this->mode) { + case self::MODE_SSH2: + return $this->ssh->_get_channel_packet(SSH2::CHANNEL_EXEC, true); + case self::MODE_SSH1: + if (!$this->ssh->bitmap) { + return false; + } + while (true) { + $response = $this->ssh->_get_binary_packet(); + switch ($response[SSH1::RESPONSE_TYPE]) { + case NET_SSH1_SMSG_STDOUT_DATA: + if (strlen($response[SSH1::RESPONSE_DATA]) < 4) { + return false; + } + extract(unpack('Nlength', $response[SSH1::RESPONSE_DATA])); + return $this->ssh->_string_shift($response[SSH1::RESPONSE_DATA], $length); + case NET_SSH1_SMSG_STDERR_DATA: + break; + case NET_SSH1_SMSG_EXITSTATUS: + $this->ssh->_send_binary_packet(chr(NET_SSH1_CMSG_EXIT_CONFIRMATION)); + fclose($this->ssh->fsock); + $this->ssh->bitmap = 0; + return false; + default: + user_error('Unknown packet received', E_USER_NOTICE); + return false; + } + } + } + } + + /** + * Closes the connection to an SSH server + * + * @access private + */ + function _close() + { + switch ($this->mode) { + case self::MODE_SSH2: + $this->ssh->_close_channel(SSH2::CHANNEL_EXEC, true); + break; + case self::MODE_SSH1: + $this->ssh->disconnect(); + } + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php new file mode 100644 index 0000000..518c320 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php @@ -0,0 +1,3901 @@ + + * login('username', 'password')) { + * exit('Login Failed'); + * } + * + * echo $sftp->pwd() . "\r\n"; + * $sftp->put('filename.ext', 'hello, world!'); + * print_r($sftp->nlist()); + * ?> + * + * + * @category Net + * @package SFTP + * @author Jim Wigginton + * @copyright 2009 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Net; + +/** + * Pure-PHP implementations of SFTP. + * + * @package SFTP + * @author Jim Wigginton + * @access public + */ +class SFTP extends SSH2 +{ + /** + * SFTP channel constant + * + * \phpseclib\Net\SSH2::exec() uses 0 and \phpseclib\Net\SSH2::read() / \phpseclib\Net\SSH2::write() use 1. + * + * @see \phpseclib\Net\SSH2::_send_channel_packet() + * @see \phpseclib\Net\SSH2::_get_channel_packet() + * @access private + */ + const CHANNEL = 0x100; + + /**#@+ + * @access public + * @see \phpseclib\Net\SFTP::put() + */ + /** + * Reads data from a local file. + */ + const SOURCE_LOCAL_FILE = 1; + /** + * Reads data from a string. + */ + // this value isn't really used anymore but i'm keeping it reserved for historical reasons + const SOURCE_STRING = 2; + /** + * Reads data from callback: + * function callback($length) returns string to proceed, null for EOF + */ + const SOURCE_CALLBACK = 16; + /** + * Resumes an upload + */ + const RESUME = 4; + /** + * Append a local file to an already existing remote file + */ + const RESUME_START = 8; + /**#@-*/ + + /** + * Packet Types + * + * @see self::__construct() + * @var array + * @access private + */ + var $packet_types = array(); + + /** + * Status Codes + * + * @see self::__construct() + * @var array + * @access private + */ + var $status_codes = array(); + + /** + * The Request ID + * + * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support + * concurrent actions, so it's somewhat academic, here. + * + * @var boolean + * @see self::_send_sftp_packet() + * @access private + */ + var $use_request_id = false; + + /** + * The Packet Type + * + * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support + * concurrent actions, so it's somewhat academic, here. + * + * @var int + * @see self::_get_sftp_packet() + * @access private + */ + var $packet_type = -1; + + /** + * Packet Buffer + * + * @var string + * @see self::_get_sftp_packet() + * @access private + */ + var $packet_buffer = ''; + + /** + * Extensions supported by the server + * + * @var array + * @see self::_initChannel() + * @access private + */ + var $extensions = array(); + + /** + * Server SFTP version + * + * @var int + * @see self::_initChannel() + * @access private + */ + var $version; + + /** + * Default Server SFTP version + * + * @var int + * @see self::_initChannel() + * @access private + */ + var $defaultVersion; + + /** + * Preferred SFTP version + * + * @var int + * @see self::_initChannel() + * @access private + */ + var $preferredVersion = 3; + + /** + * Current working directory + * + * @var string + * @see self::realpath() + * @see self::chdir() + * @access private + */ + var $pwd = false; + + /** + * Packet Type Log + * + * @see self::getLog() + * @var array + * @access private + */ + var $packet_type_log = array(); + + /** + * Packet Log + * + * @see self::getLog() + * @var array + * @access private + */ + var $packet_log = array(); + + /** + * Error information + * + * @see self::getSFTPErrors() + * @see self::getLastSFTPError() + * @var array + * @access private + */ + var $sftp_errors = array(); + + /** + * Stat Cache + * + * Rather than always having to open a directory and close it immediately there after to see if a file is a directory + * we'll cache the results. + * + * @see self::_update_stat_cache() + * @see self::_remove_from_stat_cache() + * @see self::_query_stat_cache() + * @var array + * @access private + */ + var $stat_cache = array(); + + /** + * Max SFTP Packet Size + * + * @see self::__construct() + * @see self::get() + * @var array + * @access private + */ + var $max_sftp_packet; + + /** + * Stat Cache Flag + * + * @see self::disableStatCache() + * @see self::enableStatCache() + * @var bool + * @access private + */ + var $use_stat_cache = true; + + /** + * Sort Options + * + * @see self::_comparator() + * @see self::setListOrder() + * @var array + * @access private + */ + var $sortOptions = array(); + + /** + * Canonicalization Flag + * + * Determines whether or not paths should be canonicalized before being + * passed on to the remote server. + * + * @see self::enablePathCanonicalization() + * @see self::disablePathCanonicalization() + * @see self::realpath() + * @var bool + * @access private + */ + var $canonicalize_paths = true; + + /** + * Request Buffers + * + * @see self::_get_sftp_packet() + * @var array + * @access private + */ + var $requestBuffer = array(); + + /** + * Preserve timestamps on file downloads / uploads + * + * @see self::get() + * @see self::put() + * @var bool + * @access private + */ + var $preserveTime = false; + + /** + * Arbitrary Length Packets Flag + * + * Determines whether or not packets of any length should be allowed, + * in cases where the server chooses the packet length (such as + * directory listings). By default, packets are only allowed to be + * 256 * 1024 bytes (SFTP_MAX_MSG_LENGTH from OpenSSH's sftp-common.h) + * + * @see self::enableArbitraryLengthPackets() + * @see self::_get_sftp_packet() + * @var bool + * @access private + */ + var $allow_arbitrary_length_packets = false; + + /** + * Was the last packet due to the channels being closed or not? + * + * @see self::get() + * @see self::get_sftp_packet() + * @var bool + * @access private + */ + var $channel_close = false; + + /** + * Has the SFTP channel been partially negotiated? + * + * @var bool + * @access private + */ + var $partial_init = false; + + /** + * http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1 + * the order, in this case, matters quite a lot - see \phpseclib3\Net\SFTP::_parseAttributes() to understand why + * + * @var array + * @access private + */ + var $attributes = array(); + + /** + * @var array + * @access private + */ + var $open_flags = array(); + + /** + * SFTPv5+ changed the flags up: + * https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3 + * + * @var array + * @access private + */ + var $open_flags5 = array(); + + /** + * http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 + * see \phpseclib\Net\SFTP::_parseLongname() for an explanation + * + * @var array + */ + var $file_types = array(); + + /** + * Default Constructor. + * + * Connects to an SFTP server + * + * @param string $host + * @param int $port + * @param int $timeout + * @return \phpseclib\Net\SFTP + * @access public + */ + function __construct($host, $port = 22, $timeout = 10) + { + parent::__construct($host, $port, $timeout); + + $this->max_sftp_packet = 1 << 15; + + $this->packet_types = array( + 1 => 'NET_SFTP_INIT', + 2 => 'NET_SFTP_VERSION', + 3 => 'NET_SFTP_OPEN', + 4 => 'NET_SFTP_CLOSE', + 5 => 'NET_SFTP_READ', + 6 => 'NET_SFTP_WRITE', + 7 => 'NET_SFTP_LSTAT', + 9 => 'NET_SFTP_SETSTAT', + 10 => 'NET_SFTP_FSETSTAT', + 11 => 'NET_SFTP_OPENDIR', + 12 => 'NET_SFTP_READDIR', + 13 => 'NET_SFTP_REMOVE', + 14 => 'NET_SFTP_MKDIR', + 15 => 'NET_SFTP_RMDIR', + 16 => 'NET_SFTP_REALPATH', + 17 => 'NET_SFTP_STAT', + 18 => 'NET_SFTP_RENAME', + 19 => 'NET_SFTP_READLINK', + 20 => 'NET_SFTP_SYMLINK', + 21 => 'NET_SFTP_LINK', + + 101=> 'NET_SFTP_STATUS', + 102=> 'NET_SFTP_HANDLE', + 103=> 'NET_SFTP_DATA', + 104=> 'NET_SFTP_NAME', + 105=> 'NET_SFTP_ATTRS', + + 200=> 'NET_SFTP_EXTENDED' + ); + $this->status_codes = array( + 0 => 'NET_SFTP_STATUS_OK', + 1 => 'NET_SFTP_STATUS_EOF', + 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE', + 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED', + 4 => 'NET_SFTP_STATUS_FAILURE', + 5 => 'NET_SFTP_STATUS_BAD_MESSAGE', + 6 => 'NET_SFTP_STATUS_NO_CONNECTION', + 7 => 'NET_SFTP_STATUS_CONNECTION_LOST', + 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED', + 9 => 'NET_SFTP_STATUS_INVALID_HANDLE', + 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH', + 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS', + 12 => 'NET_SFTP_STATUS_WRITE_PROTECT', + 13 => 'NET_SFTP_STATUS_NO_MEDIA', + 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM', + 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED', + 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL', + 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT', + 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY', + 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY', + 20 => 'NET_SFTP_STATUS_INVALID_FILENAME', + 21 => 'NET_SFTP_STATUS_LINK_LOOP', + 22 => 'NET_SFTP_STATUS_CANNOT_DELETE', + 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER', + 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY', + 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT', + 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED', + 27 => 'NET_SFTP_STATUS_DELETE_PENDING', + 28 => 'NET_SFTP_STATUS_FILE_CORRUPT', + 29 => 'NET_SFTP_STATUS_OWNER_INVALID', + 30 => 'NET_SFTP_STATUS_GROUP_INVALID', + 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK' + ); + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1 + // the order, in this case, matters quite a lot - see \phpseclib\Net\SFTP::_parseAttributes() to understand why + $this->attributes = array( + 0x00000001 => 'NET_SFTP_ATTR_SIZE', + 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+ + 0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP', // defined in SFTPv4+ + 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS', + 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME', + 0x00000010 => 'NET_SFTP_ATTR_CREATETIME', // SFTPv4+ + 0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME', + 0x00000040 => 'NET_SFTP_ATTR_ACL', + 0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES', + 0x00000200 => 'NET_SFTP_ATTR_BITS', // SFTPv5+ + 0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', // SFTPv6+ + 0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT', + 0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE', + 0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT', + 0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME', + 0x00008000 => 'NET_SFTP_ATTR_CTIME', + // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers + // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in + // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000. + // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored. + (PHP_INT_SIZE == 4 ? -1 : 0xFFFFFFFF) => 'NET_SFTP_ATTR_EXTENDED' + ); + $this->open_flags = array( + 0x00000001 => 'NET_SFTP_OPEN_READ', + 0x00000002 => 'NET_SFTP_OPEN_WRITE', + 0x00000004 => 'NET_SFTP_OPEN_APPEND', + 0x00000008 => 'NET_SFTP_OPEN_CREATE', + 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE', + 0x00000020 => 'NET_SFTP_OPEN_EXCL', + 0x00000040 => 'NET_SFTP_OPEN_TEXT' // defined in SFTPv4 + ); + // SFTPv5+ changed the flags up: + // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3 + $this->open_flags5 = array( + // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened + 0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW', + 0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE', + 0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING', + 0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE', + 0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING', + // the rest of the flags are not supported + 0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', // "the offset field of SS_FXP_WRITE requests is ignored" + 0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC', + 0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE', + 0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ', + 0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE', + 0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE', + 0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY', + 0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW', + 0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE', + 0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO', + 0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP', + 0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM', + 0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER', + ); + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 + // see \phpseclib\Net\SFTP::_parseLongname() for an explanation + $this->file_types = array( + 1 => 'NET_SFTP_TYPE_REGULAR', + 2 => 'NET_SFTP_TYPE_DIRECTORY', + 3 => 'NET_SFTP_TYPE_SYMLINK', + 4 => 'NET_SFTP_TYPE_SPECIAL', + 5 => 'NET_SFTP_TYPE_UNKNOWN', + // the followin types were first defined for use in SFTPv5+ + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 + 6 => 'NET_SFTP_TYPE_SOCKET', + 7 => 'NET_SFTP_TYPE_CHAR_DEVICE', + 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE', + 9 => 'NET_SFTP_TYPE_FIFO' + ); + $this->_define_array( + $this->packet_types, + $this->status_codes, + $this->attributes, + $this->open_flags, + $this->open_flags5, + $this->file_types + ); + + if (!defined('NET_SFTP_QUEUE_SIZE')) { + define('NET_SFTP_QUEUE_SIZE', 32); + } + if (!defined('NET_SFTP_UPLOAD_QUEUE_SIZE')) { + define('NET_SFTP_UPLOAD_QUEUE_SIZE', 1024); + } + } + + /** + * Check a few things before SFTP functions are called + * + * @return bool + * @access public + */ + function _precheck() + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + if ($this->pwd === false) { + return $this->_init_sftp_connection(); + } + + return true; + } + + /** + * Partially initialize an SFTP connection + * + * @return bool + * @access public + */ + function _partial_init_sftp_connection() + { + $this->window_size_server_to_client[self::CHANNEL] = $this->window_size; + + $packet = pack( + 'CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, + strlen('session'), + 'session', + self::CHANNEL, + $this->window_size, + 0x4000 + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = $this->_get_channel_packet(self::CHANNEL, true); + if ($response === false) { + return false; + } elseif ($response === true && $this->isTimeout()) { + return false; + } + + $packet = pack( + 'CNNa*CNa*', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL], + strlen('subsystem'), + 'subsystem', + 1, + strlen('sftp'), + 'sftp' + ); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(self::CHANNEL, true); + if ($response === false) { + // from PuTTY's psftp.exe + $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" . + "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" . + "exec sftp-server"; + // we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does + // is redundant + $packet = pack( + 'CNNa*CNa*', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL], + strlen('exec'), + 'exec', + 1, + strlen($command), + $command + ); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(self::CHANNEL, true); + if ($response === false) { + return false; + } + } elseif ($response === true && $this->isTimeout()) { + return false; + } + + $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA; + + if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_VERSION) { + user_error('Expected SSH_FXP_VERSION'); + return false; + } + + $this->use_request_id = true; + + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nversion', $this->_string_shift($response, 4))); + $this->defaultVersion = $version; + while (!empty($response)) { + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $key = $this->_string_shift($response, $length); + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $value = $this->_string_shift($response, $length); + $this->extensions[$key] = $value; + } + + $this->partial_init = true; + + return true; + } + + /** + * (Re)initializes the SFTP channel + * + * @return bool + * @access private + */ + function _init_sftp_connection() + { + if (!$this->partial_init && !$this->_partial_init_sftp_connection()) { + return false; + } + + /* + A Note on SFTPv4/5/6 support: + states the following: + + "If the client wishes to interoperate with servers that support noncontiguous version + numbers it SHOULD send '3'" + + Given that the server only sends its version number after the client has already done so, the above + seems to be suggesting that v3 should be the default version. This makes sense given that v3 is the + most popular. + + states the following; + + "If the server did not send the "versions" extension, or the version-from-list was not included, the + server MAY send a status response describing the failure, but MUST then close the channel without + processing any further requests." + + So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and + a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4? If it only implements + v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed + in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what \phpseclib\Net\SFTP would do is close the + channel and reopen it with a new and updated SSH_FXP_INIT packet. + */ + $this->version = $this->defaultVersion; + if (isset($this->extensions['versions']) && (!$this->preferredVersion || $this->preferredVersion != $this->version)) { + $versions = explode(',', $this->extensions['versions']); + $supported = array(6, 5, 4); + if ($this->preferredVersion) { + $supported = array_diff($supported, array($this->preferredVersion)); + array_unshift($supported, $this->preferredVersion); + } + foreach ($supported as $ver) { + if (in_array($ver, $versions)) { + if ($ver === $this->version) { + break; + } + $this->version = (int) $ver; + $packet = pack('Na*Na*', strlen('version-select'), 'version-select', strlen($ver), $ver); + if (!$this->_send_sftp_packet(NET_SFTP_EXTENDED, $packet)) { + return false; + } + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + break; + } + } + } + + /* + SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com', + however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's + not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for + one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that + 'newline@vandyke.com' would. + */ + /* + if (isset($this->extensions['newline@vandyke.com'])) { + $this->extensions['newline'] = $this->extensions['newline@vandyke.com']; + unset($this->extensions['newline@vandyke.com']); + } + */ + + if ($this->version < 2 || $this->version > 6) { + return false; + } + + $this->pwd = true; + $this->pwd = $this->_realpath('.'); + if ($this->pwd === false) { + if (!$this->canonicalize_paths) { + user_error('Unable to canonicalize current working directory'); + return false; + } + $this->canonicalize_paths = false; + $this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST); + } + + $this->_update_stat_cache($this->pwd, array()); + + return true; + } + + /** + * Disable the stat cache + * + * @access public + */ + function disableStatCache() + { + $this->use_stat_cache = false; + } + + /** + * Enable the stat cache + * + * @access public + */ + function enableStatCache() + { + $this->use_stat_cache = true; + } + + /** + * Clear the stat cache + * + * @access public + */ + function clearStatCache() + { + $this->stat_cache = array(); + } + + /** + * Enable path canonicalization + * + * @access public + */ + function enablePathCanonicalization() + { + $this->canonicalize_paths = true; + } + + /** + * Disable path canonicalization + * + * If this is enabled then $sftp->pwd() will not return the canonicalized absolute path + * + * @access public + */ + function disablePathCanonicalization() + { + $this->canonicalize_paths = false; + } + + /** + * Enable arbitrary length packets + * + * @access public + */ + function enableArbitraryLengthPackets() + { + $this->allow_arbitrary_length_packets = true; + } + + /** + * Disable arbitrary length packets + * + * @access public + */ + function disableArbitraryLengthPackets() + { + $this->allow_arbitrary_length_packets = false; + } + + /** + * Returns the current directory name + * + * @return mixed + * @access public + */ + function pwd() + { + if (!$this->_precheck()) { + return false; + } + + return $this->pwd; + } + + /** + * Logs errors + * + * @param string $response + * @param int $status + * @access public + */ + function _logError($response, $status = -1) + { + if ($status == -1) { + if (strlen($response) < 4) { + return; + } + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + } + + $error = $this->status_codes[$status]; + + if ($this->version > 2 || strlen($response) < 4) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length); + } else { + $this->sftp_errors[] = $error; + } + } + + /** + * Returns canonicalized absolute pathname + * + * realpath() expands all symbolic links and resolves references to '/./', '/../' and extra '/' characters in the input + * path and returns the canonicalized absolute pathname. + * + * @param string $path + * @return mixed + * @access public + */ + function realpath($path) + { + if (!$this->_precheck()) { + return false; + } + + return $this->_realpath($path); + } + + /** + * Canonicalize the Server-Side Path Name + * + * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it. Returns + * the absolute (canonicalized) path. + * + * If canonicalize_paths has been disabled using disablePathCanonicalization(), $path is returned as-is. + * + * @see self::chdir() + * @see self::disablePathCanonicalization() + * @param string $path + * @return mixed + * @access private + */ + function _realpath($path) + { + if (!$this->canonicalize_paths) { + if ($this->pwd === true) { + return '.'; + } + if (!strlen($path) || $path[0] != '/') { + $path = $this->pwd . '/' . $path; + } + + $parts = explode('/', $path); + $afterPWD = $beforePWD = []; + foreach ($parts as $part) { + switch ($part) { + //case '': // some SFTP servers /require/ double /'s. see https://github.com/phpseclib/phpseclib/pull/1137 + case '.': + break; + case '..': + if (!empty($afterPWD)) { + array_pop($afterPWD); + } else { + $beforePWD[] = '..'; + } + break; + default: + $afterPWD[] = $part; + } + } + + $beforePWD = count($beforePWD) ? implode('/', $beforePWD) : '.'; + return $beforePWD . '/' . implode('/', $afterPWD); + } + + if ($this->pwd === true) { + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9 + if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($path), $path))) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_NAME: + // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following + // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks + // at is the first part and that part is defined the same in SFTP versions 3 through 6. + $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + return $this->_string_shift($response, $length); + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + default: + return false; + } + } + + if (!strlen($path) || $path[0] != '/') { + $path = $this->pwd . '/' . $path; + } + + $path = explode('/', $path); + $new = array(); + foreach ($path as $dir) { + if (!strlen($dir)) { + continue; + } + switch ($dir) { + case '..': + array_pop($new); + case '.': + break; + default: + $new[] = $dir; + } + } + + return '/' . implode('/', $new); + } + + /** + * Changes the current directory + * + * @param string $dir + * @return bool + * @access public + */ + function chdir($dir) + { + if (!$this->_precheck()) { + return false; + } + + // assume current dir if $dir is empty + if ($dir === '') { + $dir = './'; + // suffix a slash if needed + } elseif ($dir[strlen($dir) - 1] != '/') { + $dir.= '/'; + } + + $dir = $this->_realpath($dir); + + // confirm that $dir is, in fact, a valid directory + if ($this->use_stat_cache && is_array($this->_query_stat_cache($dir))) { + $this->pwd = $dir; + return true; + } + + // we could do a stat on the alleged $dir to see if it's a directory but that doesn't tell us + // the currently logged in user has the appropriate permissions or not. maybe you could see if + // the file's uid / gid match the currently logged in user's uid / gid but how there's no easy + // way to get those with SFTP + + if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) { + return false; + } + + // see \phpseclib\Net\SFTP::nlist() for a more thorough explanation of the following + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + $handle = substr($response, 4); + break; + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; + } + + if (!$this->_close_handle($handle)) { + return false; + } + + $this->_update_stat_cache($dir, array()); + + $this->pwd = $dir; + return true; + } + + /** + * Returns a list of files in the given directory + * + * @param string $dir + * @param bool $recursive + * @return mixed + * @access public + */ + function nlist($dir = '.', $recursive = false) + { + return $this->_nlist_helper($dir, $recursive, ''); + } + + /** + * Helper method for nlist + * + * @param string $dir + * @param bool $recursive + * @param string $relativeDir + * @return mixed + * @access private + */ + function _nlist_helper($dir, $recursive, $relativeDir) + { + $files = $this->_list($dir, false); + + // If we get an int back, then that is an "unexpected" status. + // We do not have a file list, so return false. + if (is_int($files)) { + return false; + } + + if (!$recursive || $files === false) { + return $files; + } + + $result = array(); + foreach ($files as $value) { + if ($value == '.' || $value == '..') { + if ($relativeDir == '') { + $result[] = $value; + } + continue; + } + if (is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $value)))) { + $temp = $this->_nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/'); + $temp = is_array($temp) ? $temp : array(); + $result = array_merge($result, $temp); + } else { + $result[] = $relativeDir . $value; + } + } + + return $result; + } + + /** + * Returns a detailed list of files in the given directory + * + * @param string $dir + * @param bool $recursive + * @return mixed + * @access public + */ + function rawlist($dir = '.', $recursive = false) + { + $files = $this->_list($dir, true); + + // If we get an int back, then that is an "unexpected" status. + // We do not have a file list, so return false. + if (is_int($files)) { + return false; + } + + if (!$recursive || $files === false) { + return $files; + } + + static $depth = 0; + + foreach ($files as $key => $value) { + if ($depth != 0 && $key == '..') { + unset($files[$key]); + continue; + } + $is_directory = false; + if ($key != '.' && $key != '..') { + if ($this->use_stat_cache) { + $is_directory = is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key))); + } else { + $stat = $this->lstat($dir . '/' . $key); + $is_directory = $stat && $stat['type'] === NET_SFTP_TYPE_DIRECTORY; + } + } + + if ($is_directory) { + $depth++; + $files[$key] = $this->rawlist($dir . '/' . $key, true); + $depth--; + } else { + $files[$key] = (object) $value; + } + } + + return $files; + } + + /** + * Reads a list, be it detailed or not, of files in the given directory + * + * @param string $dir + * @param bool $raw + * @return mixed + * @access private + */ + function _list($dir, $raw = true) + { + if (!$this->_precheck()) { + return false; + } + + $dir = $this->_realpath($dir . '/'); + if ($dir === false) { + return false; + } + + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2 + if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2 + // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that + // represent the length of the string and leave it at that + $handle = substr($response, 4); + break; + case NET_SFTP_STATUS: + // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + $this->_logError($response, $status); + return $status; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; + } + + $this->_update_stat_cache($dir, array()); + + $contents = array(); + while (true) { + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2 + // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many + // SSH_MSG_CHANNEL_DATA messages is not known to me. + if (!$this->_send_sftp_packet(NET_SFTP_READDIR, pack('Na*', strlen($handle), $handle))) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_NAME: + if (strlen($response) < 4) { + return false; + } + extract(unpack('Ncount', $this->_string_shift($response, 4))); + for ($i = 0; $i < $count; $i++) { + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $shortname = $this->_string_shift($response, $length); + // SFTPv4 "removed the long filename from the names structure-- it can now be + // built from information available in the attrs structure." + if ($this->version < 4) { + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $longname = $this->_string_shift($response, $length); + } + $attributes = $this->_parseAttributes($response); + if (!isset($attributes['type']) && $this->version < 4) { + $fileType = $this->_parseLongname($longname); + if ($fileType) { + $attributes['type'] = $fileType; + } + } + $contents[$shortname] = $attributes + array('filename' => $shortname); + + if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) { + $this->_update_stat_cache($dir . '/' . $shortname, array()); + } else { + if ($shortname == '..') { + $temp = $this->_realpath($dir . '/..') . '/.'; + } else { + $temp = $dir . '/' . $shortname; + } + $this->_update_stat_cache($temp, (object) array('lstat' => $attributes)); + } + // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the + // final SSH_FXP_STATUS packet should tell us that, already. + } + break; + case NET_SFTP_STATUS: + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_EOF) { + $this->_logError($response, $status); + return $status; + } + break 2; + default: + user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); + return false; + } + } + + if (!$this->_close_handle($handle)) { + return false; + } + + if (count($this->sortOptions)) { + uasort($contents, array(&$this, '_comparator')); + } + + return $raw ? $contents : array_map('strval', array_keys($contents)); + } + + /** + * Compares two rawlist entries using parameters set by setListOrder() + * + * Intended for use with uasort() + * + * @param array $a + * @param array $b + * @return int + * @access private + */ + function _comparator($a, $b) + { + switch (true) { + case $a['filename'] === '.' || $b['filename'] === '.': + if ($a['filename'] === $b['filename']) { + return 0; + } + return $a['filename'] === '.' ? -1 : 1; + case $a['filename'] === '..' || $b['filename'] === '..': + if ($a['filename'] === $b['filename']) { + return 0; + } + return $a['filename'] === '..' ? -1 : 1; + case isset($a['type']) && $a['type'] === NET_SFTP_TYPE_DIRECTORY: + if (!isset($b['type'])) { + return 1; + } + if ($b['type'] !== $a['type']) { + return -1; + } + break; + case isset($b['type']) && $b['type'] === NET_SFTP_TYPE_DIRECTORY: + return 1; + } + foreach ($this->sortOptions as $sort => $order) { + if (!isset($a[$sort]) || !isset($b[$sort])) { + if (isset($a[$sort])) { + return -1; + } + if (isset($b[$sort])) { + return 1; + } + return 0; + } + switch ($sort) { + case 'filename': + $result = strcasecmp($a['filename'], $b['filename']); + if ($result) { + return $order === SORT_DESC ? -$result : $result; + } + break; + case 'permissions': + case 'mode': + $a[$sort]&= 07777; + $b[$sort]&= 07777; + default: + if ($a[$sort] === $b[$sort]) { + break; + } + return $order === SORT_ASC ? $a[$sort] - $b[$sort] : $b[$sort] - $a[$sort]; + } + } + } + + /** + * Defines how nlist() and rawlist() will be sorted - if at all. + * + * If sorting is enabled directories and files will be sorted independently with + * directories appearing before files in the resultant array that is returned. + * + * Any parameter returned by stat is a valid sort parameter for this function. + * Filename comparisons are case insensitive. + * + * Examples: + * + * $sftp->setListOrder('filename', SORT_ASC); + * $sftp->setListOrder('size', SORT_DESC, 'filename', SORT_ASC); + * $sftp->setListOrder(true); + * Separates directories from files but doesn't do any sorting beyond that + * $sftp->setListOrder(); + * Don't do any sort of sorting + * + * @access public + */ + function setListOrder() + { + $this->sortOptions = array(); + $args = func_get_args(); + if (empty($args)) { + return; + } + $len = count($args) & 0x7FFFFFFE; + for ($i = 0; $i < $len; $i+=2) { + $this->sortOptions[$args[$i]] = $args[$i + 1]; + } + if (!count($this->sortOptions)) { + $this->sortOptions = array('bogus' => true); + } + } + + /** + * Returns the file size, in bytes, or false, on failure + * + * Files larger than 4GB will show up as being exactly 4GB. + * + * @param string $filename + * @return mixed + * @access public + */ + function size($filename) + { + $result = $this->stat($filename); + if ($result === false) { + return false; + } + return isset($result['size']) ? $result['size'] : -1; + } + + /** + * Save files / directories to cache + * + * @param string $path + * @param mixed $value + * @access private + */ + function _update_stat_cache($path, $value) + { + if ($this->use_stat_cache === false) { + return; + } + + // preg_replace('#^/|/(?=/)|/$#', '', $dir) == str_replace('//', '/', trim($path, '/')) + $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); + + $temp = &$this->stat_cache; + $max = count($dirs) - 1; + foreach ($dirs as $i => $dir) { + // if $temp is an object that means one of two things. + // 1. a file was deleted and changed to a directory behind phpseclib's back + // 2. it's a symlink. when lstat is done it's unclear what it's a symlink to + if (is_object($temp)) { + $temp = array(); + } + if (!isset($temp[$dir])) { + $temp[$dir] = array(); + } + if ($i === $max) { + if (is_object($temp[$dir]) && is_object($value)) { + if (!isset($value->stat) && isset($temp[$dir]->stat)) { + $value->stat = $temp[$dir]->stat; + } + if (!isset($value->lstat) && isset($temp[$dir]->lstat)) { + $value->lstat = $temp[$dir]->lstat; + } + } + $temp[$dir] = $value; + break; + } + $temp = &$temp[$dir]; + } + } + + /** + * Remove files / directories from cache + * + * @param string $path + * @return bool + * @access private + */ + function _remove_from_stat_cache($path) + { + $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); + + $temp = &$this->stat_cache; + $max = count($dirs) - 1; + foreach ($dirs as $i => $dir) { + if (!is_array($temp)) { + return false; + } + if ($i === $max) { + unset($temp[$dir]); + return true; + } + if (!isset($temp[$dir])) { + return false; + } + $temp = &$temp[$dir]; + } + } + + /** + * Checks cache for path + * + * Mainly used by file_exists + * + * @param string $path + * @return mixed + * @access private + */ + function _query_stat_cache($path) + { + $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); + + $temp = &$this->stat_cache; + foreach ($dirs as $dir) { + if (!is_array($temp)) { + return null; + } + if (!isset($temp[$dir])) { + return null; + } + $temp = &$temp[$dir]; + } + return $temp; + } + + /** + * Returns general information about a file. + * + * Returns an array on success and false otherwise. + * + * @param string $filename + * @return mixed + * @access public + */ + function stat($filename) + { + if (!$this->_precheck()) { + return false; + } + + $filename = $this->_realpath($filename); + if ($filename === false) { + return false; + } + + if ($this->use_stat_cache) { + $result = $this->_query_stat_cache($filename); + if (is_array($result) && isset($result['.']) && isset($result['.']->stat)) { + return $result['.']->stat; + } + if (is_object($result) && isset($result->stat)) { + return $result->stat; + } + } + + $stat = $this->_stat($filename, NET_SFTP_STAT); + if ($stat === false) { + $this->_remove_from_stat_cache($filename); + return false; + } + if (isset($stat['type'])) { + if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename.= '/.'; + } + $this->_update_stat_cache($filename, (object) array('stat' => $stat)); + return $stat; + } + + $pwd = $this->pwd; + $stat['type'] = $this->chdir($filename) ? + NET_SFTP_TYPE_DIRECTORY : + NET_SFTP_TYPE_REGULAR; + $this->pwd = $pwd; + + if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename.= '/.'; + } + $this->_update_stat_cache($filename, (object) array('stat' => $stat)); + + return $stat; + } + + /** + * Returns general information about a file or symbolic link. + * + * Returns an array on success and false otherwise. + * + * @param string $filename + * @return mixed + * @access public + */ + function lstat($filename) + { + if (!$this->_precheck()) { + return false; + } + + $filename = $this->_realpath($filename); + if ($filename === false) { + return false; + } + + if ($this->use_stat_cache) { + $result = $this->_query_stat_cache($filename); + if (is_array($result) && isset($result['.']) && isset($result['.']->lstat)) { + return $result['.']->lstat; + } + if (is_object($result) && isset($result->lstat)) { + return $result->lstat; + } + } + + $lstat = $this->_stat($filename, NET_SFTP_LSTAT); + if ($lstat === false) { + $this->_remove_from_stat_cache($filename); + return false; + } + if (isset($lstat['type'])) { + if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename.= '/.'; + } + $this->_update_stat_cache($filename, (object) array('lstat' => $lstat)); + return $lstat; + } + + $stat = $this->_stat($filename, NET_SFTP_STAT); + + if ($lstat != $stat) { + $lstat = array_merge($lstat, array('type' => NET_SFTP_TYPE_SYMLINK)); + $this->_update_stat_cache($filename, (object) array('lstat' => $lstat)); + return $stat; + } + + $pwd = $this->pwd; + $lstat['type'] = $this->chdir($filename) ? + NET_SFTP_TYPE_DIRECTORY : + NET_SFTP_TYPE_REGULAR; + $this->pwd = $pwd; + + if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename.= '/.'; + } + $this->_update_stat_cache($filename, (object) array('lstat' => $lstat)); + + return $lstat; + } + + /** + * Returns general information about a file or symbolic link + * + * Determines information without calling \phpseclib\Net\SFTP::realpath(). + * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT. + * + * @param string $filename + * @param int $type + * @return mixed + * @access private + */ + function _stat($filename, $type) + { + // SFTPv4+ adds an additional 32-bit integer field - flags - to the following: + $packet = pack('Na*', strlen($filename), $filename); + if (!$this->_send_sftp_packet($type, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_ATTRS: + return $this->_parseAttributes($response); + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + } + + user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); + return false; + } + + /** + * Truncates a file to a given length + * + * @param string $filename + * @param int $new_size + * @return bool + * @access public + */ + function truncate($filename, $new_size) + { + $attr = pack('N3', NET_SFTP_ATTR_SIZE, $new_size / 4294967296, $new_size); // 4294967296 == 0x100000000 == 1<<32 + + return $this->_setstat($filename, $attr, false); + } + + /** + * Sets access and modification time of file. + * + * If the file does not exist, it will be created. + * + * @param string $filename + * @param int $time + * @param int $atime + * @return bool + * @access public + */ + function touch($filename, $time = null, $atime = null) + { + if (!$this->_precheck()) { + return false; + } + + $filename = $this->_realpath($filename); + if ($filename === false) { + return false; + } + + if (!isset($time)) { + $time = time(); + } + if (!isset($atime)) { + $atime = $time; + } + + if ($this->version < 4) { + $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $atime, $time); + } else { + $attr = pack( + 'N5', + NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME, + $atime / 4294967296, + $atime, + $time / 4294967296, + $time + ); + } + + $packet = pack('Na*', strlen($filename), $filename); + $packet.= $this->version >= 5 ? + pack('N2', 0, NET_SFTP_OPEN_OPEN_EXISTING) : + pack('N', NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL); + $packet.= $attr; + + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + return $this->_close_handle(substr($response, 4)); + case NET_SFTP_STATUS: + $this->_logError($response); + break; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; + } + + return $this->_setstat($filename, $attr, false); + } + + /** + * Changes file or directory owner + * + * $uid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string + * would be of the form "user@dns_domain" but it does not need to be. + * `$sftp->getSupportedVersions()['version']` will return the specific version + * that's being used. + * + * Returns true on success or false on error. + * + * @param string $filename + * @param int|string $uid + * @param bool $recursive + * @return bool + * @access public + */ + function chown($filename, $uid, $recursive = false) + { + /* + quoting , + + "To avoid a representation that is tied to a particular underlying + implementation at the client or server, the use of UTF-8 strings has + been chosen. The string should be of the form "user@dns_domain". + This will allow for a client and server that do not use the same + local representation the ability to translate to a common syntax that + can be interpreted by both. In the case where there is no + translation available to the client or server, the attribute value + must be constructed without the "@"." + + phpseclib _could_ auto append the dns_domain to $uid BUT what if it shouldn't + have one? phpseclib would have no way of knowing so rather than guess phpseclib + will just use whatever value the user provided + */ + + $attr = $this->version < 4 ? + // quoting , + // "if the owner or group is specified as -1, then that ID is not changed" + pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1) : + // quoting , + // "If either the owner or group field is zero length, the field should be + // considered absent, and no change should be made to that specific field + // during a modification operation" + pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, strlen($uid), $uid, 0, ''); + + return $this->_setstat($filename, $attr, $recursive); + } + + /** + * Changes file or directory group + * + * $gid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string + * would be of the form "user@dns_domain" but it does not need to be. + * `$sftp->getSupportedVersions()['version']` will return the specific version + * that's being used. + * + * Returns true on success or false on error. + * + * @param string $filename + * @param int|string $gid + * @param bool $recursive + * @return bool + * @access public + */ + function chgrp($filename, $gid, $recursive = false) + { + $attr = $this->version < 4 ? + pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid) : + pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, 0, '', strlen($gid), $gid); + + return $this->_setstat($filename, $attr, $recursive); + } + + /** + * Set permissions on a file. + * + * Returns the new file permissions on success or false on error. + * If $recursive is true than this just returns true or false. + * + * @param int $mode + * @param string $filename + * @param bool $recursive + * @return mixed + * @access public + */ + function chmod($mode, $filename, $recursive = false) + { + if (is_string($mode) && is_int($filename)) { + $temp = $mode; + $mode = $filename; + $filename = $temp; + } + + $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); + if (!$this->_setstat($filename, $attr, $recursive)) { + return false; + } + if ($recursive) { + return true; + } + + $filename = $this->realpath($filename); + // rather than return what the permissions *should* be, we'll return what they actually are. this will also + // tell us if the file actually exists. + // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following: + $packet = pack('Na*', strlen($filename), $filename); + if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_ATTRS: + $attrs = $this->_parseAttributes($response); + return $attrs['permissions']; + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + } + + user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); + return false; + } + + /** + * Sets information about a file + * + * @param string $filename + * @param string $attr + * @param bool $recursive + * @return bool + * @access private + */ + function _setstat($filename, $attr, $recursive) + { + if (!$this->_precheck()) { + return false; + } + + $filename = $this->_realpath($filename); + if ($filename === false) { + return false; + } + + $this->_remove_from_stat_cache($filename); + + if ($recursive) { + $i = 0; + $result = $this->_setstat_recursive($filename, $attr, $i); + $this->_read_put_responses($i); + return $result; + } + + $packet = $this->version >= 4 ? + pack('Na*a*Ca*', strlen($filename), $filename, substr($attr, 0, 4), NET_SFTP_TYPE_UNKNOWN, substr($attr, 4)) : + pack('Na*a*', strlen($filename), $filename, $attr); + if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) { + return false; + } + + /* + "Because some systems must use separate system calls to set various attributes, it is possible that a failure + response will be returned, but yet some of the attributes may be have been successfully modified. If possible, + servers SHOULD avoid this situation; however, clients MUST be aware that this is possible." + + -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6 + */ + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + return true; + } + + /** + * Recursively sets information on directories on the SFTP server + * + * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. + * + * @param string $path + * @param string $attr + * @param int $i + * @return bool + * @access private + */ + function _setstat_recursive($path, $attr, &$i) + { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + $entries = $this->_list($path, true); + + if ($entries === false || is_int($entries)) { + return $this->_setstat($path, $attr, false); + } + + // normally $entries would have at least . and .. but it might not if the directories + // permissions didn't allow reading + if (empty($entries)) { + return false; + } + + unset($entries['.'], $entries['..']); + foreach ($entries as $filename => $props) { + if (!isset($props['type'])) { + return false; + } + + $temp = $path . '/' . $filename; + if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { + if (!$this->_setstat_recursive($temp, $attr, $i)) { + return false; + } + } else { + $packet = $this->version >= 4 ? + pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) : + pack('Na*a*', strlen($temp), $temp, $attr); + if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) { + return false; + } + + $i++; + + if ($i >= NET_SFTP_QUEUE_SIZE) { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + } + } + } + + $packet = $this->version >= 4 ? + pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) : + pack('Na*a*', strlen($temp), $temp, $attr); + if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) { + return false; + } + + $i++; + + if ($i >= NET_SFTP_QUEUE_SIZE) { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + } + + return true; + } + + /** + * Return the target of a symbolic link + * + * @param string $link + * @return mixed + * @access public + */ + function readlink($link) + { + if (!$this->_precheck()) { + return false; + } + + $link = $this->_realpath($link); + + if (!$this->_send_sftp_packet(NET_SFTP_READLINK, pack('Na*', strlen($link), $link))) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_NAME: + break; + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + default: + user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); + return false; + } + + if (strlen($response) < 4) { + return false; + } + extract(unpack('Ncount', $this->_string_shift($response, 4))); + // the file isn't a symlink + if (!$count) { + return false; + } + + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + return $this->_string_shift($response, $length); + } + + /** + * Create a symlink + * + * symlink() creates a symbolic link to the existing target with the specified name link. + * + * @param string $target + * @param string $link + * @return bool + * @access public + */ + function symlink($target, $link) + { + if (!$this->_precheck()) { + return false; + } + + //$target = $this->_realpath($target); + $link = $this->_realpath($link); + + /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-09#section-12.1 : + + Changed the SYMLINK packet to be LINK and give it the ability to + create hard links. Also change it's packet number because many + implementation implemented SYMLINK with the arguments reversed. + Hopefully the new argument names make it clear which way is which. + */ + if ($this->version == 6) { + $type = NET_SFTP_LINK; + $packet = pack('Na*Na*C', strlen($link), $link, strlen($target), $target, 1); + } else { + $type = NET_SFTP_SYMLINK; + /* quoting http://bxr.su/OpenBSD/usr.bin/ssh/PROTOCOL#347 : + + 3.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK + + When OpenSSH's sftp-server was implemented, the order of the arguments + to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately, + the reversal was not noticed until the server was widely deployed. Since + fixing this to follow the specification would cause incompatibility, the + current order was retained. For correct operation, clients should send + SSH_FXP_SYMLINK as follows: + + uint32 id + string targetpath + string linkpath */ + $packet = substr($this->server_identifier, 0, 15) == 'SSH-2.0-OpenSSH' ? + pack('Na*Na*', strlen($target), $target, strlen($link), $link) : + pack('Na*Na*', strlen($link), $link, strlen($target), $target); + } + if (!$this->_send_sftp_packet($type, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + return true; + } + + /** + * Creates a directory. + * + * @param string $dir + * @param int $mode + * @param bool $recursive + * @return bool + * @access public + */ + function mkdir($dir, $mode = -1, $recursive = false) + { + if (!$this->_precheck()) { + return false; + } + + $dir = $this->_realpath($dir); + + if ($recursive) { + $dirs = explode('/', preg_replace('#/(?=/)|/$#', '', $dir)); + if (empty($dirs[0])) { + array_shift($dirs); + $dirs[0] = '/' . $dirs[0]; + } + for ($i = 0; $i < count($dirs); $i++) { + $temp = array_slice($dirs, 0, $i + 1); + $temp = implode('/', $temp); + $result = $this->_mkdir_helper($temp, $mode); + } + return $result; + } + + return $this->_mkdir_helper($dir, $mode); + } + + /** + * Helper function for directory creation + * + * @param string $dir + * @param int $mode + * @return bool + * @access private + */ + function _mkdir_helper($dir, $mode) + { + // send SSH_FXP_MKDIR without any attributes (that's what the \0\0\0\0 is doing) + if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*a*', strlen($dir), $dir, "\0\0\0\0"))) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + if ($mode !== -1) { + $this->chmod($mode, $dir); + } + + return true; + } + + /** + * Removes a directory. + * + * @param string $dir + * @return bool + * @access public + */ + function rmdir($dir) + { + if (!$this->_precheck()) { + return false; + } + + $dir = $this->_realpath($dir); + if ($dir === false) { + return false; + } + + if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($dir), $dir))) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED? + $this->_logError($response, $status); + return false; + } + + $this->_remove_from_stat_cache($dir); + // the following will do a soft delete, which would be useful if you deleted a file + // and then tried to do a stat on the deleted file. the above, in contrast, does + // a hard delete + //$this->_update_stat_cache($dir, false); + + return true; + } + + /** + * Uploads a file to the SFTP server. + * + * By default, \phpseclib\Net\SFTP::put() does not read from the local filesystem. $data is dumped directly into $remote_file. + * So, for example, if you set $data to 'filename.ext' and then do \phpseclib\Net\SFTP::get(), you will get a file, twelve bytes + * long, containing 'filename.ext' as its contents. + * + * Setting $mode to self::SOURCE_LOCAL_FILE will change the above behavior. With self::SOURCE_LOCAL_FILE, $remote_file will + * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how + * large $remote_file will be, as well. + * + * Setting $mode to self::SOURCE_CALLBACK will use $data as callback function, which gets only one parameter -- number + * of bytes to return, and returns a string if there is some data or null if there is no more data + * + * If $data is a resource then it'll be used as a resource instead. + * + * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take + * care of that, yourself. + * + * $mode can take an additional two parameters - self::RESUME and self::RESUME_START. These are bitwise AND'd with + * $mode. So if you want to resume upload of a 300mb file on the local file system you'd set $mode to the following: + * + * self::SOURCE_LOCAL_FILE | self::RESUME + * + * If you wanted to simply append the full contents of a local file to the full contents of a remote file you'd replace + * self::RESUME with self::RESUME_START. + * + * If $mode & (self::RESUME | self::RESUME_START) then self::RESUME_START will be assumed. + * + * $start and $local_start give you more fine grained control over this process and take precident over self::RESUME + * when they're non-negative. ie. $start could let you write at the end of a file (like self::RESUME) or in the middle + * of one. $local_start could let you start your reading from the end of a file (like self::RESUME_START) or in the + * middle of one. + * + * Setting $local_start to > 0 or $mode | self::RESUME_START doesn't do anything unless $mode | self::SOURCE_LOCAL_FILE. + * + * @param string $remote_file + * @param string|resource $data + * @param int $mode + * @param int $start + * @param int $local_start + * @param callable|null $progressCallback + * @return bool + * @access public + * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - \phpseclib\Net\SFTP::setMode(). + */ + function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null) + { + if (!$this->_precheck()) { + return false; + } + + $remote_file = $this->_realpath($remote_file); + if ($remote_file === false) { + return false; + } + + $this->_remove_from_stat_cache($remote_file); + + if ($this->version >= 5) { + $flags = NET_SFTP_OPEN_OPEN_OR_CREATE; + } else { + $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE; + // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file." + // in practice, it doesn't seem to do that. + //$flags|= ($mode & SFTP::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE; + } + + if ($start >= 0) { + $offset = $start; + } elseif ($mode & self::RESUME) { + // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called + $size = $this->size($remote_file); + $offset = $size !== false ? $size : 0; + } else { + $offset = 0; + if ($this->version >= 5) { + $flags = NET_SFTP_OPEN_CREATE_TRUNCATE; + } else { + $flags|= NET_SFTP_OPEN_TRUNCATE; + } + } + + $packet = pack('Na*', strlen($remote_file), $remote_file); + $packet.= $this->version >= 5 ? + pack('N3', 0, $flags, 0) : + pack('N2', $flags, 0); + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + $handle = substr($response, 4); + break; + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; + } + + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3 + $dataCallback = false; + switch (true) { + case $mode & self::SOURCE_CALLBACK: + if (!is_callable($data)) { + user_error("\$data should be is_callable() if you specify SOURCE_CALLBACK flag"); + } + $dataCallback = $data; + // do nothing + break; + case is_resource($data): + $mode = $mode & ~self::SOURCE_LOCAL_FILE; + $info = stream_get_meta_data($data); + if (isset($info['wrapper_type']) && $info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') { + $fp = fopen('php://memory', 'w+'); + stream_copy_to_stream($data, $fp); + rewind($fp); + } else { + $fp = $data; + } + break; + case $mode & self::SOURCE_LOCAL_FILE: + if (!is_file($data)) { + user_error("$data is not a valid file"); + return false; + } + $fp = @fopen($data, 'rb'); + if (!$fp) { + return false; + } + } + + if (isset($fp)) { + $stat = fstat($fp); + $size = !empty($stat) ? $stat['size'] : 0; + + if ($local_start >= 0) { + fseek($fp, $local_start); + $size-= $local_start; + } + } elseif ($dataCallback) { + $size = 0; + } else { + $size = strlen($data); + } + + $sent = 0; + $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; + + $sftp_packet_size = $this->max_sftp_packet; + // make the SFTP packet be exactly the SFTP packet size by including the bytes in the NET_SFTP_WRITE packets "header" + $sftp_packet_size-= strlen($handle) + 25; + $i = $j = 0; + while ($dataCallback || ($size === 0 || $sent < $size)) { + if ($dataCallback) { + $temp = call_user_func($dataCallback, $sftp_packet_size); + if (is_null($temp)) { + break; + } + } else { + $temp = isset($fp) ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size); + if ($temp === false || $temp === '') { + break; + } + } + + $subtemp = $offset + $sent; + $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp); + if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet, $j)) { + if ($mode & self::SOURCE_LOCAL_FILE) { + fclose($fp); + } + return false; + } + $sent+= strlen($temp); + if (is_callable($progressCallback)) { + call_user_func($progressCallback, $sent); + } + + $i++; + $j++; + + if ($i == NET_SFTP_UPLOAD_QUEUE_SIZE) { + if (!$this->_read_put_responses($i)) { + $i = 0; + break; + } + $i = 0; + } + } + + $result = $this->_close_handle($handle); + + if (!$this->_read_put_responses($i)) { + if ($mode & self::SOURCE_LOCAL_FILE) { + fclose($fp); + } + $this->_close_handle($handle); + return false; + } + + if ($mode & SFTP::SOURCE_LOCAL_FILE) { + if (isset($fp) && is_resource($fp)) { + fclose($fp); + } + + if ($this->preserveTime) { + $stat = stat($data); + if ($this->version < 4) { + $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $stat['atime'], $stat['mtime']); + } else { + $attr = pack( + 'N5', + NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME, + $stat['atime'] / 4294967296, + $stat['atime'], + $stat['mtime'] / 4294967296, + $stat['mtime'] + ); + } + + if (!$this->_setstat($remote_file, $attr, false)) { + user_error('Error setting file time'); + } + } + } + + return $result; + } + + /** + * Reads multiple successive SSH_FXP_WRITE responses + * + * Sending an SSH_FXP_WRITE packet and immediately reading its response isn't as efficient as blindly sending out $i + * SSH_FXP_WRITEs, in succession, and then reading $i responses. + * + * @param int $i + * @return bool + * @access private + */ + function _read_put_responses($i) + { + while ($i--) { + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + break; + } + } + + return $i < 0; + } + + /** + * Close handle + * + * @param string $handle + * @return bool + * @access private + */ + function _close_handle($handle) + { + if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { + return false; + } + + // "The client MUST release all resources associated with the handle regardless of the status." + // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3 + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + return true; + } + + /** + * Downloads a file from the SFTP server. + * + * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if + * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the + * operation. + * + * $offset and $length can be used to download files in chunks. + * + * @param string $remote_file + * @param string $local_file + * @param int $offset + * @param int $length + * @param callable|null $progressCallback + * @return mixed + * @access public + */ + function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null) + { + if (!$this->_precheck()) { + return false; + } + + $remote_file = $this->_realpath($remote_file); + if ($remote_file === false) { + return false; + } + + $packet = pack('Na*', strlen($remote_file), $remote_file); + $packet.= $this->version >= 5 ? + pack('N3', 0, NET_SFTP_OPEN_OPEN_EXISTING, 0) : + pack('N2', NET_SFTP_OPEN_READ, 0); + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + $handle = substr($response, 4); + break; + case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + $this->_logError($response); + return false; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; + } + + if (is_resource($local_file)) { + $fp = $local_file; + $stat = fstat($fp); + $res_offset = $stat['size']; + } else { + $res_offset = 0; + if ($local_file !== false && !is_callable($local_file)) { + $fp = fopen($local_file, 'wb'); + if (!$fp) { + return false; + } + } else { + $content = ''; + } + } + + $fclose_check = $local_file !== false && !is_callable($local_file) && !is_resource($local_file); + + $start = $offset; + $read = 0; + while (true) { + $i = 0; + + while ($i < NET_SFTP_QUEUE_SIZE && ($length < 0 || $read < $length)) { + $tempoffset = $start + $read; + + $packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet; + + $packet = pack('Na*N3', strlen($handle), $handle, $tempoffset / 4294967296, $tempoffset, $packet_size); + if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet, $i)) { + if ($fclose_check) { + fclose($fp); + } + return false; + } + $packet = null; + $read+= $packet_size; + $i++; + } + + if (!$i) { + break; + } + + $packets_sent = $i - 1; + + $clear_responses = false; + while ($i > 0) { + $i--; + + if ($clear_responses) { + $this->_get_sftp_packet($packets_sent - $i); + continue; + } else { + $response = $this->_get_sftp_packet($packets_sent - $i); + } + + switch ($this->packet_type) { + case NET_SFTP_DATA: + $temp = substr($response, 4); + $offset+= strlen($temp); + if ($local_file === false) { + $content.= $temp; + } elseif (is_callable($local_file)) { + $local_file($temp); + } else { + fputs($fp, $temp); + } + if (is_callable($progressCallback)) { + call_user_func($progressCallback, $offset); + } + $temp = null; + break; + case NET_SFTP_STATUS: + // could, in theory, return false if !strlen($content) but we'll hold off for the time being + $this->_logError($response); + $clear_responses = true; // don't break out of the loop yet, so we can read the remaining responses + break; + default: + if ($fclose_check) { + fclose($fp); + } + // maybe the file was successfully transferred, maybe it wasn't + if ($this->channel_close) { + $this->partial_init = false; + $this->_init_sftp_connection(); + return false; + } else { + user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS'); + } + } + $response = null; + } + + if ($clear_responses) { + break; + } + } + + if ($length > 0 && $length <= $offset - $start) { + if ($local_file === false) { + $content = substr($content, 0, $length); + } else { + ftruncate($fp, $length + $res_offset); + } + } + + if ($fclose_check) { + fclose($fp); + + if ($this->preserveTime) { + $stat = $this->stat($remote_file); + touch($local_file, $stat['mtime'], $stat['atime']); + } + } + + if (!$this->_close_handle($handle)) { + return false; + } + + // if $content isn't set that means a file was written to + return isset($content) ? $content : true; + } + + /** + * Deletes a file on the SFTP server. + * + * @param string $path + * @param bool $recursive + * @return bool + * @access public + */ + function delete($path, $recursive = true) + { + if (!$this->_precheck()) { + return false; + } + + if (is_object($path)) { + // It's an object. Cast it as string before we check anything else. + $path = (string) $path; + } + + if (!is_string($path) || $path == '') { + return false; + } + + $path = $this->_realpath($path); + if ($path === false) { + return false; + } + + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 + if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path))) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + if (!$recursive) { + return false; + } + $i = 0; + $result = $this->_delete_recursive($path, $i); + $this->_read_put_responses($i); + return $result; + } + + $this->_remove_from_stat_cache($path); + + return true; + } + + /** + * Recursively deletes directories on the SFTP server + * + * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. + * + * @param string $path + * @param int $i + * @return bool + * @access private + */ + function _delete_recursive($path, &$i) + { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + $entries = $this->_list($path, true); + + // The folder does not exist at all, so we cannot delete it. + if ($entries === NET_SFTP_STATUS_NO_SUCH_FILE) { + return false; + } + + // Normally $entries would have at least . and .. but it might not if the directories + // permissions didn't allow reading. If this happens then default to an empty list of files. + if ($entries === false || is_int($entries)) { + $entries = array(); + } + + unset($entries['.'], $entries['..']); + foreach ($entries as $filename => $props) { + if (!isset($props['type'])) { + return false; + } + + $temp = $path . '/' . $filename; + if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { + if (!$this->_delete_recursive($temp, $i)) { + return false; + } + } else { + if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($temp), $temp))) { + return false; + } + $this->_remove_from_stat_cache($temp); + + $i++; + + if ($i >= NET_SFTP_QUEUE_SIZE) { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + } + } + } + + if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($path), $path))) { + return false; + } + $this->_remove_from_stat_cache($path); + + $i++; + + if ($i >= NET_SFTP_QUEUE_SIZE) { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + } + + return true; + } + + /** + * Checks whether a file or directory exists + * + * @param string $path + * @return bool + * @access public + */ + function file_exists($path) + { + if ($this->use_stat_cache) { + if (!$this->_precheck()) { + return false; + } + + $path = $this->_realpath($path); + + $result = $this->_query_stat_cache($path); + + if (isset($result)) { + // return true if $result is an array or if it's an stdClass object + return $result !== false; + } + } + + return $this->stat($path) !== false; + } + + /** + * Tells whether the filename is a directory + * + * @param string $path + * @return bool + * @access public + */ + function is_dir($path) + { + $result = $this->_get_stat_cache_prop($path, 'type'); + if ($result === false) { + return false; + } + return $result === NET_SFTP_TYPE_DIRECTORY; + } + + /** + * Tells whether the filename is a regular file + * + * @param string $path + * @return bool + * @access public + */ + function is_file($path) + { + $result = $this->_get_stat_cache_prop($path, 'type'); + if ($result === false) { + return false; + } + return $result === NET_SFTP_TYPE_REGULAR; + } + + /** + * Tells whether the filename is a symbolic link + * + * @param string $path + * @return bool + * @access public + */ + function is_link($path) + { + $result = $this->_get_lstat_cache_prop($path, 'type'); + if ($result === false) { + return false; + } + return $result === NET_SFTP_TYPE_SYMLINK; + } + + /** + * Tells whether a file exists and is readable + * + * @param string $path + * @return bool + * @access public + */ + function is_readable($path) + { + if (!$this->_precheck()) { + return false; + } + + $path = $this->_realpath($path); + + $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_READ, 0); + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + return true; + case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + return false; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; + } + } + + /** + * Tells whether the filename is writable + * + * @param string $path + * @return bool + * @access public + */ + function is_writable($path) + { + if (!$this->_precheck()) { + return false; + } + + $path = $this->_realpath($path); + + $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_WRITE, 0); + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + return true; + case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + return false; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; + } + } + + /** + * Tells whether the filename is writeable + * + * Alias of is_writable + * + * @param string $path + * @return bool + * @access public + */ + function is_writeable($path) + { + return $this->is_writable($path); + } + + /** + * Gets last access time of file + * + * @param string $path + * @return mixed + * @access public + */ + function fileatime($path) + { + return $this->_get_stat_cache_prop($path, 'atime'); + } + + /** + * Gets file modification time + * + * @param string $path + * @return mixed + * @access public + */ + function filemtime($path) + { + return $this->_get_stat_cache_prop($path, 'mtime'); + } + + /** + * Gets file permissions + * + * @param string $path + * @return mixed + * @access public + */ + function fileperms($path) + { + return $this->_get_stat_cache_prop($path, 'permissions'); + } + + /** + * Gets file owner + * + * @param string $path + * @return mixed + * @access public + */ + function fileowner($path) + { + return $this->_get_stat_cache_prop($path, 'uid'); + } + + /** + * Gets file group + * + * @param string $path + * @return mixed + * @access public + */ + function filegroup($path) + { + return $this->_get_stat_cache_prop($path, 'gid'); + } + + /** + * Gets file size + * + * @param string $path + * @return mixed + * @access public + */ + function filesize($path) + { + return $this->_get_stat_cache_prop($path, 'size'); + } + + /** + * Gets file type + * + * @param string $path + * @return mixed + * @access public + */ + function filetype($path) + { + $type = $this->_get_stat_cache_prop($path, 'type'); + if ($type === false) { + return false; + } + + switch ($type) { + case NET_SFTP_TYPE_BLOCK_DEVICE: + return 'block'; + case NET_SFTP_TYPE_CHAR_DEVICE: + return 'char'; + case NET_SFTP_TYPE_DIRECTORY: + return 'dir'; + case NET_SFTP_TYPE_FIFO: + return 'fifo'; + case NET_SFTP_TYPE_REGULAR: + return 'file'; + case NET_SFTP_TYPE_SYMLINK: + return 'link'; + default: + return false; + } + } + + /** + * Return a stat properity + * + * Uses cache if appropriate. + * + * @param string $path + * @param string $prop + * @return mixed + * @access private + */ + function _get_stat_cache_prop($path, $prop) + { + return $this->_get_xstat_cache_prop($path, $prop, 'stat'); + } + + /** + * Return an lstat properity + * + * Uses cache if appropriate. + * + * @param string $path + * @param string $prop + * @return mixed + * @access private + */ + function _get_lstat_cache_prop($path, $prop) + { + return $this->_get_xstat_cache_prop($path, $prop, 'lstat'); + } + + /** + * Return a stat or lstat properity + * + * Uses cache if appropriate. + * + * @param string $path + * @param string $prop + * @param mixed $type + * @return mixed + * @access private + */ + function _get_xstat_cache_prop($path, $prop, $type) + { + if (!$this->_precheck()) { + return false; + } + + if ($this->use_stat_cache) { + $path = $this->_realpath($path); + + $result = $this->_query_stat_cache($path); + + if (is_object($result) && isset($result->$type)) { + return $result->{$type}[$prop]; + } + } + + $result = $this->$type($path); + + if ($result === false || !isset($result[$prop])) { + return false; + } + + return $result[$prop]; + } + + /** + * Renames a file or a directory on the SFTP server. + * + * If the file already exists this will return false + * + * @param string $oldname + * @param string $newname + * @return bool + * @access public + */ + function rename($oldname, $newname) + { + if (!$this->_precheck()) { + return false; + } + + $oldname = $this->_realpath($oldname); + $newname = $this->_realpath($newname); + if ($oldname === false || $newname === false) { + return false; + } + + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 + $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname); + if ($this->version >= 5) { + /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-6.5 , + + 'flags' is 0 or a combination of: + + SSH_FXP_RENAME_OVERWRITE 0x00000001 + SSH_FXP_RENAME_ATOMIC 0x00000002 + SSH_FXP_RENAME_NATIVE 0x00000004 + + (none of these are currently supported) */ + $packet.= "\0\0\0\0"; + } + if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + user_error('Expected SSH_FXP_STATUS'); + return false; + } + + // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + // don't move the stat cache entry over since this operation could very well change the + // atime and mtime attributes + //$this->_update_stat_cache($newname, $this->_query_stat_cache($oldname)); + $this->_remove_from_stat_cache($oldname); + $this->_remove_from_stat_cache($newname); + + return true; + } + + /** + * Parse Time + * + * See '7.7. Times' of draft-ietf-secsh-filexfer-13 for more info. + * + * @param string $key + * @param int $flags + * @param string $response + * @return array + * @access private + */ + function _parseTime($key, $flags, &$response) + { + if (strlen($response) < 8) { + user_error('Malformed file attributes'); + return array(); + } + $attr = array(); + $attr[$key] = hexdec(bin2hex($this->_string_shift($response, 8))); + if ($flags & NET_SFTP_ATTR_SUBSECOND_TIMES) { + $attr+= extract(unpack('N' . $key . '_nseconds', $this->_string_shift($response, 4))); + } + return $attr; + } + + /** + * Parse Attributes + * + * See '7. File Attributes' of draft-ietf-secsh-filexfer-13 for more info. + * + * @param string $response + * @return array + * @access private + */ + function _parseAttributes(&$response) + { + if ($this->version >= 4) { + $length = 5; + $format = 'Nflags/Ctype'; + } else { + $length = 4; + $format = 'Nflags'; + } + + $attr = array(); + if (strlen($response) < $length) { + user_error('Malformed file attributes'); + return array(); + } + extract(unpack($format, $this->_string_shift($response, $length))); + if (isset($type)) { + $attr['type'] = $type; + } + foreach ($this->attributes as $key => $value) { + switch ($flags & $key) { + case NET_SFTP_ATTR_UIDGID: + if ($this->version > 3) { + continue 2; + } + break; + case NET_SFTP_ATTR_CREATETIME: + case NET_SFTP_ATTR_MODIFYTIME: + case NET_SFTP_ATTR_ACL: + case NET_SFTP_ATTR_OWNERGROUP: + case NET_SFTP_ATTR_SUBSECOND_TIMES: + if ($this->version < 4) { + continue 2; + } + break; + case NET_SFTP_ATTR_BITS: + if ($this->version < 5) { + continue 2; + } + break; + case NET_SFTP_ATTR_ALLOCATION_SIZE: + case NET_SFTP_ATTR_TEXT_HINT: + case NET_SFTP_ATTR_MIME_TYPE: + case NET_SFTP_ATTR_LINK_COUNT: + case NET_SFTP_ATTR_UNTRANSLATED_NAME: + case NET_SFTP_ATTR_CTIME: + if ($this->version < 6) { + continue 2; + } + } + switch ($flags & $key) { + case NET_SFTP_ATTR_SIZE: // 0x00000001 + // The size attribute is defined as an unsigned 64-bit integer. + // The following will use floats on 32-bit platforms, if necessary. + // As can be seen in the BigInteger class, floats are generally + // IEEE 754 binary64 "double precision" on such platforms and + // as such can represent integers of at least 2^50 without loss + // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB. + $attr['size'] = hexdec(bin2hex($this->_string_shift($response, 8))); + break; + case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 or earlier) + if (strlen($response) < 8) { + user_error('Malformed file attributes'); + return $attr; + } + $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8)); + break; + case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004 + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + $attr+= unpack('Npermissions', $this->_string_shift($response, 4)); + // mode == permissions; permissions was the original array key and is retained for bc purposes. + // mode was added because that's the more industry standard terminology + $attr+= array('mode' => $attr['permissions']); + $fileType = $this->_parseMode($attr['permissions']); + if ($fileType !== false) { + $attr+= array('type' => $fileType); + } + break; + case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008 + if ($this->version >= 4) { + $attr+= $this->_parseTime('atime', $flags, $response); + break; + } + if (strlen($response) < 8) { + user_error('Malformed file attributes'); + return $attr; + } + $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8)); + break; + case NET_SFTP_ATTR_CREATETIME: // 0x00000010 (SFTPv4+) + $attr+= $this->_parseTime('createtime', $flags, $response); + break; + case NET_SFTP_ATTR_MODIFYTIME: // 0x00000020 + $attr+= $this->_parseTime('mtime', $flags, $response); + break; + case NET_SFTP_ATTR_ACL: // 0x00000040 + // access control list + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-04#section-5.7 + // currently unsupported + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Ncount', $this->_string_shift($response, 4))); + for ($i = 0; $i < $count; $i++) { + if (strlen($response) < 16) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Ntype/Nflag/Nmask/Nlength', $this->_string_shift($response, 16))); + if (strlen($response) < $length) { + user_error('Malformed file attributes'); + return $attr; + } + $this->_string_shift($response, $length); // who + } + break; + case NET_SFTP_ATTR_OWNERGROUP: // 0x00000080 + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + if (strlen($response) < $length) { + user_error('Malformed file attributes'); + return $attr; + } + $attr['owner'] = $this->_string_shift($response, $length); + + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + if (strlen($response) < $length) { + user_error('Malformed file attributes'); + return $attr; + } + $attr['group'] = $this->_string_shift($response, $length); + break; + case NET_SFTP_ATTR_SUBSECOND_TIMES: // 0x00000100 + break; + case NET_SFTP_ATTR_BITS: // 0x00000200 (SFTPv5+) + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-5.8 + // currently unsupported + // tells if you file is: + // readonly, system, hidden, case inensitive, archive, encrypted, compressed, sparse + // append only, immutable, sync + if (strlen($response) < 8) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Nattrib-bits/Nattrib-bits-valid', $this->_string_shift($response, 8))); + break; + case NET_SFTP_ATTR_ALLOCATION_SIZE: // 0x00000400 (SFTPv6+) + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.4 + // represents the number of bytes htat the file consumes on the disk. will + // usually be larger than the 'size' field + $attr['allocation-size'] = hexdec(bin2hex($this->_string_shift($response, 8))); + break; + case NET_SFTP_ATTR_TEXT_HINT: // 0x00000800 + // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.10 + // currently unsupported + // tells if file is "known text", "guessed text", "known binary", "guessed binary" + extract(unpack('Ctext-hint', $this->_string_shift($response))); + break; + case NET_SFTP_ATTR_MIME_TYPE: // 0x00001000 + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.11 + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + if (strlen($response) < $length) { + user_error('Malformed file attributes'); + return $attr; + } + $attr['mime-type'] = $this->_string_shift($response, $length); + break; + case NET_SFTP_ATTR_LINK_COUNT: // 0x00002000 + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.12 + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + $attr+= unpack('Nlink-count', $this->_string_shift($response, 4)); + break; + case NET_SFTP_ATTR_UNTRANSLATED_NAME:// 0x00004000 + // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.13 + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + if (strlen($response) < $length) { + user_error('Malformed file attributes'); + return $attr; + } + $attr['untranslated-name'] = $this->_string_shift($response, $length); + break; + case NET_SFTP_ATTR_CTIME: // 0x00008000 + // 'ctime' contains the last time the file attributes were changed. The + // exact meaning of this field depends on the server. + $attr+= $this->_parseTime('ctime', $flags, $response); + break; + case NET_SFTP_ATTR_EXTENDED: // 0x80000000 + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Ncount', $this->_string_shift($response, 4))); + for ($i = 0; $i < $count; $i++) { + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $key = $this->_string_shift($response, $length); + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $attr[$key] = $this->_string_shift($response, $length); + } + } + } + return $attr; + } + + /** + * Attempt to identify the file type + * + * Quoting the SFTP RFC, "Implementations MUST NOT send bits that are not defined" but they seem to anyway + * + * @param int $mode + * @return int + * @access private + */ + function _parseMode($mode) + { + // values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12 + // see, also, http://linux.die.net/man/2/stat + switch ($mode & 0170000) {// ie. 1111 0000 0000 0000 + case 0000000: // no file type specified - figure out the file type using alternative means + return false; + case 0040000: + return NET_SFTP_TYPE_DIRECTORY; + case 0100000: + return NET_SFTP_TYPE_REGULAR; + case 0120000: + return NET_SFTP_TYPE_SYMLINK; + // new types introduced in SFTPv5+ + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 + case 0010000: // named pipe (fifo) + return NET_SFTP_TYPE_FIFO; + case 0020000: // character special + return NET_SFTP_TYPE_CHAR_DEVICE; + case 0060000: // block special + return NET_SFTP_TYPE_BLOCK_DEVICE; + case 0140000: // socket + return NET_SFTP_TYPE_SOCKET; + case 0160000: // whiteout + // "SPECIAL should be used for files that are of + // a known type which cannot be expressed in the protocol" + return NET_SFTP_TYPE_SPECIAL; + default: + return NET_SFTP_TYPE_UNKNOWN; + } + } + + /** + * Parse Longname + * + * SFTPv3 doesn't provide any easy way of identifying a file type. You could try to open + * a file as a directory and see if an error is returned or you could try to parse the + * SFTPv3-specific longname field of the SSH_FXP_NAME packet. That's what this function does. + * The result is returned using the + * {@link http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4 type constants}. + * + * If the longname is in an unrecognized format bool(false) is returned. + * + * @param string $longname + * @return mixed + * @access private + */ + function _parseLongname($longname) + { + // http://en.wikipedia.org/wiki/Unix_file_types + // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions + if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) { + switch ($longname[0]) { + case '-': + return NET_SFTP_TYPE_REGULAR; + case 'd': + return NET_SFTP_TYPE_DIRECTORY; + case 'l': + return NET_SFTP_TYPE_SYMLINK; + default: + return NET_SFTP_TYPE_SPECIAL; + } + } + + return false; + } + + /** + * Sends SFTP Packets + * + * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info. + * + * @param int $type + * @param string $data + * @param int $request_id + * @see self::_get_sftp_packet() + * @see self::_send_channel_packet() + * @return bool + * @access private + */ + function _send_sftp_packet($type, $data, $request_id = 1) + { + // in SSH2.php the timeout is cumulative per function call. eg. exec() will + // timeout after 10s. but for SFTP.php it's cumulative per packet + $this->curTimeout = $this->timeout; + + $packet = $this->use_request_id ? + pack('NCNa*', strlen($data) + 5, $type, $request_id, $data) : + pack('NCa*', strlen($data) + 1, $type, $data); + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $result = $this->_send_channel_packet(self::CHANNEL, $packet); + $stop = strtok(microtime(), ' ') + strtok(''); + + if (defined('NET_SFTP_LOGGING')) { + $packet_type = '-> ' . $this->packet_types[$type] . + ' (' . round($stop - $start, 4) . 's)'; + if (NET_SFTP_LOGGING == self::LOG_REALTIME) { + switch (PHP_SAPI) { + case 'cli': + $start = $stop = "\r\n"; + break; + default: + $start = '
      ';
      +                        $stop = '
      '; + } + echo $start . $this->_format_log(array($data), array($packet_type)) . $stop; + @flush(); + @ob_flush(); + } else { + $this->packet_type_log[] = $packet_type; + if (NET_SFTP_LOGGING == self::LOG_COMPLEX) { + $this->packet_log[] = $data; + } + } + } + + return $result; + } + + /** + * Resets a connection for re-use + * + * @param int $reason + * @access private + */ + function _reset_connection($reason) + { + parent::_reset_connection($reason); + $this->use_request_id = false; + $this->pwd = false; + $this->requestBuffer = array(); + } + + /** + * Receives SFTP Packets + * + * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info. + * + * Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no bearing on the number of SFTP packets present. + * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA + * messages containing one SFTP packet. + * + * @see self::_send_sftp_packet() + * @return string + * @access private + */ + function _get_sftp_packet($request_id = null) + { + $this->channel_close = false; + + if (isset($request_id) && isset($this->requestBuffer[$request_id])) { + $this->packet_type = $this->requestBuffer[$request_id]['packet_type']; + $temp = $this->requestBuffer[$request_id]['packet']; + unset($this->requestBuffer[$request_id]); + return $temp; + } + + // in SSH2.php the timeout is cumulative per function call. eg. exec() will + // timeout after 10s. but for SFTP.php it's cumulative per packet + $this->curTimeout = $this->timeout; + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + + // SFTP packet length + while (strlen($this->packet_buffer) < 4) { + $temp = $this->_get_channel_packet(self::CHANNEL, true); + if ($temp === true) { + if ($this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) { + $this->channel_close = true; + } + $this->packet_type = false; + $this->packet_buffer = ''; + return false; + } + if ($temp === false) { + return false; + } + $this->packet_buffer.= $temp; + } + if (strlen($this->packet_buffer) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4))); + $tempLength = $length; + $tempLength-= strlen($this->packet_buffer); + + // 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in OpenSSH's sftp-common.h + if (!$this->allow_arbitrary_length_packets && !$this->use_request_id && $tempLength > 256 * 1024) { + user_error('Invalid SFTP packet size'); + return false; + } + + // SFTP packet type and data payload + while ($tempLength > 0) { + $temp = $this->_get_channel_packet(self::CHANNEL, true); + if (is_bool($temp)) { + if ($temp && $this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) { + $this->channel_close = true; + } + $this->packet_type = false; + $this->packet_buffer = ''; + return false; + } + $this->packet_buffer.= $temp; + $tempLength-= strlen($temp); + } + + $stop = strtok(microtime(), ' ') + strtok(''); + + $this->packet_type = ord($this->_string_shift($this->packet_buffer)); + + if ($this->use_request_id) { + extract(unpack('Npacket_id', $this->_string_shift($this->packet_buffer, 4))); // remove the request id + $length-= 5; // account for the request id and the packet type + } else { + $length-= 1; // account for the packet type + } + + $packet = $this->_string_shift($this->packet_buffer, $length); + + if (defined('NET_SFTP_LOGGING')) { + $packet_type = '<- ' . $this->packet_types[$this->packet_type] . + ' (' . round($stop - $start, 4) . 's)'; + if (NET_SFTP_LOGGING == self::LOG_REALTIME) { + switch (PHP_SAPI) { + case 'cli': + $start = $stop = "\r\n"; + break; + default: + $start = '
      ';
      +                        $stop = '
      '; + } + echo $start . $this->_format_log(array($packet), array($packet_type)) . $stop; + @flush(); + @ob_flush(); + } else { + $this->packet_type_log[] = $packet_type; + if (NET_SFTP_LOGGING == self::LOG_COMPLEX) { + $this->packet_log[] = $packet; + } + } + } + + if (isset($request_id) && $this->use_request_id && $packet_id != $request_id) { + $this->requestBuffer[$packet_id] = array( + 'packet_type' => $this->packet_type, + 'packet' => $packet + ); + return $this->_get_sftp_packet($request_id); + } + + return $packet; + } + + /** + * Returns a log of the packets that have been sent and received. + * + * Returns a string if NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX, an array if NET_SFTP_LOGGING == NET_SFTP_LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING') + * + * @access public + * @return string or Array + */ + function getSFTPLog() + { + if (!defined('NET_SFTP_LOGGING')) { + return false; + } + + switch (NET_SFTP_LOGGING) { + case self::LOG_COMPLEX: + return $this->_format_log($this->packet_log, $this->packet_type_log); + break; + //case self::LOG_SIMPLE: + default: + return $this->packet_type_log; + } + } + + /** + * Returns all errors + * + * @return array + * @access public + */ + function getSFTPErrors() + { + return $this->sftp_errors; + } + + /** + * Returns the last error + * + * @return string + * @access public + */ + function getLastSFTPError() + { + return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : ''; + } + + /** + * Get supported SFTP versions + * + * @return array + * @access public + */ + function getSupportedVersions() + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + if (!$this->partial_init) { + $this->_partial_init_sftp_connection(); + } + + $temp = array('version' => $this->defaultVersion); + if (isset($this->extensions['versions'])) { + $temp['extensions'] = $this->extensions['versions']; + } + return $temp; + } + + /** + * Get supported SFTP versions + * + * @return array + * @access public + */ + function getNegotiatedVersion() + { + if (!$this->_precheck()) { + return false; + } + + return $this->version; + } + + /** + * Set preferred version + * + * If you're preferred version isn't supported then the highest supported + * version of SFTP will be utilized. Set to null or false or int(0) to + * unset the preferred version + * + * @param int $version + * @access public + */ + function setPreferredVersion($version) + { + $this->preferredVersion = $version; + } + + /** + * Disconnect + * + * @param int $reason + * @return bool + * @access private + */ + function _disconnect($reason) + { + $this->pwd = false; + parent::_disconnect($reason); + } + + /** + * Enable Date Preservation + * + * @access public + */ + function enableDatePreservation() + { + $this->preserveTime = true; + } + + /** + * Disable Date Preservation + * + * @access public + */ + function disableDatePreservation() + { + $this->preserveTime = false; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php new file mode 100644 index 0000000..7482b8f --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php @@ -0,0 +1,796 @@ + + * @copyright 2013 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Net\SFTP; + +use phpseclib\Crypt\RSA; +use phpseclib\Net\SFTP; + +/** + * SFTP Stream Wrapper + * + * @package SFTP + * @author Jim Wigginton + * @access public + */ +class Stream +{ + /** + * SFTP instances + * + * Rather than re-create the connection we re-use instances if possible + * + * @var array + */ + static $instances; + + /** + * SFTP instance + * + * @var object + * @access private + */ + var $sftp; + + /** + * Path + * + * @var string + * @access private + */ + var $path; + + /** + * Mode + * + * @var string + * @access private + */ + var $mode; + + /** + * Position + * + * @var int + * @access private + */ + var $pos; + + /** + * Size + * + * @var int + * @access private + */ + var $size; + + /** + * Directory entries + * + * @var array + * @access private + */ + var $entries; + + /** + * EOF flag + * + * @var bool + * @access private + */ + var $eof; + + /** + * Context resource + * + * Technically this needs to be publically accessible so PHP can set it directly + * + * @var resource + * @access public + */ + var $context; + + /** + * Notification callback function + * + * @var callable + * @access public + */ + var $notification; + + /** + * Registers this class as a URL wrapper. + * + * @param string $protocol The wrapper name to be registered. + * @return bool True on success, false otherwise. + * @access public + */ + static function register($protocol = 'sftp') + { + if (in_array($protocol, stream_get_wrappers(), true)) { + return false; + } + return stream_wrapper_register($protocol, get_called_class()); + } + + /** + * The Constructor + * + * @access public + */ + function __construct() + { + if (defined('NET_SFTP_STREAM_LOGGING')) { + echo "__construct()\r\n"; + } + } + + /** + * Path Parser + * + * Extract a path from a URI and actually connect to an SSH server if appropriate + * + * If "notification" is set as a context parameter the message code for successful login is + * NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's NET_SSH2_MSG_USERAUTH_FAILURE. + * + * @param string $path + * @return string + * @access private + */ + function _parse_path($path) + { + $orig = $path; + extract(parse_url($path) + array('port' => 22)); + if (isset($query)) { + $path.= '?' . $query; + } elseif (preg_match('/(\?|\?#)$/', $orig)) { + $path.= '?'; + } + if (isset($fragment)) { + $path.= '#' . $fragment; + } elseif ($orig[strlen($orig) - 1] == '#') { + $path.= '#'; + } + + if (!isset($host)) { + return false; + } + + if (isset($this->context)) { + $context = stream_context_get_params($this->context); + if (isset($context['notification'])) { + $this->notification = $context['notification']; + } + } + + if ($host[0] == '$') { + $host = substr($host, 1); + global ${$host}; + if (($$host instanceof SFTP) === false) { + return false; + } + $this->sftp = $$host; + } else { + if (isset($this->context)) { + $context = stream_context_get_options($this->context); + } + if (isset($context[$scheme]['session'])) { + $sftp = $context[$scheme]['session']; + } + if (isset($context[$scheme]['sftp'])) { + $sftp = $context[$scheme]['sftp']; + } + if (isset($sftp) && $sftp instanceof SFTP) { + $this->sftp = $sftp; + return $path; + } + if (isset($context[$scheme]['username'])) { + $user = $context[$scheme]['username']; + } + if (isset($context[$scheme]['password'])) { + $pass = $context[$scheme]['password']; + } + if (isset($context[$scheme]['privkey']) && $context[$scheme]['privkey'] instanceof RSA) { + $pass = $context[$scheme]['privkey']; + } + + if (!isset($user) || !isset($pass)) { + return false; + } + + // casting $pass to a string is necessary in the event that it's a \phpseclib\Crypt\RSA object + if (isset(self::$instances[$host][$port][$user][(string) $pass])) { + $this->sftp = self::$instances[$host][$port][$user][(string) $pass]; + } else { + $this->sftp = new SFTP($host, $port); + $this->sftp->disableStatCache(); + if (isset($this->notification) && is_callable($this->notification)) { + /* if !is_callable($this->notification) we could do this: + + user_error('fopen(): failed to call user notifier', E_USER_WARNING); + + the ftp wrapper gives errors like that when the notifier isn't callable. + i've opted not to do that, however, since the ftp wrapper gives the line + on which the fopen occurred as the line number - not the line that the + user_error is on. + */ + call_user_func($this->notification, STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); + call_user_func($this->notification, STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); + if (!$this->sftp->login($user, $pass)) { + call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0); + return false; + } + call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0); + } else { + if (!$this->sftp->login($user, $pass)) { + return false; + } + } + self::$instances[$host][$port][$user][(string) $pass] = $this->sftp; + } + } + + return $path; + } + + /** + * Opens file or URL + * + * @param string $path + * @param string $mode + * @param int $options + * @param string $opened_path + * @return bool + * @access public + */ + function _stream_open($path, $mode, $options, &$opened_path) + { + $path = $this->_parse_path($path); + + if ($path === false) { + return false; + } + $this->path = $path; + + $this->size = $this->sftp->size($path); + $this->mode = preg_replace('#[bt]$#', '', $mode); + $this->eof = false; + + if ($this->size === false) { + if ($this->mode[0] == 'r') { + return false; + } else { + $this->sftp->touch($path); + $this->size = 0; + } + } else { + switch ($this->mode[0]) { + case 'x': + return false; + case 'w': + $this->sftp->truncate($path, 0); + $this->size = 0; + } + } + + $this->pos = $this->mode[0] != 'a' ? 0 : $this->size; + + return true; + } + + /** + * Read from stream + * + * @param int $count + * @return mixed + * @access public + */ + function _stream_read($count) + { + switch ($this->mode) { + case 'w': + case 'a': + case 'x': + case 'c': + return false; + } + + // commented out because some files - eg. /dev/urandom - will say their size is 0 when in fact it's kinda infinite + //if ($this->pos >= $this->size) { + // $this->eof = true; + // return false; + //} + + $result = $this->sftp->get($this->path, false, $this->pos, $count); + if (isset($this->notification) && is_callable($this->notification)) { + if ($result === false) { + call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); + return 0; + } + // seems that PHP calls stream_read in 8k chunks + call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result), $this->size); + } + + if (empty($result)) { // ie. false or empty string + $this->eof = true; + return false; + } + $this->pos+= strlen($result); + + return $result; + } + + /** + * Write to stream + * + * @param string $data + * @return mixed + * @access public + */ + function _stream_write($data) + { + switch ($this->mode) { + case 'r': + return false; + } + + $result = $this->sftp->put($this->path, $data, SFTP::SOURCE_STRING, $this->pos); + if (isset($this->notification) && is_callable($this->notification)) { + if (!$result) { + call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); + return 0; + } + // seems that PHP splits up strings into 8k blocks before calling stream_write + call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data), strlen($data)); + } + + if ($result === false) { + return false; + } + $this->pos+= strlen($data); + if ($this->pos > $this->size) { + $this->size = $this->pos; + } + $this->eof = false; + return strlen($data); + } + + /** + * Retrieve the current position of a stream + * + * @return int + * @access public + */ + function _stream_tell() + { + return $this->pos; + } + + /** + * Tests for end-of-file on a file pointer + * + * In my testing there are four classes functions that normally effect the pointer: + * fseek, fputs / fwrite, fgets / fread and ftruncate. + * + * Only fgets / fread, however, results in feof() returning true. do fputs($fp, 'aaa') on a blank file and feof() + * will return false. do fread($fp, 1) and feof() will then return true. do fseek($fp, 10) on ablank file and feof() + * will return false. do fread($fp, 1) and feof() will then return true. + * + * @return bool + * @access public + */ + function _stream_eof() + { + return $this->eof; + } + + /** + * Seeks to specific location in a stream + * + * @param int $offset + * @param int $whence + * @return bool + * @access public + */ + function _stream_seek($offset, $whence) + { + switch ($whence) { + case SEEK_SET: + if ($offset < 0) { + return false; + } + break; + case SEEK_CUR: + $offset+= $this->pos; + break; + case SEEK_END: + $offset+= $this->size; + } + + $this->pos = $offset; + $this->eof = false; + return true; + } + + /** + * Change stream options + * + * @param string $path + * @param int $option + * @param mixed $var + * @return bool + * @access public + */ + function _stream_metadata($path, $option, $var) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + // stream_metadata was introduced in PHP 5.4.0 but as of 5.4.11 the constants haven't been defined + // see http://www.php.net/streamwrapper.stream-metadata and https://bugs.php.net/64246 + // and https://github.com/php/php-src/blob/master/main/php_streams.h#L592 + switch ($option) { + case 1: // PHP_STREAM_META_TOUCH + $time = isset($var[0]) ? $var[0] : null; + $atime = isset($var[1]) ? $var[1] : null; + return $this->sftp->touch($path, $time, $atime); + case 2: // PHP_STREAM_OWNER_NAME + case 3: // PHP_STREAM_GROUP_NAME + return false; + case 4: // PHP_STREAM_META_OWNER + return $this->sftp->chown($path, $var); + case 5: // PHP_STREAM_META_GROUP + return $this->sftp->chgrp($path, $var); + case 6: // PHP_STREAM_META_ACCESS + return $this->sftp->chmod($path, $var) !== false; + } + } + + /** + * Retrieve the underlaying resource + * + * @param int $cast_as + * @return resource + * @access public + */ + function _stream_cast($cast_as) + { + return $this->sftp->fsock; + } + + /** + * Advisory file locking + * + * @param int $operation + * @return bool + * @access public + */ + function _stream_lock($operation) + { + return false; + } + + /** + * Renames a file or directory + * + * Attempts to rename oldname to newname, moving it between directories if necessary. + * If newname exists, it will be overwritten. This is a departure from what \phpseclib\Net\SFTP + * does. + * + * @param string $path_from + * @param string $path_to + * @return bool + * @access public + */ + function _rename($path_from, $path_to) + { + $path1 = parse_url($path_from); + $path2 = parse_url($path_to); + unset($path1['path'], $path2['path']); + if ($path1 != $path2) { + return false; + } + + $path_from = $this->_parse_path($path_from); + $path_to = parse_url($path_to); + if ($path_from === false) { + return false; + } + + $path_to = $path_to['path']; // the $component part of parse_url() was added in PHP 5.1.2 + // "It is an error if there already exists a file with the name specified by newpath." + // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.5 + if (!$this->sftp->rename($path_from, $path_to)) { + if ($this->sftp->stat($path_to)) { + return $this->sftp->delete($path_to, true) && $this->sftp->rename($path_from, $path_to); + } + return false; + } + + return true; + } + + /** + * Open directory handle + * + * The only $options is "whether or not to enforce safe_mode (0x04)". Since safe mode was deprecated in 5.3 and + * removed in 5.4 I'm just going to ignore it. + * + * Also, nlist() is the best that this function is realistically going to be able to do. When an SFTP client + * sends a SSH_FXP_READDIR packet you don't generally get info on just one file but on multiple files. Quoting + * the SFTP specs: + * + * The SSH_FXP_NAME response has the following format: + * + * uint32 id + * uint32 count + * repeats count times: + * string filename + * string longname + * ATTRS attrs + * + * @param string $path + * @param int $options + * @return bool + * @access public + */ + function _dir_opendir($path, $options) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + $this->pos = 0; + $this->entries = $this->sftp->nlist($path); + return $this->entries !== false; + } + + /** + * Read entry from directory handle + * + * @return mixed + * @access public + */ + function _dir_readdir() + { + if (isset($this->entries[$this->pos])) { + return $this->entries[$this->pos++]; + } + return false; + } + + /** + * Rewind directory handle + * + * @return bool + * @access public + */ + function _dir_rewinddir() + { + $this->pos = 0; + return true; + } + + /** + * Close directory handle + * + * @return bool + * @access public + */ + function _dir_closedir() + { + return true; + } + + /** + * Create a directory + * + * Only valid $options is STREAM_MKDIR_RECURSIVE + * + * @param string $path + * @param int $mode + * @param int $options + * @return bool + * @access public + */ + function _mkdir($path, $mode, $options) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + return $this->sftp->mkdir($path, $mode, $options & STREAM_MKDIR_RECURSIVE); + } + + /** + * Removes a directory + * + * Only valid $options is STREAM_MKDIR_RECURSIVE per , however, + * does not have a $recursive parameter as mkdir() does so I don't know how + * STREAM_MKDIR_RECURSIVE is supposed to be set. Also, when I try it out with rmdir() I get 8 as + * $options. What does 8 correspond to? + * + * @param string $path + * @param int $options + * @return bool + * @access public + */ + function _rmdir($path, $options) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + return $this->sftp->rmdir($path); + } + + /** + * Flushes the output + * + * See . Always returns true because \phpseclib\Net\SFTP doesn't cache stuff before writing + * + * @return bool + * @access public + */ + function _stream_flush() + { + return true; + } + + /** + * Retrieve information about a file resource + * + * @return mixed + * @access public + */ + function _stream_stat() + { + $results = $this->sftp->stat($this->path); + if ($results === false) { + return false; + } + return $results; + } + + /** + * Delete a file + * + * @param string $path + * @return bool + * @access public + */ + function _unlink($path) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + return $this->sftp->delete($path, false); + } + + /** + * Retrieve information about a file + * + * Ignores the STREAM_URL_STAT_QUIET flag because the entirety of \phpseclib\Net\SFTP\Stream is quiet by default + * might be worthwhile to reconstruct bits 12-16 (ie. the file type) if mode doesn't have them but we'll + * cross that bridge when and if it's reached + * + * @param string $path + * @param int $flags + * @return mixed + * @access public + */ + function _url_stat($path, $flags) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + $results = $flags & STREAM_URL_STAT_LINK ? $this->sftp->lstat($path) : $this->sftp->stat($path); + if ($results === false) { + return false; + } + + return $results; + } + + /** + * Truncate stream + * + * @param int $new_size + * @return bool + * @access public + */ + function _stream_truncate($new_size) + { + if (!$this->sftp->truncate($this->path, $new_size)) { + return false; + } + + $this->eof = false; + $this->size = $new_size; + + return true; + } + + /** + * Change stream options + * + * STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason stream_flush isn't. + * The other two aren't supported because of limitations in \phpseclib\Net\SFTP. + * + * @param int $option + * @param int $arg1 + * @param int $arg2 + * @return bool + * @access public + */ + function _stream_set_option($option, $arg1, $arg2) + { + return false; + } + + /** + * Close an resource + * + * @access public + */ + function _stream_close() + { + } + + /** + * __call Magic Method + * + * When you're utilizing an SFTP stream you're not calling the methods in this class directly - PHP is calling them for you. + * Which kinda begs the question... what methods is PHP calling and what parameters is it passing to them? This function + * lets you figure that out. + * + * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not + * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method. + * + * @param string $name + * @param array $arguments + * @return mixed + * @access public + */ + function __call($name, $arguments) + { + if (defined('NET_SFTP_STREAM_LOGGING')) { + echo $name . '('; + $last = count($arguments) - 1; + foreach ($arguments as $i => $argument) { + var_export($argument); + if ($i != $last) { + echo ','; + } + } + echo ")\r\n"; + } + $name = '_' . $name; + if (!method_exists($this, $name)) { + return false; + } + return call_user_func_array(array($this, $name), $arguments); + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php new file mode 100644 index 0000000..5a943c1 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php @@ -0,0 +1,1662 @@ + + * login('username', 'password')) { + * exit('Login Failed'); + * } + * + * echo $ssh->exec('ls -la'); + * ?> + * + * + * Here's another short example: + * + * login('username', 'password')) { + * exit('Login Failed'); + * } + * + * echo $ssh->read('username@username:~$'); + * $ssh->write("ls -la\n"); + * echo $ssh->read('username@username:~$'); + * ?> + * + * + * More information on the SSHv1 specification can be found by reading + * {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}. + * + * @category Net + * @package SSH1 + * @author Jim Wigginton + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Net; + +use phpseclib\Crypt\DES; +use phpseclib\Crypt\Random; +use phpseclib\Crypt\TripleDES; +use phpseclib\Math\BigInteger; + +/** + * Pure-PHP implementation of SSHv1. + * + * @package SSH1 + * @author Jim Wigginton + * @access public + */ +class SSH1 +{ + /**#@+ + * Encryption Methods + * + * @see \phpseclib\Net\SSH1::getSupportedCiphers() + * @access public + */ + /** + * No encryption + * + * Not supported. + */ + const CIPHER_NONE = 0; + /** + * IDEA in CFB mode + * + * Not supported. + */ + const CIPHER_IDEA = 1; + /** + * DES in CBC mode + */ + const CIPHER_DES = 2; + /** + * Triple-DES in CBC mode + * + * All implementations are required to support this + */ + const CIPHER_3DES = 3; + /** + * TRI's Simple Stream encryption CBC + * + * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, does define it (see cipher.h), + * although it doesn't use it (see cipher.c) + */ + const CIPHER_BROKEN_TSS = 4; + /** + * RC4 + * + * Not supported. + * + * @internal According to the SSH1 specs: + * + * "The first 16 bytes of the session key are used as the key for + * the server to client direction. The remaining 16 bytes are used + * as the key for the client to server direction. This gives + * independent 128-bit keys for each direction." + * + * This library currently only supports encryption when the same key is being used for both directions. This is + * because there's only one $crypto object. Two could be added ($encrypt and $decrypt, perhaps). + */ + const CIPHER_RC4 = 5; + /** + * Blowfish + * + * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, defines it (see cipher.h) and + * uses it (see cipher.c) + */ + const CIPHER_BLOWFISH = 6; + /**#@-*/ + + /**#@+ + * Authentication Methods + * + * @see \phpseclib\Net\SSH1::getSupportedAuthentications() + * @access public + */ + /** + * .rhosts or /etc/hosts.equiv + */ + const AUTH_RHOSTS = 1; + /** + * pure RSA authentication + */ + const AUTH_RSA = 2; + /** + * password authentication + * + * This is the only method that is supported by this library. + */ + const AUTH_PASSWORD = 3; + /** + * .rhosts with RSA host authentication + */ + const AUTH_RHOSTS_RSA = 4; + /**#@-*/ + + /**#@+ + * Terminal Modes + * + * @link http://3sp.com/content/developer/maverick-net/docs/Maverick.SSH.PseudoTerminalModesMembers.html + * @access private + */ + const TTY_OP_END = 0; + /**#@-*/ + + /** + * The Response Type + * + * @see \phpseclib\Net\SSH1::_get_binary_packet() + * @access private + */ + const RESPONSE_TYPE = 1; + + /** + * The Response Data + * + * @see \phpseclib\Net\SSH1::_get_binary_packet() + * @access private + */ + const RESPONSE_DATA = 2; + + /**#@+ + * Execution Bitmap Masks + * + * @see \phpseclib\Net\SSH1::bitmap + * @access private + */ + const MASK_CONSTRUCTOR = 0x00000001; + const MASK_CONNECTED = 0x00000002; + const MASK_LOGIN = 0x00000004; + const MASK_SHELL = 0x00000008; + /**#@-*/ + + /**#@+ + * @access public + * @see \phpseclib\Net\SSH1::getLog() + */ + /** + * Returns the message numbers + */ + const LOG_SIMPLE = 1; + /** + * Returns the message content + */ + const LOG_COMPLEX = 2; + /** + * Outputs the content real-time + */ + const LOG_REALTIME = 3; + /** + * Dumps the content real-time to a file + */ + const LOG_REALTIME_FILE = 4; + /** + * Make sure that the log never gets larger than this + */ + const LOG_MAX_SIZE = 1048576; // 1024 * 1024 + /**#@-*/ + + /**#@+ + * @access public + * @see \phpseclib\Net\SSH1::read() + */ + /** + * Returns when a string matching $expect exactly is found + */ + const READ_SIMPLE = 1; + /** + * Returns when a string matching the regular expression $expect is found + */ + const READ_REGEX = 2; + /**#@-*/ + + /** + * The SSH identifier + * + * @var string + * @access private + */ + var $identifier = 'SSH-1.5-phpseclib'; + + /** + * The Socket Object + * + * @var object + * @access private + */ + var $fsock; + + /** + * The cryptography object + * + * @var object + * @access private + */ + var $crypto = false; + + /** + * Execution Bitmap + * + * The bits that are set represent functions that have been called already. This is used to determine + * if a requisite function has been successfully executed. If not, an error should be thrown. + * + * @var int + * @access private + */ + var $bitmap = 0; + + /** + * The Server Key Public Exponent + * + * Logged for debug purposes + * + * @see self::getServerKeyPublicExponent() + * @var string + * @access private + */ + var $server_key_public_exponent; + + /** + * The Server Key Public Modulus + * + * Logged for debug purposes + * + * @see self::getServerKeyPublicModulus() + * @var string + * @access private + */ + var $server_key_public_modulus; + + /** + * The Host Key Public Exponent + * + * Logged for debug purposes + * + * @see self::getHostKeyPublicExponent() + * @var string + * @access private + */ + var $host_key_public_exponent; + + /** + * The Host Key Public Modulus + * + * Logged for debug purposes + * + * @see self::getHostKeyPublicModulus() + * @var string + * @access private + */ + var $host_key_public_modulus; + + /** + * Supported Ciphers + * + * Logged for debug purposes + * + * @see self::getSupportedCiphers() + * @var array + * @access private + */ + var $supported_ciphers = array( + self::CIPHER_NONE => 'No encryption', + self::CIPHER_IDEA => 'IDEA in CFB mode', + self::CIPHER_DES => 'DES in CBC mode', + self::CIPHER_3DES => 'Triple-DES in CBC mode', + self::CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream encryption CBC', + self::CIPHER_RC4 => 'RC4', + self::CIPHER_BLOWFISH => 'Blowfish' + ); + + /** + * Supported Authentications + * + * Logged for debug purposes + * + * @see self::getSupportedAuthentications() + * @var array + * @access private + */ + var $supported_authentications = array( + self::AUTH_RHOSTS => '.rhosts or /etc/hosts.equiv', + self::AUTH_RSA => 'pure RSA authentication', + self::AUTH_PASSWORD => 'password authentication', + self::AUTH_RHOSTS_RSA => '.rhosts with RSA host authentication' + ); + + /** + * Server Identification + * + * @see self::getServerIdentification() + * @var string + * @access private + */ + var $server_identification = ''; + + /** + * Protocol Flags + * + * @see self::__construct() + * @var array + * @access private + */ + var $protocol_flags = array(); + + /** + * Protocol Flag Log + * + * @see self::getLog() + * @var array + * @access private + */ + var $protocol_flags_log = array(); + + /** + * Message Log + * + * @see self::getLog() + * @var array + * @access private + */ + var $message_log = array(); + + /** + * Real-time log file pointer + * + * @see self::_append_log() + * @var resource + * @access private + */ + var $realtime_log_file; + + /** + * Real-time log file size + * + * @see self::_append_log() + * @var int + * @access private + */ + var $realtime_log_size; + + /** + * Real-time log file wrap boolean + * + * @see self::_append_log() + * @var bool + * @access private + */ + var $realtime_log_wrap; + + /** + * Interactive Buffer + * + * @see self::read() + * @var array + * @access private + */ + var $interactiveBuffer = ''; + + /** + * Current log size + * + * Should never exceed self::LOG_MAX_SIZE + * + * @see self::_send_binary_packet() + * @see self::_get_binary_packet() + * @var int + * @access private + */ + var $log_size; + + /** + * Timeout + * + * @see self::setTimeout() + * @access private + */ + var $timeout; + + /** + * Current Timeout + * + * @see self::_get_channel_packet() + * @access private + */ + var $curTimeout; + + /** + * Log Boundary + * + * @see self::_format_log() + * @access private + */ + var $log_boundary = ':'; + + /** + * Log Long Width + * + * @see self::_format_log() + * @access private + */ + var $log_long_width = 65; + + /** + * Log Short Width + * + * @see self::_format_log() + * @access private + */ + var $log_short_width = 16; + + /** + * Hostname + * + * @see self::__construct() + * @see self::_connect() + * @var string + * @access private + */ + var $host; + + /** + * Port Number + * + * @see self::__construct() + * @see self::_connect() + * @var int + * @access private + */ + var $port; + + /** + * Timeout for initial connection + * + * Set by the constructor call. Calling setTimeout() is optional. If it's not called functions like + * exec() won't timeout unless some PHP setting forces it too. The timeout specified in the constructor, + * however, is non-optional. There will be a timeout, whether or not you set it. If you don't it'll be + * 10 seconds. It is used by fsockopen() in that function. + * + * @see self::__construct() + * @see self::_connect() + * @var int + * @access private + */ + var $connectionTimeout; + + /** + * Default cipher + * + * @see self::__construct() + * @see self::_connect() + * @var int + * @access private + */ + var $cipher; + + /** + * Default Constructor. + * + * Connects to an SSHv1 server + * + * @param string $host + * @param int $port + * @param int $timeout + * @param int $cipher + * @return \phpseclib\Net\SSH1 + * @access public + */ + function __construct($host, $port = 22, $timeout = 10, $cipher = self::CIPHER_3DES) + { + $this->protocol_flags = array( + 1 => 'NET_SSH1_MSG_DISCONNECT', + 2 => 'NET_SSH1_SMSG_PUBLIC_KEY', + 3 => 'NET_SSH1_CMSG_SESSION_KEY', + 4 => 'NET_SSH1_CMSG_USER', + 9 => 'NET_SSH1_CMSG_AUTH_PASSWORD', + 10 => 'NET_SSH1_CMSG_REQUEST_PTY', + 12 => 'NET_SSH1_CMSG_EXEC_SHELL', + 13 => 'NET_SSH1_CMSG_EXEC_CMD', + 14 => 'NET_SSH1_SMSG_SUCCESS', + 15 => 'NET_SSH1_SMSG_FAILURE', + 16 => 'NET_SSH1_CMSG_STDIN_DATA', + 17 => 'NET_SSH1_SMSG_STDOUT_DATA', + 18 => 'NET_SSH1_SMSG_STDERR_DATA', + 19 => 'NET_SSH1_CMSG_EOF', + 20 => 'NET_SSH1_SMSG_EXITSTATUS', + 33 => 'NET_SSH1_CMSG_EXIT_CONFIRMATION' + ); + + $this->_define_array($this->protocol_flags); + + $this->host = $host; + $this->port = $port; + $this->connectionTimeout = $timeout; + $this->cipher = $cipher; + } + + /** + * Connect to an SSHv1 server + * + * @return bool + * @access private + */ + function _connect() + { + $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout); + if (!$this->fsock) { + user_error(rtrim("Cannot connect to {$this->host}:{$this->port}. Error $errno. $errstr")); + return false; + } + + $this->server_identification = $init_line = fgets($this->fsock, 255); + + if (defined('NET_SSH1_LOGGING')) { + $this->_append_log('<-', $this->server_identification); + $this->_append_log('->', $this->identifier . "\r\n"); + } + + if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) { + user_error('Can only connect to SSH servers'); + return false; + } + if ($parts[1][0] != 1) { + user_error("Cannot connect to SSH $parts[1] servers"); + return false; + } + + fputs($this->fsock, $this->identifier."\r\n"); + + $response = $this->_get_binary_packet(); + if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) { + user_error('Expected SSH_SMSG_PUBLIC_KEY'); + return false; + } + + $anti_spoofing_cookie = $this->_string_shift($response[self::RESPONSE_DATA], 8); + + $this->_string_shift($response[self::RESPONSE_DATA], 4); + + if (strlen($response[self::RESPONSE_DATA]) < 2) { + return false; + } + $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); + $server_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); + $this->server_key_public_exponent = $server_key_public_exponent; + + if (strlen($response[self::RESPONSE_DATA]) < 2) { + return false; + } + $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); + $server_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); + + $this->server_key_public_modulus = $server_key_public_modulus; + + $this->_string_shift($response[self::RESPONSE_DATA], 4); + + if (strlen($response[self::RESPONSE_DATA]) < 2) { + return false; + } + $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); + $host_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); + $this->host_key_public_exponent = $host_key_public_exponent; + + if (strlen($response[self::RESPONSE_DATA]) < 2) { + return false; + } + $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); + $host_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); + + $this->host_key_public_modulus = $host_key_public_modulus; + + $this->_string_shift($response[self::RESPONSE_DATA], 4); + + // get a list of the supported ciphers + if (strlen($response[self::RESPONSE_DATA]) < 4) { + return false; + } + extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4))); + + foreach ($this->supported_ciphers as $mask => $name) { + if (($supported_ciphers_mask & (1 << $mask)) == 0) { + unset($this->supported_ciphers[$mask]); + } + } + + // get a list of the supported authentications + if (strlen($response[self::RESPONSE_DATA]) < 4) { + return false; + } + extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4))); + foreach ($this->supported_authentications as $mask => $name) { + if (($supported_authentications_mask & (1 << $mask)) == 0) { + unset($this->supported_authentications[$mask]); + } + } + + $session_id = pack('H*', md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie)); + + $session_key = Random::string(32); + $double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0)); + + if ($server_key_public_modulus->compare($host_key_public_modulus) < 0) { + $double_encrypted_session_key = $this->_rsa_crypt( + $double_encrypted_session_key, + array( + $server_key_public_exponent, + $server_key_public_modulus + ) + ); + $double_encrypted_session_key = $this->_rsa_crypt( + $double_encrypted_session_key, + array( + $host_key_public_exponent, + $host_key_public_modulus + ) + ); + } else { + $double_encrypted_session_key = $this->_rsa_crypt( + $double_encrypted_session_key, + array( + $host_key_public_exponent, + $host_key_public_modulus + ) + ); + $double_encrypted_session_key = $this->_rsa_crypt( + $double_encrypted_session_key, + array( + $server_key_public_exponent, + $server_key_public_modulus + ) + ); + } + + $cipher = isset($this->supported_ciphers[$this->cipher]) ? $this->cipher : self::CIPHER_3DES; + $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0); + + if (!$this->_send_binary_packet($data)) { + user_error('Error sending SSH_CMSG_SESSION_KEY'); + return false; + } + + switch ($cipher) { + //case self::CIPHER_NONE: + // $this->crypto = new \phpseclib\Crypt\Null(); + // break; + case self::CIPHER_DES: + $this->crypto = new DES(); + $this->crypto->disablePadding(); + $this->crypto->enableContinuousBuffer(); + $this->crypto->setKey(substr($session_key, 0, 8)); + break; + case self::CIPHER_3DES: + $this->crypto = new TripleDES(TripleDES::MODE_3CBC); + $this->crypto->disablePadding(); + $this->crypto->enableContinuousBuffer(); + $this->crypto->setKey(substr($session_key, 0, 24)); + break; + //case self::CIPHER_RC4: + // $this->crypto = new RC4(); + // $this->crypto->enableContinuousBuffer(); + // $this->crypto->setKey(substr($session_key, 0, 16)); + // break; + } + + $response = $this->_get_binary_packet(); + + if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { + user_error('Expected SSH_SMSG_SUCCESS'); + return false; + } + + $this->bitmap = self::MASK_CONNECTED; + + return true; + } + + /** + * Login + * + * @param string $username + * @param string $password + * @return bool + * @access public + */ + function login($username, $password = '') + { + if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { + $this->bitmap |= self::MASK_CONSTRUCTOR; + if (!$this->_connect()) { + return false; + } + } + + if (!($this->bitmap & self::MASK_CONNECTED)) { + return false; + } + + $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username); + + if (!$this->_send_binary_packet($data)) { + user_error('Error sending SSH_CMSG_USER'); + return false; + } + + $response = $this->_get_binary_packet(); + + if ($response === true) { + return false; + } + if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { + $this->bitmap |= self::MASK_LOGIN; + return true; + } elseif ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) { + user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); + return false; + } + + $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password); + + if (!$this->_send_binary_packet($data)) { + user_error('Error sending SSH_CMSG_AUTH_PASSWORD'); + return false; + } + + // remove the username and password from the last logged packet + if (defined('NET_SSH1_LOGGING') && NET_SSH1_LOGGING == self::LOG_COMPLEX) { + $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen('password'), 'password'); + $this->message_log[count($this->message_log) - 1] = $data; + } + + $response = $this->_get_binary_packet(); + + if ($response === true) { + return false; + } + if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { + $this->bitmap |= self::MASK_LOGIN; + return true; + } elseif ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) { + return false; + } else { + user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); + return false; + } + } + + /** + * Set Timeout + * + * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout. + * Setting $timeout to false or 0 will mean there is no timeout. + * + * @param mixed $timeout + */ + function setTimeout($timeout) + { + $this->timeout = $this->curTimeout = $timeout; + } + + /** + * Executes a command on a non-interactive shell, returns the output, and quits. + * + * An SSH1 server will close the connection after a command has been executed on a non-interactive shell. SSH2 + * servers don't, however, this isn't an SSH2 client. The way this works, on the server, is by initiating a + * shell with the -s option, as discussed in the following links: + * + * {@link http://www.faqs.org/docs/bashman/bashref_65.html http://www.faqs.org/docs/bashman/bashref_65.html} + * {@link http://www.faqs.org/docs/bashman/bashref_62.html http://www.faqs.org/docs/bashman/bashref_62.html} + * + * To execute further commands, a new \phpseclib\Net\SSH1 object will need to be created. + * + * Returns false on failure and the output, otherwise. + * + * @see self::interactiveRead() + * @see self::interactiveWrite() + * @param string $cmd + * @param bool $block + * @return mixed + * @access public + */ + function exec($cmd, $block = true) + { + if (!($this->bitmap & self::MASK_LOGIN)) { + user_error('Operation disallowed prior to login()'); + return false; + } + + $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd); + + if (!$this->_send_binary_packet($data)) { + user_error('Error sending SSH_CMSG_EXEC_CMD'); + return false; + } + + if (!$block) { + return true; + } + + $output = ''; + $response = $this->_get_binary_packet(); + + if ($response !== false) { + do { + $output.= substr($response[self::RESPONSE_DATA], 4); + $response = $this->_get_binary_packet(); + } while (is_array($response) && $response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS); + } + + $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION); + + // i don't think it's really all that important if this packet gets sent or not. + $this->_send_binary_packet($data); + + fclose($this->fsock); + + // reset the execution bitmap - a new \phpseclib\Net\SSH1 object needs to be created. + $this->bitmap = 0; + + return $output; + } + + /** + * Creates an interactive shell + * + * @see self::interactiveRead() + * @see self::interactiveWrite() + * @return bool + * @access private + */ + function _initShell() + { + // connect using the sample parameters in protocol-1.5.txt. + // according to wikipedia.org's entry on text terminals, "the fundamental type of application running on a text + // terminal is a command line interpreter or shell". thus, opening a terminal session to run the shell. + $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, self::TTY_OP_END); + + if (!$this->_send_binary_packet($data)) { + user_error('Error sending SSH_CMSG_REQUEST_PTY'); + return false; + } + + $response = $this->_get_binary_packet(); + + if ($response === true) { + return false; + } + if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { + user_error('Expected SSH_SMSG_SUCCESS'); + return false; + } + + $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL); + + if (!$this->_send_binary_packet($data)) { + user_error('Error sending SSH_CMSG_EXEC_SHELL'); + return false; + } + + $this->bitmap |= self::MASK_SHELL; + + //stream_set_blocking($this->fsock, 0); + + return true; + } + + /** + * Inputs a command into an interactive shell. + * + * @see self::interactiveWrite() + * @param string $cmd + * @return bool + * @access public + */ + function write($cmd) + { + return $this->interactiveWrite($cmd); + } + + /** + * Returns the output of an interactive shell when there's a match for $expect + * + * $expect can take the form of a string literal or, if $mode == self::READ_REGEX, + * a regular expression. + * + * @see self::write() + * @param string $expect + * @param int $mode + * @return bool + * @access public + */ + function read($expect, $mode = self::READ_SIMPLE) + { + if (!($this->bitmap & self::MASK_LOGIN)) { + user_error('Operation disallowed prior to login()'); + return false; + } + + if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { + user_error('Unable to initiate an interactive shell session'); + return false; + } + + $match = $expect; + while (true) { + if ($mode == self::READ_REGEX) { + preg_match($expect, $this->interactiveBuffer, $matches); + $match = isset($matches[0]) ? $matches[0] : ''; + } + $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false; + if ($pos !== false) { + return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match)); + } + $response = $this->_get_binary_packet(); + + if ($response === true) { + return $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)); + } + $this->interactiveBuffer.= substr($response[self::RESPONSE_DATA], 4); + } + } + + /** + * Inputs a command into an interactive shell. + * + * @see self::interactiveRead() + * @param string $cmd + * @return bool + * @access public + */ + function interactiveWrite($cmd) + { + if (!($this->bitmap & self::MASK_LOGIN)) { + user_error('Operation disallowed prior to login()'); + return false; + } + + if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { + user_error('Unable to initiate an interactive shell session'); + return false; + } + + $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd); + + if (!$this->_send_binary_packet($data)) { + user_error('Error sending SSH_CMSG_STDIN'); + return false; + } + + return true; + } + + /** + * Returns the output of an interactive shell when no more output is available. + * + * Requires PHP 4.3.0 or later due to the use of the stream_select() function. If you see stuff like + * "^[[00m", you're seeing ANSI escape codes. According to + * {@link http://support.microsoft.com/kb/101875 How to Enable ANSI.SYS in a Command Window}, "Windows NT + * does not support ANSI escape sequences in Win32 Console applications", so if you're a Windows user, + * there's not going to be much recourse. + * + * @see self::interactiveRead() + * @return string + * @access public + */ + function interactiveRead() + { + if (!($this->bitmap & self::MASK_LOGIN)) { + user_error('Operation disallowed prior to login()'); + return false; + } + + if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { + user_error('Unable to initiate an interactive shell session'); + return false; + } + + $read = array($this->fsock); + $write = $except = null; + if (stream_select($read, $write, $except, 0)) { + $response = $this->_get_binary_packet(); + return substr($response[self::RESPONSE_DATA], 4); + } else { + return ''; + } + } + + /** + * Disconnect + * + * @access public + */ + function disconnect() + { + $this->_disconnect(); + } + + /** + * Destructor. + * + * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call + * disconnect(). + * + * @access public + */ + function __destruct() + { + $this->_disconnect(); + } + + /** + * Disconnect + * + * @param string $msg + * @access private + */ + function _disconnect($msg = 'Client Quit') + { + if ($this->bitmap) { + $data = pack('C', NET_SSH1_CMSG_EOF); + $this->_send_binary_packet($data); + /* + $response = $this->_get_binary_packet(); + if ($response === true) { + $response = array(self::RESPONSE_TYPE => -1); + } + switch ($response[self::RESPONSE_TYPE]) { + case NET_SSH1_SMSG_EXITSTATUS: + $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION); + break; + default: + $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg); + } + */ + $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg); + + $this->_send_binary_packet($data); + fclose($this->fsock); + $this->bitmap = 0; + } + } + + /** + * Gets Binary Packets + * + * See 'The Binary Packet Protocol' of protocol-1.5.txt for more info. + * + * Also, this function could be improved upon by adding detection for the following exploit: + * http://www.securiteam.com/securitynews/5LP042K3FY.html + * + * @see self::_send_binary_packet() + * @return array + * @access private + */ + function _get_binary_packet() + { + if (feof($this->fsock)) { + //user_error('connection closed prematurely'); + return false; + } + + if ($this->curTimeout) { + $read = array($this->fsock); + $write = $except = null; + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $sec = floor($this->curTimeout); + $usec = 1000000 * ($this->curTimeout - $sec); + // on windows this returns a "Warning: Invalid CRT parameters detected" error + if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { + //$this->_disconnect('Timeout'); + return true; + } + $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $this->curTimeout-= $elapsed; + } + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $data = fread($this->fsock, 4); + if (strlen($data) < 4) { + return false; + } + $temp = unpack('Nlength', $data); + + $padding_length = 8 - ($temp['length'] & 7); + $length = $temp['length'] + $padding_length; + $raw = ''; + + while ($length > 0) { + $temp = fread($this->fsock, $length); + if (strlen($temp) != $length) { + return false; + } + $raw.= $temp; + $length-= strlen($temp); + } + $stop = strtok(microtime(), ' ') + strtok(''); + + if (strlen($raw) && $this->crypto !== false) { + $raw = $this->crypto->decrypt($raw); + } + + $padding = substr($raw, 0, $padding_length); + $type = $raw[$padding_length]; + $data = substr($raw, $padding_length + 1, -4); + + if (strlen($raw) < 4) { + return false; + } + $temp = unpack('Ncrc', substr($raw, -4)); + + //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) { + // user_error('Bad CRC in packet from server'); + // return false; + //} + + $type = ord($type); + + if (defined('NET_SSH1_LOGGING')) { + $temp = isset($this->protocol_flags[$type]) ? $this->protocol_flags[$type] : 'UNKNOWN'; + $temp = '<- ' . $temp . + ' (' . round($stop - $start, 4) . 's)'; + $this->_append_log($temp, $data); + } + + return array( + self::RESPONSE_TYPE => $type, + self::RESPONSE_DATA => $data + ); + } + + /** + * Sends Binary Packets + * + * Returns true on success, false on failure. + * + * @see self::_get_binary_packet() + * @param string $data + * @return bool + * @access private + */ + function _send_binary_packet($data) + { + if (feof($this->fsock)) { + //user_error('connection closed prematurely'); + return false; + } + + $length = strlen($data) + 4; + + $padding = Random::string(8 - ($length & 7)); + + $orig = $data; + $data = $padding . $data; + $data.= pack('N', $this->_crc($data)); + + if ($this->crypto !== false) { + $data = $this->crypto->encrypt($data); + } + + $packet = pack('Na*', $length, $data); + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $result = strlen($packet) == fputs($this->fsock, $packet); + $stop = strtok(microtime(), ' ') + strtok(''); + + if (defined('NET_SSH1_LOGGING')) { + $temp = isset($this->protocol_flags[ord($orig[0])]) ? $this->protocol_flags[ord($orig[0])] : 'UNKNOWN'; + $temp = '-> ' . $temp . + ' (' . round($stop - $start, 4) . 's)'; + $this->_append_log($temp, $orig); + } + + return $result; + } + + /** + * Cyclic Redundancy Check (CRC) + * + * PHP's crc32 function is implemented slightly differently than the one that SSH v1 uses, so + * we've reimplemented it. A more detailed discussion of the differences can be found after + * $crc_lookup_table's initialization. + * + * @see self::_get_binary_packet() + * @see self::_send_binary_packet() + * @param string $data + * @return int + * @access private + */ + function _crc($data) + { + static $crc_lookup_table = array( + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + ); + + // For this function to yield the same output as PHP's crc32 function, $crc would have to be + // set to 0xFFFFFFFF, initially - not 0x00000000 as it currently is. + $crc = 0x00000000; + $length = strlen($data); + + for ($i=0; $i<$length; $i++) { + // We AND $crc >> 8 with 0x00FFFFFF because we want the eight newly added bits to all + // be zero. PHP, unfortunately, doesn't always do this. 0x80000000 >> 8, as an example, + // yields 0xFF800000 - not 0x00800000. The following link elaborates: + // http://www.php.net/manual/en/language.operators.bitwise.php#57281 + $crc = (($crc >> 8) & 0x00FFFFFF) ^ $crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])]; + } + + // In addition to having to set $crc to 0xFFFFFFFF, initially, the return value must be XOR'd with + // 0xFFFFFFFF for this function to return the same thing that PHP's crc32 function would. + return $crc; + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * RSA Encrypt + * + * Returns mod(pow($m, $e), $n), where $n should be the product of two (large) primes $p and $q and where $e + * should be a number with the property that gcd($e, ($p - 1) * ($q - 1)) == 1. Could just make anything that + * calls this call modexp, instead, but I think this makes things clearer, maybe... + * + * @see self::__construct() + * @param BigInteger $m + * @param array $key + * @return BigInteger + * @access private + */ + function _rsa_crypt($m, $key) + { + /* + $rsa = new RSA(); + $rsa->loadKey($key, RSA::PUBLIC_FORMAT_RAW); + $rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1); + return $rsa->encrypt($m); + */ + + // To quote from protocol-1.5.txt: + // The most significant byte (which is only partial as the value must be + // less than the public modulus, which is never a power of two) is zero. + // + // The next byte contains the value 2 (which stands for public-key + // encrypted data in the PKCS standard [PKCS#1]). Then, there are non- + // zero random bytes to fill any unused space, a zero byte, and the data + // to be encrypted in the least significant bytes, the last byte of the + // data in the least significant byte. + + // Presumably the part of PKCS#1 they're refering to is "Section 7.2.1 Encryption Operation", + // under "7.2 RSAES-PKCS1-v1.5" and "7 Encryption schemes" of the following URL: + // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf + $modulus = $key[1]->toBytes(); + $length = strlen($modulus) - strlen($m) - 3; + $random = ''; + while (strlen($random) != $length) { + $block = Random::string($length - strlen($random)); + $block = str_replace("\x00", '', $block); + $random.= $block; + } + $temp = chr(0) . chr(2) . $random . chr(0) . $m; + + $m = new BigInteger($temp, 256); + $m = $m->modPow($key[0], $key[1]); + + return $m->toBytes(); + } + + /** + * Define Array + * + * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of + * named constants from it, using the value as the name of the constant and the index as the value of the constant. + * If any of the constants that would be defined already exists, none of the constants will be defined. + * + * @access private + */ + function _define_array() + { + $args = func_get_args(); + foreach ($args as $arg) { + foreach ($arg as $key => $value) { + if (!defined($value)) { + define($value, $key); + } else { + break 2; + } + } + } + } + + /** + * Returns a log of the packets that have been sent and received. + * + * Returns a string if NET_SSH1_LOGGING == self::LOG_COMPLEX, an array if NET_SSH1_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH1_LOGGING') + * + * @access public + * @return array|false|string + */ + function getLog() + { + if (!defined('NET_SSH1_LOGGING')) { + return false; + } + + switch (NET_SSH1_LOGGING) { + case self::LOG_SIMPLE: + return $this->protocol_flags_log; + break; + case self::LOG_COMPLEX: + return $this->_format_log($this->message_log, $this->protocol_flags_log); + break; + default: + return false; + } + } + + /** + * Formats a log for printing + * + * @param array $message_log + * @param array $message_number_log + * @access private + * @return string + */ + function _format_log($message_log, $message_number_log) + { + $output = ''; + for ($i = 0; $i < count($message_log); $i++) { + $output.= $message_number_log[$i] . "\r\n"; + $current_log = $message_log[$i]; + $j = 0; + do { + if (strlen($current_log)) { + $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 '; + } + $fragment = $this->_string_shift($current_log, $this->log_short_width); + $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary)); + // replace non ASCII printable characters with dots + // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters + // also replace < with a . since < messes up the output on web browsers + $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment); + $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n"; + $j++; + } while (strlen($current_log)); + $output.= "\r\n"; + } + + return $output; + } + + /** + * Helper function for _format_log + * + * For use with preg_replace_callback() + * + * @param array $matches + * @access private + * @return string + */ + function _format_log_helper($matches) + { + return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT); + } + + /** + * Return the server key public exponent + * + * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, + * the raw bytes. This behavior is similar to PHP's md5() function. + * + * @param bool $raw_output + * @return string + * @access public + */ + function getServerKeyPublicExponent($raw_output = false) + { + return $raw_output ? $this->server_key_public_exponent->toBytes() : $this->server_key_public_exponent->toString(); + } + + /** + * Return the server key public modulus + * + * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, + * the raw bytes. This behavior is similar to PHP's md5() function. + * + * @param bool $raw_output + * @return string + * @access public + */ + function getServerKeyPublicModulus($raw_output = false) + { + return $raw_output ? $this->server_key_public_modulus->toBytes() : $this->server_key_public_modulus->toString(); + } + + /** + * Return the host key public exponent + * + * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, + * the raw bytes. This behavior is similar to PHP's md5() function. + * + * @param bool $raw_output + * @return string + * @access public + */ + function getHostKeyPublicExponent($raw_output = false) + { + return $raw_output ? $this->host_key_public_exponent->toBytes() : $this->host_key_public_exponent->toString(); + } + + /** + * Return the host key public modulus + * + * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, + * the raw bytes. This behavior is similar to PHP's md5() function. + * + * @param bool $raw_output + * @return string + * @access public + */ + function getHostKeyPublicModulus($raw_output = false) + { + return $raw_output ? $this->host_key_public_modulus->toBytes() : $this->host_key_public_modulus->toString(); + } + + /** + * Return a list of ciphers supported by SSH1 server. + * + * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output + * is set to true, returns, instead, an array of constants. ie. instead of array('Triple-DES in CBC mode'), you'll + * get array(self::CIPHER_3DES). + * + * @param bool $raw_output + * @return array + * @access public + */ + function getSupportedCiphers($raw_output = false) + { + return $raw_output ? array_keys($this->supported_ciphers) : array_values($this->supported_ciphers); + } + + /** + * Return a list of authentications supported by SSH1 server. + * + * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output + * is set to true, returns, instead, an array of constants. ie. instead of array('password authentication'), you'll + * get array(self::AUTH_PASSWORD). + * + * @param bool $raw_output + * @return array + * @access public + */ + function getSupportedAuthentications($raw_output = false) + { + return $raw_output ? array_keys($this->supported_authentications) : array_values($this->supported_authentications); + } + + /** + * Return the server identification. + * + * @return string + * @access public + */ + function getServerIdentification() + { + return rtrim($this->server_identification); + } + + /** + * Logs data packets + * + * Makes sure that only the last 1MB worth of packets will be logged + * + * @param int $protocol_flags + * @param string $message + * @access private + */ + function _append_log($protocol_flags, $message) + { + switch (NET_SSH1_LOGGING) { + // useful for benchmarks + case self::LOG_SIMPLE: + $this->protocol_flags_log[] = $protocol_flags; + break; + // the most useful log for SSH1 + case self::LOG_COMPLEX: + $this->protocol_flags_log[] = $protocol_flags; + $this->_string_shift($message); + $this->log_size+= strlen($message); + $this->message_log[] = $message; + while ($this->log_size > self::LOG_MAX_SIZE) { + $this->log_size-= strlen(array_shift($this->message_log)); + array_shift($this->protocol_flags_log); + } + break; + // dump the output out realtime; packets may be interspersed with non packets, + // passwords won't be filtered out and select other packets may not be correctly + // identified + case self::LOG_REALTIME: + echo "
      \r\n" . $this->_format_log(array($message), array($protocol_flags)) . "\r\n
      \r\n"; + @flush(); + @ob_flush(); + break; + // basically the same thing as self::LOG_REALTIME with the caveat that self::LOG_REALTIME_FILE + // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE. + // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily + // at the beginning of the file + case self::LOG_REALTIME_FILE: + if (!isset($this->realtime_log_file)) { + // PHP doesn't seem to like using constants in fopen() + $filename = self::LOG_REALTIME_FILE; + $fp = fopen($filename, 'w'); + $this->realtime_log_file = $fp; + } + if (!is_resource($this->realtime_log_file)) { + break; + } + $entry = $this->_format_log(array($message), array($protocol_flags)); + if ($this->realtime_log_wrap) { + $temp = "<<< START >>>\r\n"; + $entry.= $temp; + fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp)); + } + $this->realtime_log_size+= strlen($entry); + if ($this->realtime_log_size > self::LOG_MAX_SIZE) { + fseek($this->realtime_log_file, 0); + $this->realtime_log_size = strlen($entry); + $this->realtime_log_wrap = true; + } + fputs($this->realtime_log_file, $entry); + } + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php b/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php new file mode 100644 index 0000000..e9b17a7 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php @@ -0,0 +1,5423 @@ + + * login('username', 'password')) { + * exit('Login Failed'); + * } + * + * echo $ssh->exec('pwd'); + * echo $ssh->exec('ls -la'); + * ?> + * + * + * + * setPassword('whatever'); + * $key->loadKey(file_get_contents('privatekey')); + * + * $ssh = new \phpseclib\Net\SSH2('www.domain.tld'); + * if (!$ssh->login('username', $key)) { + * exit('Login Failed'); + * } + * + * echo $ssh->read('username@username:~$'); + * $ssh->write("ls -la\n"); + * echo $ssh->read('username@username:~$'); + * ?> + * + * + * @category Net + * @package SSH2 + * @author Jim Wigginton + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Net; + +use phpseclib\Crypt\Base; +use phpseclib\Crypt\Blowfish; +use phpseclib\Crypt\Hash; +use phpseclib\Crypt\Random; +use phpseclib\Crypt\RC4; +use phpseclib\Crypt\Rijndael; +use phpseclib\Crypt\RSA; +use phpseclib\Crypt\TripleDES; +use phpseclib\Crypt\Twofish; +use phpseclib\Math\BigInteger; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification. +use phpseclib\System\SSH\Agent; + +/** + * Pure-PHP implementation of SSHv2. + * + * @package SSH2 + * @author Jim Wigginton + * @access public + */ +class SSH2 +{ + /**#@+ + * Compression Types + * + * @access private + */ + /** + * No compression + */ + const NET_SSH2_COMPRESSION_NONE = 1; + /** + * zlib compression + */ + const NET_SSH2_COMPRESSION_ZLIB = 2; + /** + * zlib@openssh.com + */ + const NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH = 3; + /**#@-*/ + + /**#@+ + * Execution Bitmap Masks + * + * @see \phpseclib\Net\SSH2::bitmap + * @access private + */ + const MASK_CONSTRUCTOR = 0x00000001; + const MASK_CONNECTED = 0x00000002; + const MASK_LOGIN_REQ = 0x00000004; + const MASK_LOGIN = 0x00000008; + const MASK_SHELL = 0x00000010; + const MASK_WINDOW_ADJUST = 0x00000020; + /**#@-*/ + + /**#@+ + * Channel constants + * + * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer + * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with + * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a + * recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel + * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet: + * The 'recipient channel' is the channel number given in the original + * open request, and 'sender channel' is the channel number allocated by + * the other side. + * + * @see \phpseclib\Net\SSH2::_send_channel_packet() + * @see \phpseclib\Net\SSH2::_get_channel_packet() + * @access private + */ + const CHANNEL_EXEC = 1; // PuTTy uses 0x100 + const CHANNEL_SHELL = 2; + const CHANNEL_SUBSYSTEM = 3; + const CHANNEL_AGENT_FORWARD = 4; + const CHANNEL_KEEP_ALIVE = 5; + /**#@-*/ + + /**#@+ + * @access public + * @see \phpseclib\Net\SSH2::getLog() + */ + /** + * Returns the message numbers + */ + const LOG_SIMPLE = 1; + /** + * Returns the message content + */ + const LOG_COMPLEX = 2; + /** + * Outputs the content real-time + */ + const LOG_REALTIME = 3; + /** + * Dumps the content real-time to a file + */ + const LOG_REALTIME_FILE = 4; + /** + * Make sure that the log never gets larger than this + */ + const LOG_MAX_SIZE = 1048576; // 1024 * 1024 + /**#@-*/ + + /**#@+ + * @access public + * @see \phpseclib\Net\SSH2::read() + */ + /** + * Returns when a string matching $expect exactly is found + */ + const READ_SIMPLE = 1; + /** + * Returns when a string matching the regular expression $expect is found + */ + const READ_REGEX = 2; + /** + * Returns whenever a data packet is received. + * + * Some data packets may only contain a single character so it may be necessary + * to call read() multiple times when using this option + */ + const READ_NEXT = 3; + /**#@-*/ + + /** + * The SSH identifier + * + * @var string + * @access private + */ + var $identifier; + + /** + * The Socket Object + * + * @var object + * @access private + */ + var $fsock; + + /** + * Execution Bitmap + * + * The bits that are set represent functions that have been called already. This is used to determine + * if a requisite function has been successfully executed. If not, an error should be thrown. + * + * @var int + * @access private + */ + var $bitmap = 0; + + /** + * Error information + * + * @see self::getErrors() + * @see self::getLastError() + * @var string + * @access private + */ + var $errors = array(); + + /** + * Server Identifier + * + * @see self::getServerIdentification() + * @var array|false + * @access private + */ + var $server_identifier = false; + + /** + * Key Exchange Algorithms + * + * @see self::getKexAlgorithims() + * @var array|false + * @access private + */ + var $kex_algorithms = false; + + /** + * Key Exchange Algorithm + * + * @see self::getMethodsNegotiated() + * @var string|false + * @access private + */ + var $kex_algorithm = false; + + /** + * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods + * + * @see self::_key_exchange() + * @var int + * @access private + */ + var $kex_dh_group_size_min = 1536; + + /** + * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods + * + * @see self::_key_exchange() + * @var int + * @access private + */ + var $kex_dh_group_size_preferred = 2048; + + /** + * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods + * + * @see self::_key_exchange() + * @var int + * @access private + */ + var $kex_dh_group_size_max = 4096; + + /** + * Server Host Key Algorithms + * + * @see self::getServerHostKeyAlgorithms() + * @var array|false + * @access private + */ + var $server_host_key_algorithms = false; + + /** + * Supported Private Key Algorithms + * + * In theory this should be the same as the Server Host Key Algorithms but, in practice, + * some servers (eg. Azure) will support rsa-sha2-512 as a server host key algorithm but + * not a private key algorithm + * + * @see self::privatekey_login() + * @var array|false + */ + var $supported_private_key_algorithms = false; + + /** + * Encryption Algorithms: Client to Server + * + * @see self::getEncryptionAlgorithmsClient2Server() + * @var array|false + * @access private + */ + var $encryption_algorithms_client_to_server = false; + + /** + * Encryption Algorithms: Server to Client + * + * @see self::getEncryptionAlgorithmsServer2Client() + * @var array|false + * @access private + */ + var $encryption_algorithms_server_to_client = false; + + /** + * MAC Algorithms: Client to Server + * + * @see self::getMACAlgorithmsClient2Server() + * @var array|false + * @access private + */ + var $mac_algorithms_client_to_server = false; + + /** + * MAC Algorithms: Server to Client + * + * @see self::getMACAlgorithmsServer2Client() + * @var array|false + * @access private + */ + var $mac_algorithms_server_to_client = false; + + /** + * Compression Algorithms: Client to Server + * + * @see self::getCompressionAlgorithmsClient2Server() + * @var array|false + * @access private + */ + var $compression_algorithms_client_to_server = false; + + /** + * Compression Algorithms: Server to Client + * + * @see self::getCompressionAlgorithmsServer2Client() + * @var array|false + * @access private + */ + var $compression_algorithms_server_to_client = false; + + /** + * Languages: Server to Client + * + * @see self::getLanguagesServer2Client() + * @var array|false + * @access private + */ + var $languages_server_to_client = false; + + /** + * Languages: Client to Server + * + * @see self::getLanguagesClient2Server() + * @var array|false + * @access private + */ + var $languages_client_to_server = false; + + /** + * Preferred Algorithms + * + * @see self::setPreferredAlgorithms() + * @var array + * @access private + */ + var $preferred = array(); + + /** + * Block Size for Server to Client Encryption + * + * "Note that the length of the concatenation of 'packet_length', + * 'padding_length', 'payload', and 'random padding' MUST be a multiple + * of the cipher block size or 8, whichever is larger. This constraint + * MUST be enforced, even when using stream ciphers." + * + * -- http://tools.ietf.org/html/rfc4253#section-6 + * + * @see self::__construct() + * @see self::_send_binary_packet() + * @var int + * @access private + */ + var $encrypt_block_size = 8; + + /** + * Block Size for Client to Server Encryption + * + * @see self::__construct() + * @see self::_get_binary_packet() + * @var int + * @access private + */ + var $decrypt_block_size = 8; + + /** + * Server to Client Encryption Object + * + * @see self::_get_binary_packet() + * @var object + * @access private + */ + var $decrypt = false; + + /** + * Client to Server Encryption Object + * + * @see self::_send_binary_packet() + * @var object + * @access private + */ + var $encrypt = false; + + /** + * Client to Server HMAC Object + * + * @see self::_send_binary_packet() + * @var object + * @access private + */ + var $hmac_create = false; + + /** + * Server to Client HMAC Object + * + * @see self::_get_binary_packet() + * @var object + * @access private + */ + var $hmac_check = false; + + /** + * Size of server to client HMAC + * + * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read. + * For the client to server side, the HMAC object will make the HMAC as long as it needs to be. All we need to do is + * append it. + * + * @see self::_get_binary_packet() + * @var int + * @access private + */ + var $hmac_size = false; + + /** + * Server Public Host Key + * + * @see self::getServerPublicHostKey() + * @var string + * @access private + */ + var $server_public_host_key; + + /** + * Session identifier + * + * "The exchange hash H from the first key exchange is additionally + * used as the session identifier, which is a unique identifier for + * this connection." + * + * -- http://tools.ietf.org/html/rfc4253#section-7.2 + * + * @see self::_key_exchange() + * @var string + * @access private + */ + var $session_id = false; + + /** + * Exchange hash + * + * The current exchange hash + * + * @see self::_key_exchange() + * @var string + * @access private + */ + var $exchange_hash = false; + + /** + * Message Numbers + * + * @see self::__construct() + * @var array + * @access private + */ + var $message_numbers = array(); + + /** + * Disconnection Message 'reason codes' defined in RFC4253 + * + * @see self::__construct() + * @var array + * @access private + */ + var $disconnect_reasons = array(); + + /** + * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254 + * + * @see self::__construct() + * @var array + * @access private + */ + var $channel_open_failure_reasons = array(); + + /** + * Terminal Modes + * + * @link http://tools.ietf.org/html/rfc4254#section-8 + * @see self::__construct() + * @var array + * @access private + */ + var $terminal_modes = array(); + + /** + * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes + * + * @link http://tools.ietf.org/html/rfc4254#section-5.2 + * @see self::__construct() + * @var array + * @access private + */ + var $channel_extended_data_type_codes = array(); + + /** + * Send Sequence Number + * + * See 'Section 6.4. Data Integrity' of rfc4253 for more info. + * + * @see self::_send_binary_packet() + * @var int + * @access private + */ + var $send_seq_no = 0; + + /** + * Get Sequence Number + * + * See 'Section 6.4. Data Integrity' of rfc4253 for more info. + * + * @see self::_get_binary_packet() + * @var int + * @access private + */ + var $get_seq_no = 0; + + /** + * Server Channels + * + * Maps client channels to server channels + * + * @see self::_get_channel_packet() + * @see self::exec() + * @var array + * @access private + */ + var $server_channels = array(); + + /** + * Channel Buffers + * + * If a client requests a packet from one channel but receives two packets from another those packets should + * be placed in a buffer + * + * @see self::_get_channel_packet() + * @see self::exec() + * @var array + * @access private + */ + var $channel_buffers = array(); + + /** + * Channel Status + * + * Contains the type of the last sent message + * + * @see self::_get_channel_packet() + * @var array + * @access private + */ + var $channel_status = array(); + + /** + * Packet Size + * + * Maximum packet size indexed by channel + * + * @see self::_send_channel_packet() + * @var array + * @access private + */ + var $packet_size_client_to_server = array(); + + /** + * Message Number Log + * + * @see self::getLog() + * @var array + * @access private + */ + var $message_number_log = array(); + + /** + * Message Log + * + * @see self::getLog() + * @var array + * @access private + */ + var $message_log = array(); + + /** + * The Window Size + * + * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB) + * + * @var int + * @see self::_send_channel_packet() + * @see self::exec() + * @access private + */ + var $window_size = 0x7FFFFFFF; + + /** + * What we resize the window to + * + * When PuTTY resizes the window it doesn't add an additional 0x7FFFFFFF bytes - it adds 0x40000000 bytes. + * Some SFTP clients (GoAnywhere) don't support adding 0x7FFFFFFF to the window size after the fact so + * we'll just do what PuTTY does + * + * @var int + * @see self::_send_channel_packet() + * @see self::exec() + * @access private + */ + var $window_resize = 0x40000000; + + /** + * Window size, server to client + * + * Window size indexed by channel + * + * @see self::_send_channel_packet() + * @var array + * @access private + */ + var $window_size_server_to_client = array(); + + /** + * Window size, client to server + * + * Window size indexed by channel + * + * @see self::_get_channel_packet() + * @var array + * @access private + */ + var $window_size_client_to_server = array(); + + /** + * Server signature + * + * Verified against $this->session_id + * + * @see self::getServerPublicHostKey() + * @var string + * @access private + */ + var $signature = ''; + + /** + * Server signature format + * + * ssh-rsa or ssh-dss. + * + * @see self::getServerPublicHostKey() + * @var string + * @access private + */ + var $signature_format = ''; + + /** + * Interactive Buffer + * + * @see self::read() + * @var array + * @access private + */ + var $interactiveBuffer = ''; + + /** + * Current log size + * + * Should never exceed self::LOG_MAX_SIZE + * + * @see self::_send_binary_packet() + * @see self::_get_binary_packet() + * @var int + * @access private + */ + var $log_size; + + /** + * Timeout + * + * @see self::setTimeout() + * @access private + */ + var $timeout; + + /** + * Current Timeout + * + * @see self::_get_channel_packet() + * @access private + */ + var $curTimeout; + + /** + * Keep Alive Interval + * + * @see self::setKeepAlive() + * @access private + */ + var $keepAlive; + + /** + * Real-time log file pointer + * + * @see self::_append_log() + * @var resource + * @access private + */ + var $realtime_log_file; + + /** + * Real-time log file size + * + * @see self::_append_log() + * @var int + * @access private + */ + var $realtime_log_size; + + /** + * Has the signature been validated? + * + * @see self::getServerPublicHostKey() + * @var bool + * @access private + */ + var $signature_validated = false; + + /** + * Real-time log file wrap boolean + * + * @see self::_append_log() + * @access private + */ + var $realtime_log_wrap; + + /** + * Flag to suppress stderr from output + * + * @see self::enableQuietMode() + * @access private + */ + var $quiet_mode = false; + + /** + * Time of first network activity + * + * @var int + * @access private + */ + var $last_packet; + + /** + * Exit status returned from ssh if any + * + * @var int + * @access private + */ + var $exit_status; + + /** + * Flag to request a PTY when using exec() + * + * @var bool + * @see self::enablePTY() + * @access private + */ + var $request_pty = false; + + /** + * Flag set while exec() is running when using enablePTY() + * + * @var bool + * @access private + */ + var $in_request_pty_exec = false; + + /** + * Flag set after startSubsystem() is called + * + * @var bool + * @access private + */ + var $in_subsystem; + + /** + * Contents of stdError + * + * @var string + * @access private + */ + var $stdErrorLog; + + /** + * The Last Interactive Response + * + * @see self::_keyboard_interactive_process() + * @var string + * @access private + */ + var $last_interactive_response = ''; + + /** + * Keyboard Interactive Request / Responses + * + * @see self::_keyboard_interactive_process() + * @var array + * @access private + */ + var $keyboard_requests_responses = array(); + + /** + * Banner Message + * + * Quoting from the RFC, "in some jurisdictions, sending a warning message before + * authentication may be relevant for getting legal protection." + * + * @see self::_filter() + * @see self::getBannerMessage() + * @var string + * @access private + */ + var $banner_message = ''; + + /** + * Did read() timeout or return normally? + * + * @see self::isTimeout() + * @var bool + * @access private + */ + var $is_timeout = false; + + /** + * Log Boundary + * + * @see self::_format_log() + * @var string + * @access private + */ + var $log_boundary = ':'; + + /** + * Log Long Width + * + * @see self::_format_log() + * @var int + * @access private + */ + var $log_long_width = 65; + + /** + * Log Short Width + * + * @see self::_format_log() + * @var int + * @access private + */ + var $log_short_width = 16; + + /** + * Hostname + * + * @see self::__construct() + * @see self::_connect() + * @var string + * @access private + */ + var $host; + + /** + * Port Number + * + * @see self::__construct() + * @see self::_connect() + * @var int + * @access private + */ + var $port; + + /** + * Number of columns for terminal window size + * + * @see self::getWindowColumns() + * @see self::setWindowColumns() + * @see self::setWindowSize() + * @var int + * @access private + */ + var $windowColumns = 80; + + /** + * Number of columns for terminal window size + * + * @see self::getWindowRows() + * @see self::setWindowRows() + * @see self::setWindowSize() + * @var int + * @access private + */ + var $windowRows = 24; + + /** + * Crypto Engine + * + * @see self::setCryptoEngine() + * @see self::_key_exchange() + * @var int + * @access private + */ + var $crypto_engine = false; + + /** + * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario + * + * @var System_SSH_Agent + * @access private + */ + var $agent; + + /** + * Send the identification string first? + * + * @var bool + * @access private + */ + var $send_id_string_first = true; + + /** + * Send the key exchange initiation packet first? + * + * @var bool + * @access private + */ + var $send_kex_first = true; + + /** + * Some versions of OpenSSH incorrectly calculate the key size + * + * @var bool + * @access private + */ + var $bad_key_size_fix = false; + + /** + * Should we try to re-connect to re-establish keys? + * + * @var bool + * @access private + */ + var $retry_connect = false; + + /** + * Binary Packet Buffer + * + * @var string|false + * @access private + */ + var $binary_packet_buffer = false; + + /** + * Preferred Signature Format + * + * @var string|false + * @access private + */ + var $preferred_signature_format = false; + + /** + * Authentication Credentials + * + * @var array + * @access private + */ + var $auth = array(); + + /** + * The authentication methods that may productively continue authentication. + * + * @see https://tools.ietf.org/html/rfc4252#section-5.1 + * @var array|null + * @access private + */ + var $auth_methods_to_continue = null; + + /** + * Compression method + * + * @var int + * @access private + */ + var $compress = self::NET_SSH2_COMPRESSION_NONE; + + /** + * Decompression method + * + * @var resource|object + * @access private + */ + var $decompress = self::NET_SSH2_COMPRESSION_NONE; + + /** + * Compression context + * + * @var int + * @access private + */ + var $compress_context; + + /** + * Decompression context + * + * @var resource|object + * @access private + */ + var $decompress_context; + + /** + * Regenerate Compression Context + * + * @var bool + * @access private + */ + var $regenerate_compression_context = false; + + /** + * Regenerate Decompression Context + * + * @var bool + * @access private + */ + var $regenerate_decompression_context = false; + + /** + * Smart multi-factor authentication flag + * + * @var bool + * @access private + */ + var $smartMFA = true; + + /** + * Default Constructor. + * + * $host can either be a string, representing the host, or a stream resource. + * + * @param mixed $host + * @param int $port + * @param int $timeout + * @see self::login() + * @return \phpseclib\Net\SSH2 + * @access public + */ + function __construct($host, $port = 22, $timeout = 10) + { + $this->message_numbers = array( + 1 => 'NET_SSH2_MSG_DISCONNECT', + 2 => 'NET_SSH2_MSG_IGNORE', + 3 => 'NET_SSH2_MSG_UNIMPLEMENTED', + 4 => 'NET_SSH2_MSG_DEBUG', + 5 => 'NET_SSH2_MSG_SERVICE_REQUEST', + 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT', + 20 => 'NET_SSH2_MSG_KEXINIT', + 21 => 'NET_SSH2_MSG_NEWKEYS', + 30 => 'NET_SSH2_MSG_KEXDH_INIT', + 31 => 'NET_SSH2_MSG_KEXDH_REPLY', + 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST', + 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE', + 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS', + 53 => 'NET_SSH2_MSG_USERAUTH_BANNER', + + 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST', + 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS', + 82 => 'NET_SSH2_MSG_REQUEST_FAILURE', + 90 => 'NET_SSH2_MSG_CHANNEL_OPEN', + 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION', + 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE', + 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST', + 94 => 'NET_SSH2_MSG_CHANNEL_DATA', + 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA', + 96 => 'NET_SSH2_MSG_CHANNEL_EOF', + 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE', + 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST', + 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS', + 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE' + ); + $this->disconnect_reasons = array( + 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT', + 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR', + 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED', + 4 => 'NET_SSH2_DISCONNECT_RESERVED', + 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR', + 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR', + 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE', + 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED', + 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE', + 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST', + 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION', + 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS', + 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER', + 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE', + 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME' + ); + $this->channel_open_failure_reasons = array( + 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED' + ); + $this->terminal_modes = array( + 0 => 'NET_SSH2_TTY_OP_END' + ); + $this->channel_extended_data_type_codes = array( + 1 => 'NET_SSH2_EXTENDED_DATA_STDERR' + ); + + $this->_define_array( + $this->message_numbers, + $this->disconnect_reasons, + $this->channel_open_failure_reasons, + $this->terminal_modes, + $this->channel_extended_data_type_codes, + array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'), + array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'), + array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', + 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'), + // RFC 4419 - diffie-hellman-group-exchange-sha{1,256} + array(30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD', + 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP', + 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT', + 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY', + 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'), + // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org) + array(30 => 'NET_SSH2_MSG_KEX_ECDH_INIT', + 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY') + ); + + if (is_resource($host)) { + $this->fsock = $host; + return; + } + + if (is_string($host)) { + $this->host = $host; + $this->port = $port; + $this->timeout = $timeout; + } + } + + /** + * Set Crypto Engine Mode + * + * Possible $engine values: + * CRYPT_MODE_INTERNAL, CRYPT_MODE_MCRYPT + * + * @param int $engine + * @access public + */ + function setCryptoEngine($engine) + { + $this->crypto_engine = $engine; + } + + /** + * Send Identification String First + * + * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established, + * both sides MUST send an identification string". It does not say which side sends it first. In + * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy + * + * @access public + */ + function sendIdentificationStringFirst() + { + $this->send_id_string_first = true; + } + + /** + * Send Identification String Last + * + * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established, + * both sides MUST send an identification string". It does not say which side sends it first. In + * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy + * + * @access public + */ + function sendIdentificationStringLast() + { + $this->send_id_string_first = false; + } + + /** + * Send SSH_MSG_KEXINIT First + * + * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending + * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory + * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy + * + * @access public + */ + function sendKEXINITFirst() + { + $this->send_kex_first = true; + } + + /** + * Send SSH_MSG_KEXINIT Last + * + * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending + * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory + * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy + * + * @access public + */ + function sendKEXINITLast() + { + $this->send_kex_first = false; + } + + /** + * Connect to an SSHv2 server + * + * @return bool + * @access private + */ + function _connect() + { + if ($this->bitmap & self::MASK_CONSTRUCTOR) { + return false; + } + + $this->bitmap |= self::MASK_CONSTRUCTOR; + + $this->curTimeout = $this->timeout; + + $this->last_packet = microtime(true); + + if (!is_resource($this->fsock)) { + $start = microtime(true); + // with stream_select a timeout of 0 means that no timeout takes place; + // with fsockopen a timeout of 0 means that you instantly timeout + // to resolve this incompatibility a timeout of 100,000 will be used for fsockopen if timeout is 0 + $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout); + if (!$this->fsock) { + $host = $this->host . ':' . $this->port; + user_error(rtrim("Cannot connect to $host. Error $errno. $errstr")); + return false; + } + $elapsed = microtime(true) - $start; + + if ($this->curTimeout) { + $this->curTimeout-= $elapsed; + if ($this->curTimeout < 0) { + $this->is_timeout = true; + return false; + } + } + } + + $this->identifier = $this->_generate_identifier(); + + if ($this->send_id_string_first) { + fputs($this->fsock, $this->identifier . "\r\n"); + } + + /* According to the SSH2 specs, + + "The server MAY send other lines of data before sending the version + string. Each line SHOULD be terminated by a Carriage Return and Line + Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded + in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients + MUST be able to process such lines." */ + $data = ''; + while (!feof($this->fsock) && !preg_match('#(.*)^(SSH-(\d\.\d+).*)#ms', $data, $matches)) { + $line = ''; + while (true) { + if ($this->curTimeout) { + if ($this->curTimeout < 0) { + $this->is_timeout = true; + return false; + } + $read = array($this->fsock); + $write = $except = null; + $start = microtime(true); + $sec = (int) floor($this->curTimeout); + $usec = (int) (1000000 * ($this->curTimeout - $sec)); + // on windows this returns a "Warning: Invalid CRT parameters detected" error + // the !count() is done as a workaround for + if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { + $this->is_timeout = true; + return false; + } + $elapsed = microtime(true) - $start; + $this->curTimeout-= $elapsed; + } + + $temp = stream_get_line($this->fsock, 255, "\n"); + if (strlen($temp) == 255) { + continue; + } + + if ($temp === false) { + return false; + } + + $line.= "$temp\n"; + + // quoting RFC4253, "Implementers who wish to maintain + // compatibility with older, undocumented versions of this protocol may + // want to process the identification string without expecting the + // presence of the carriage return character for reasons described in + // Section 5 of this document." + + //if (substr($line, -2) == "\r\n") { + // break; + //} + + break; + } + + $data.= $line; + } + + if (feof($this->fsock)) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + + $extra = $matches[1]; + + if (defined('NET_SSH2_LOGGING')) { + $this->_append_log('<-', $matches[0]); + $this->_append_log('->', $this->identifier . "\r\n"); + } + + $this->server_identifier = trim($temp, "\r\n"); + if (strlen($extra)) { + $this->errors[] = $data; + } + + if (version_compare($matches[3], '1.99', '<')) { + user_error("Cannot connect to SSH $matches[3] servers"); + return false; + } + + if (!$this->send_id_string_first) { + fputs($this->fsock, $this->identifier . "\r\n"); + } + + if (!$this->send_kex_first) { + $response = $this->_get_binary_packet(); + if ($response === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + + if (!strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) { + user_error('Expected SSH_MSG_KEXINIT'); + return false; + } + + if (!$this->_key_exchange($response)) { + return false; + } + } + + if ($this->send_kex_first && !$this->_key_exchange()) { + return false; + } + + $this->bitmap|= self::MASK_CONNECTED; + + return true; + } + + /** + * Generates the SSH identifier + * + * You should overwrite this method in your own class if you want to use another identifier + * + * @access protected + * @return string + */ + function _generate_identifier() + { + $identifier = 'SSH-2.0-phpseclib_2.0'; + + $ext = array(); + if (function_exists('sodium_crypto_box_publickey_from_secretkey')) { + $ext[] = 'libsodium'; + } + + if (extension_loaded('openssl')) { + $ext[] = 'openssl'; + } elseif (extension_loaded('mcrypt')) { + $ext[] = 'mcrypt'; + } + + if (extension_loaded('gmp')) { + $ext[] = 'gmp'; + } elseif (extension_loaded('bcmath')) { + $ext[] = 'bcmath'; + } + + if (!empty($ext)) { + $identifier .= ' (' . implode(', ', $ext) . ')'; + } + + return $identifier; + } + + /** + * Key Exchange + * + * @param string $kexinit_payload_server optional + * @access private + */ + function _key_exchange($kexinit_payload_server = false) + { + $preferred = $this->preferred; + $send_kex = true; + + $kex_algorithms = isset($preferred['kex']) ? + $preferred['kex'] : + $this->getSupportedKEXAlgorithms(); + $server_host_key_algorithms = isset($preferred['hostkey']) ? + $preferred['hostkey'] : + $this->getSupportedHostKeyAlgorithms(); + $s2c_encryption_algorithms = isset($preferred['server_to_client']['crypt']) ? + $preferred['server_to_client']['crypt'] : + $this->getSupportedEncryptionAlgorithms(); + $c2s_encryption_algorithms = isset($preferred['client_to_server']['crypt']) ? + $preferred['client_to_server']['crypt'] : + $this->getSupportedEncryptionAlgorithms(); + $s2c_mac_algorithms = isset($preferred['server_to_client']['mac']) ? + $preferred['server_to_client']['mac'] : + $this->getSupportedMACAlgorithms(); + $c2s_mac_algorithms = isset($preferred['client_to_server']['mac']) ? + $preferred['client_to_server']['mac'] : + $this->getSupportedMACAlgorithms(); + $s2c_compression_algorithms = isset($preferred['server_to_client']['comp']) ? + $preferred['server_to_client']['comp'] : + $this->getSupportedCompressionAlgorithms(); + $c2s_compression_algorithms = isset($preferred['client_to_server']['comp']) ? + $preferred['client_to_server']['comp'] : + $this->getSupportedCompressionAlgorithms(); + + // some SSH servers have buggy implementations of some of the above algorithms + switch (true) { + case $this->server_identifier == 'SSH-2.0-SSHD': + case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK': + if (!isset($preferred['server_to_client']['mac'])) { + $s2c_mac_algorithms = array_values(array_diff( + $s2c_mac_algorithms, + array('hmac-sha1-96', 'hmac-md5-96') + )); + } + if (!isset($preferred['client_to_server']['mac'])) { + $c2s_mac_algorithms = array_values(array_diff( + $c2s_mac_algorithms, + array('hmac-sha1-96', 'hmac-md5-96') + )); + } + } + + $str_kex_algorithms = implode(',', $kex_algorithms); + $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms); + $encryption_algorithms_server_to_client = implode(',', $s2c_encryption_algorithms); + $encryption_algorithms_client_to_server = implode(',', $c2s_encryption_algorithms); + $mac_algorithms_server_to_client = implode(',', $s2c_mac_algorithms); + $mac_algorithms_client_to_server = implode(',', $c2s_mac_algorithms); + $compression_algorithms_server_to_client = implode(',', $s2c_compression_algorithms); + $compression_algorithms_client_to_server = implode(',', $c2s_compression_algorithms); + + $client_cookie = Random::string(16); + + $kexinit_payload_client = pack( + 'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN', + NET_SSH2_MSG_KEXINIT, + $client_cookie, + strlen($str_kex_algorithms), + $str_kex_algorithms, + strlen($str_server_host_key_algorithms), + $str_server_host_key_algorithms, + strlen($encryption_algorithms_client_to_server), + $encryption_algorithms_client_to_server, + strlen($encryption_algorithms_server_to_client), + $encryption_algorithms_server_to_client, + strlen($mac_algorithms_client_to_server), + $mac_algorithms_client_to_server, + strlen($mac_algorithms_server_to_client), + $mac_algorithms_server_to_client, + strlen($compression_algorithms_client_to_server), + $compression_algorithms_client_to_server, + strlen($compression_algorithms_server_to_client), + $compression_algorithms_server_to_client, + 0, + '', + 0, + '', + 0, + 0 + ); + + if ($kexinit_payload_server === false) { + if (!$this->_send_binary_packet($kexinit_payload_client)) { + return false; + } + + $kexinit_payload_server = $this->_get_binary_packet(); + if ($kexinit_payload_server === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + + if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) { + user_error('Expected SSH_MSG_KEXINIT'); + return false; + } + + $send_kex = false; + } + + $response = $kexinit_payload_server; + $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT) + $server_cookie = $this->_string_shift($response, 16); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); + + $this->supported_private_key_algorithms = $this->server_host_key_algorithms; + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + if (!strlen($response)) { + return false; + } + extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1))); + $first_kex_packet_follows = $first_kex_packet_follows != 0; + + if ($send_kex && !$this->_send_binary_packet($kexinit_payload_client)) { + return false; + } + + // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange + // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the + // diffie-hellman key exchange as fast as possible + $decrypt = $this->_array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client); + $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt); + if ($decryptKeyLength === null) { + user_error('No compatible server to client encryption algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $encrypt = $this->_array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server); + $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt); + if ($encryptKeyLength === null) { + user_error('No compatible client to server encryption algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + // through diffie-hellman key exchange a symmetric key is obtained + $this->kex_algorithm = $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms); + if ($kex_algorithm === false) { + user_error('No compatible key exchange algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); + if ($server_host_key_algorithm === false) { + user_error('No compatible server host key algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $mac_algorithm_in = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client); + if ($mac_algorithm_in === false) { + user_error('No compatible server to client message authentication algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $compression_map = array( + 'none' => self::NET_SSH2_COMPRESSION_NONE, + 'zlib' => self::NET_SSH2_COMPRESSION_ZLIB, + 'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH + ); + + $compression_algorithm_out = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server); + if ($compression_algorithm_out === false) { + user_error('No compatible client to server compression algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + $this->compress = $compression_map[$compression_algorithm_out]; + + $compression_algorithm_in = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client); + if ($compression_algorithm_in === false) { + user_error('No compatible server to client compression algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + $this->decompress = $compression_map[$compression_algorithm_in]; + + // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty. + $exchange_hash_rfc4419 = ''; + + if ($kex_algorithm === 'curve25519-sha256@libssh.org') { + $x = Random::string(32); + $eBytes = sodium_crypto_box_publickey_from_secretkey($x); + $clientKexInitMessage = 'NET_SSH2_MSG_KEX_ECDH_INIT'; + $serverKexReplyMessage = 'NET_SSH2_MSG_KEX_ECDH_REPLY'; + $kexHash = new Hash('sha256'); + } else { + if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) { + $dh_group_sizes_packed = pack( + 'NNN', + $this->kex_dh_group_size_min, + $this->kex_dh_group_size_preferred, + $this->kex_dh_group_size_max + ); + $packet = pack( + 'Ca*', + NET_SSH2_MSG_KEXDH_GEX_REQUEST, + $dh_group_sizes_packed + ); + if (!$this->_send_binary_packet($packet)) { + return false; + } + $this->_updateLogHistory('UNKNOWN (34)', 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'); + + $response = $this->_get_binary_packet(); + if ($response === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + extract(unpack('Ctype', $this->_string_shift($response, 1))); + if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) { + user_error('Expected SSH_MSG_KEX_DH_GEX_GROUP'); + return false; + } + $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEXDH_GEX_GROUP'); + + if (strlen($response) < 4) { + return false; + } + extract(unpack('NprimeLength', $this->_string_shift($response, 4))); + $primeBytes = $this->_string_shift($response, $primeLength); + $prime = new BigInteger($primeBytes, -256); + + if (strlen($response) < 4) { + return false; + } + extract(unpack('NgLength', $this->_string_shift($response, 4))); + $gBytes = $this->_string_shift($response, $gLength); + $g = new BigInteger($gBytes, -256); + + $exchange_hash_rfc4419 = pack( + 'a*Na*Na*', + $dh_group_sizes_packed, + $primeLength, + $primeBytes, + $gLength, + $gBytes + ); + + $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_GEX_INIT'; + $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_GEX_REPLY'; + } else { + switch ($kex_algorithm) { + // see http://tools.ietf.org/html/rfc2409#section-6.2 and + // http://tools.ietf.org/html/rfc2412, appendex E + case 'diffie-hellman-group1-sha1': + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . + '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . + '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'; + break; + // see http://tools.ietf.org/html/rfc3526#section-3 + case 'diffie-hellman-group14-sha1': + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . + '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . + '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . + '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . + '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . + 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . + '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF'; + break; + } + // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1 + // the generator field element is 2 (decimal) and the hash function is sha1. + $g = new BigInteger(2); + $prime = new BigInteger($prime, 16); + $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_INIT'; + $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_REPLY'; + } + + switch ($kex_algorithm) { + case 'diffie-hellman-group-exchange-sha256': + $kexHash = new Hash('sha256'); + break; + default: + $kexHash = new Hash('sha1'); + } + + /* To increase the speed of the key exchange, both client and server may + reduce the size of their private exponents. It should be at least + twice as long as the key material that is generated from the shared + secret. For more details, see the paper by van Oorschot and Wiener + [VAN-OORSCHOT]. + + -- http://tools.ietf.org/html/rfc4419#section-6.2 */ + $one = new BigInteger(1); + $keyLength = min($kexHash->getLength(), max($encryptKeyLength, $decryptKeyLength)); + $max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength + $max = $max->subtract($one); + + $x = $one->random($one, $max); + $e = $g->modPow($x, $prime); + + $eBytes = $e->toBytes(true); + } + $data = pack('CNa*', constant($clientKexInitMessage), strlen($eBytes), $eBytes); + + if (!$this->_send_binary_packet($data)) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + switch ($clientKexInitMessage) { + case 'NET_SSH2_MSG_KEX_ECDH_INIT': + $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_INIT', 'NET_SSH2_MSG_KEX_ECDH_INIT'); + break; + case 'NET_SSH2_MSG_KEXDH_GEX_INIT': + $this->_updateLogHistory('UNKNOWN (32)', 'NET_SSH2_MSG_KEXDH_GEX_INIT'); + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + if (!strlen($response)) { + return false; + } + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + if ($type != constant($serverKexReplyMessage)) { + user_error("Expected $serverKexReplyMessage"); + return false; + } + switch ($serverKexReplyMessage) { + case 'NET_SSH2_MSG_KEX_ECDH_REPLY': + $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEX_ECDH_REPLY'); + break; + case 'NET_SSH2_MSG_KEXDH_GEX_REPLY': + $this->_updateLogHistory('UNKNOWN (33)', 'NET_SSH2_MSG_KEXDH_GEX_REPLY'); + } + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']); + + if (strlen($server_public_host_key) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $fBytes = $this->_string_shift($response, $temp['length']); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->signature = $this->_string_shift($response, $temp['length']); + + if (strlen($this->signature) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($this->signature, 4)); + $this->signature_format = $this->_string_shift($this->signature, $temp['length']); + + if ($kex_algorithm === 'curve25519-sha256@libssh.org') { + if (strlen($fBytes) !== 32) { + user_error('Received curve25519 public key of invalid length.'); + return false; + } + $key = new BigInteger(sodium_crypto_scalarmult($x, $fBytes), 256); + // sodium_compat doesn't emulate sodium_memzero + // also, with v1 of libsodium API the extension identifies itself as + // libsodium whereas v2 of the libsodium API (what PHP 7.2+ includes) + // identifies itself as sodium. sodium_compat uses the v1 API to + // emulate the v2 API if it's the v1 API that's available + if (extension_loaded('sodium') || extension_loaded('libsodium')) { + sodium_memzero($x); + } + } else { + $f = new BigInteger($fBytes, -256); + $key = $f->modPow($x, $prime); + } + $keyBytes = $key->toBytes(true); + + $this->exchange_hash = pack( + 'Na*Na*Na*Na*Na*a*Na*Na*Na*', + strlen($this->identifier), + $this->identifier, + strlen($this->server_identifier), + $this->server_identifier, + strlen($kexinit_payload_client), + $kexinit_payload_client, + strlen($kexinit_payload_server), + $kexinit_payload_server, + strlen($this->server_public_host_key), + $this->server_public_host_key, + $exchange_hash_rfc4419, + strlen($eBytes), + $eBytes, + strlen($fBytes), + $fBytes, + strlen($keyBytes), + $keyBytes + ); + + $this->exchange_hash = $kexHash->hash($this->exchange_hash); + + if ($this->session_id === false) { + $this->session_id = $this->exchange_hash; + } + + switch ($server_host_key_algorithm) { + case 'ssh-dss': + $expected_key_format = 'ssh-dss'; + break; + //case 'rsa-sha2-256': + //case 'rsa-sha2-512': + //case 'ssh-rsa': + default: + $expected_key_format = 'ssh-rsa'; + } + + if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) { + switch (true) { + case $this->signature_format == $server_host_key_algorithm: + case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512': + case $this->signature_format != 'ssh-rsa': + user_error('Server Host Key Algorithm Mismatch'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + } + + $packet = pack( + 'C', + NET_SSH2_MSG_NEWKEYS + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + + if ($response === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + + if (!strlen($response)) { + return false; + } + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + if ($type != NET_SSH2_MSG_NEWKEYS) { + user_error('Expected SSH_MSG_NEWKEYS'); + return false; + } + + $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes); + + $this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt); + if ($this->encrypt) { + if ($this->crypto_engine) { + $this->encrypt->setPreferredEngine($this->crypto_engine); + } + if ($this->encrypt->block_size) { + $this->encrypt_block_size = $this->encrypt->block_size; + } + $this->encrypt->enableContinuousBuffer(); + $this->encrypt->disablePadding(); + + if ($this->encrypt->getBlockLength()) { + $this->encrypt_block_size = $this->encrypt->getBlockLength() >> 3; + } + + $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id); + while ($this->encrypt_block_size > strlen($iv)) { + $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); + } + $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size)); + + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id); + while ($encryptKeyLength > strlen($key)) { + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); + } + $this->encrypt->setKey(substr($key, 0, $encryptKeyLength)); + + $this->encrypt->name = $decrypt; + } + + $this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt); + if ($this->decrypt) { + if ($this->crypto_engine) { + $this->decrypt->setPreferredEngine($this->crypto_engine); + } + if ($this->decrypt->block_size) { + $this->decrypt_block_size = $this->decrypt->block_size; + } + $this->decrypt->enableContinuousBuffer(); + $this->decrypt->disablePadding(); + + if ($this->decrypt->getBlockLength()) { + $this->decrypt_block_size = $this->decrypt->getBlockLength() >> 3; + } + + $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id); + while ($this->decrypt_block_size > strlen($iv)) { + $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); + } + $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size)); + + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id); + while ($decryptKeyLength > strlen($key)) { + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); + } + $this->decrypt->setKey(substr($key, 0, $decryptKeyLength)); + + $this->decrypt->name = $decrypt; + } + + /* The "arcfour128" algorithm is the RC4 cipher, as described in + [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream + generated by the cipher MUST be discarded, and the first byte of the + first encrypted packet MUST be encrypted using the 1537th byte of + keystream. + + -- http://tools.ietf.org/html/rfc4345#section-4 */ + if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') { + $this->encrypt->encrypt(str_repeat("\0", 1536)); + } + if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') { + $this->decrypt->decrypt(str_repeat("\0", 1536)); + } + + $mac_algorithm_out = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server); + if ($mac_algorithm_out === false) { + user_error('No compatible client to server message authentication algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $createKeyLength = 0; // ie. $mac_algorithm == 'none' + switch ($mac_algorithm_out) { + case 'hmac-sha2-256': + $this->hmac_create = new Hash('sha256'); + $createKeyLength = 32; + break; + case 'hmac-sha1': + $this->hmac_create = new Hash('sha1'); + $createKeyLength = 20; + break; + case 'hmac-sha1-96': + $this->hmac_create = new Hash('sha1-96'); + $createKeyLength = 20; + break; + case 'hmac-md5': + $this->hmac_create = new Hash('md5'); + $createKeyLength = 16; + break; + case 'hmac-md5-96': + $this->hmac_create = new Hash('md5-96'); + $createKeyLength = 16; + } + $this->hmac_create->name = $mac_algorithm_out; + + $checkKeyLength = 0; + $this->hmac_size = 0; + switch ($mac_algorithm_in) { + case 'hmac-sha2-256': + $this->hmac_check = new Hash('sha256'); + $checkKeyLength = 32; + $this->hmac_size = 32; + break; + case 'hmac-sha1': + $this->hmac_check = new Hash('sha1'); + $checkKeyLength = 20; + $this->hmac_size = 20; + break; + case 'hmac-sha1-96': + $this->hmac_check = new Hash('sha1-96'); + $checkKeyLength = 20; + $this->hmac_size = 12; + break; + case 'hmac-md5': + $this->hmac_check = new Hash('md5'); + $checkKeyLength = 16; + $this->hmac_size = 16; + break; + case 'hmac-md5-96': + $this->hmac_check = new Hash('md5-96'); + $checkKeyLength = 16; + $this->hmac_size = 12; + } + $this->hmac_check->name = $mac_algorithm_in; + + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id); + while ($createKeyLength > strlen($key)) { + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); + } + $this->hmac_create->setKey(substr($key, 0, $createKeyLength)); + + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id); + while ($checkKeyLength > strlen($key)) { + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); + } + $this->hmac_check->setKey(substr($key, 0, $checkKeyLength)); + + $this->regenerate_compression_context = $this->regenerate_decompression_context = true; + + return true; + } + + /** + * Maps an encryption algorithm name to the number of key bytes. + * + * @param string $algorithm Name of the encryption algorithm + * @return int|null Number of bytes as an integer or null for unknown + * @access private + */ + function _encryption_algorithm_to_key_size($algorithm) + { + if ($this->bad_key_size_fix && $this->_bad_algorithm_candidate($algorithm)) { + return 16; + } + + switch ($algorithm) { + case 'none': + return 0; + case 'aes128-cbc': + case 'aes128-ctr': + case 'arcfour': + case 'arcfour128': + case 'blowfish-cbc': + case 'blowfish-ctr': + case 'twofish128-cbc': + case 'twofish128-ctr': + return 16; + case '3des-cbc': + case '3des-ctr': + case 'aes192-cbc': + case 'aes192-ctr': + case 'twofish192-cbc': + case 'twofish192-ctr': + return 24; + case 'aes256-cbc': + case 'aes256-ctr': + case 'arcfour256': + case 'twofish-cbc': + case 'twofish256-cbc': + case 'twofish256-ctr': + return 32; + } + return null; + } + + /** + * Maps an encryption algorithm name to an instance of a subclass of + * \phpseclib\Crypt\Base. + * + * @param string $algorithm Name of the encryption algorithm + * @return mixed Instance of \phpseclib\Crypt\Base or null for unknown + * @access private + */ + function _encryption_algorithm_to_crypt_instance($algorithm) + { + switch ($algorithm) { + case '3des-cbc': + return new TripleDES(); + case '3des-ctr': + return new TripleDES(Base::MODE_CTR); + case 'aes256-cbc': + case 'aes192-cbc': + case 'aes128-cbc': + return new Rijndael(); + case 'aes256-ctr': + case 'aes192-ctr': + case 'aes128-ctr': + return new Rijndael(Base::MODE_CTR); + case 'blowfish-cbc': + return new Blowfish(); + case 'blowfish-ctr': + return new Blowfish(Base::MODE_CTR); + case 'twofish128-cbc': + case 'twofish192-cbc': + case 'twofish256-cbc': + case 'twofish-cbc': + return new Twofish(); + case 'twofish128-ctr': + case 'twofish192-ctr': + case 'twofish256-ctr': + return new Twofish(Base::MODE_CTR); + case 'arcfour': + case 'arcfour128': + case 'arcfour256': + return new RC4(); + } + return null; + } + + /** + * Tests whether or not proposed algorithm has a potential for issues + * + * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html + * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291 + * @param string $algorithm Name of the encryption algorithm + * @return bool + * @access private + */ + function _bad_algorithm_candidate($algorithm) + { + switch ($algorithm) { + case 'arcfour256': + case 'aes192-ctr': + case 'aes256-ctr': + return true; + } + + return false; + } + + /** + * Login + * + * The $password parameter can be a plaintext password, a \phpseclib\Crypt\RSA object or an array + * + * @param string $username + * @return bool + * @see self::_login() + * @access public + */ + function login($username) + { + $args = func_get_args(); + $this->auth[] = $args; + + // try logging with 'none' as an authentication method first since that's what + // PuTTY does + if (substr($this->server_identifier, 0, 15) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) { + if ($this->_login($username)) { + return true; + } + if (count($args) == 1) { + return false; + } + } + return call_user_func_array(array(&$this, '_login'), $args); + } + + /** + * Login Helper + * + * @param string $username + * @return bool + * @see self::_login_helper() + * @access private + */ + function _login($username) + { + if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { + if (!$this->_connect()) { + return false; + } + } + + $args = array_slice(func_get_args(), 1); + if (empty($args)) { + return $this->_login_helper($username); + } + + while (count($args)) { + if (!$this->auth_methods_to_continue || !$this->smartMFA) { + $newargs = $args; + $args = array(); + } else { + $newargs = array(); + foreach ($this->auth_methods_to_continue as $method) { + switch ($method) { + case 'publickey': + foreach ($args as $key => $arg) { + if (is_object($arg)) { + $newargs[] = $arg; + unset($args[$key]); + break; + } + } + break; + case 'keyboard-interactive': + $hasArray = $hasString = false; + foreach ($args as $arg) { + if ($hasArray || is_array($arg)) { + $hasArray = true; + break; + } + if ($hasString || is_string($arg)) { + $hasString = true; + break; + } + } + if ($hasArray && $hasString) { + foreach ($args as $key => $arg) { + if (is_array($arg)) { + $newargs[] = $arg; + break 2; + } + } + } + case 'password': + foreach ($args as $key => $arg) { + $newargs[] = $arg; + unset($args[$key]); + break; + } + } + } + } + + if (!count($newargs)) { + return false; + } + + foreach ($newargs as $arg) { + if ($this->_login_helper($username, $arg)) { + return true; + } + } + } + return false; + } + + /** + * Login Helper + * + * @param string $username + * @param string $password + * @return bool + * @access private + * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} + * by sending dummy SSH_MSG_IGNORE messages. + */ + function _login_helper($username, $password = null) + { + if (!($this->bitmap & self::MASK_CONNECTED)) { + return false; + } + + if (!($this->bitmap & self::MASK_LOGIN_REQ)) { + $packet = pack( + 'CNa*', + NET_SSH2_MSG_SERVICE_REQUEST, + strlen('ssh-userauth'), + 'ssh-userauth' + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + if ($this->retry_connect) { + $this->retry_connect = false; + if (!$this->_connect()) { + return false; + } + return $this->_login_helper($username, $password); + } + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + + if (strlen($response) < 4) { + return false; + } + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) { + user_error('Expected SSH_MSG_SERVICE_ACCEPT'); + return false; + } + $this->bitmap |= self::MASK_LOGIN_REQ; + } + + if (strlen($this->last_interactive_response)) { + return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password); + } + + if ($password instanceof RSA) { + return $this->_privatekey_login($username, $password); + } elseif ($password instanceof Agent) { + return $this->_ssh_agent_login($username, $password); + } + + if (is_array($password)) { + if ($this->_keyboard_interactive_login($username, $password)) { + $this->bitmap |= self::MASK_LOGIN; + return true; + } + return false; + } + + if (!isset($password)) { + $packet = pack( + 'CNa*Na*Na*', + NET_SSH2_MSG_USERAUTH_REQUEST, + strlen($username), + $username, + strlen('ssh-connection'), + 'ssh-connection', + strlen('none'), + 'none' + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + + if (!strlen($response)) { + return false; + } + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= self::MASK_LOGIN; + return true; + case NET_SSH2_MSG_USERAUTH_FAILURE: + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); + default: + return false; + } + } + + $packet = pack( + 'CNa*Na*Na*CNa*', + NET_SSH2_MSG_USERAUTH_REQUEST, + strlen($username), + $username, + strlen('ssh-connection'), + 'ssh-connection', + strlen('password'), + 'password', + 0, + strlen($password), + $password + ); + + // remove the username and password from the logged packet + if (!defined('NET_SSH2_LOGGING')) { + $logged = null; + } else { + $logged = pack( + 'CNa*Na*Na*CNa*', + NET_SSH2_MSG_USERAUTH_REQUEST, + strlen('username'), + 'username', + strlen('ssh-connection'), + 'ssh-connection', + strlen('password'), + 'password', + 0, + strlen('password'), + 'password' + ); + } + + if (!$this->_send_binary_packet($packet, $logged)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + + if (!strlen($response)) { + return false; + } + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed + $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'); + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $this->_string_shift($response, $length); + return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); + case NET_SSH2_MSG_USERAUTH_FAILURE: + // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees + // multi-factor authentication + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $auth_methods = explode(',', $this->_string_shift($response, $length)); + $this->auth_methods_to_continue = $auth_methods; + if (!strlen($response)) { + return false; + } + extract(unpack('Cpartial_success', $this->_string_shift($response, 1))); + $partial_success = $partial_success != 0; + + if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) { + if ($this->_keyboard_interactive_login($username, $password)) { + $this->bitmap |= self::MASK_LOGIN; + return true; + } + return false; + } + return false; + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= self::MASK_LOGIN; + return true; + } + + return false; + } + + /** + * Login via keyboard-interactive authentication + * + * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator. + * + * @param string $username + * @param string $password + * @return bool + * @access private + */ + function _keyboard_interactive_login($username, $password) + { + $packet = pack( + 'CNa*Na*Na*Na*Na*', + NET_SSH2_MSG_USERAUTH_REQUEST, + strlen($username), + $username, + strlen('ssh-connection'), + 'ssh-connection', + strlen('keyboard-interactive'), + 'keyboard-interactive', + 0, + '', + 0, + '' + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + return $this->_keyboard_interactive_process($password); + } + + /** + * Handle the keyboard-interactive requests / responses. + * + * @return bool + * @access private + */ + function _keyboard_interactive_process() + { + $responses = func_get_args(); + + if (strlen($this->last_interactive_response)) { + $response = $this->last_interactive_response; + } else { + $orig = $response = $this->_get_binary_packet(); + if ($response === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + } + + if (!strlen($response)) { + return false; + } + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_INFO_REQUEST: + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->_string_shift($response, $length); // name; may be empty + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->_string_shift($response, $length); // instruction; may be empty + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->_string_shift($response, $length); // language tag; may be empty + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nnum_prompts', $this->_string_shift($response, 4))); + + for ($i = 0; $i < count($responses); $i++) { + if (is_array($responses[$i])) { + foreach ($responses[$i] as $key => $value) { + $this->keyboard_requests_responses[$key] = $value; + } + unset($responses[$i]); + } + } + $responses = array_values($responses); + + if (isset($this->keyboard_requests_responses)) { + for ($i = 0; $i < $num_prompts; $i++) { + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + // prompt - ie. "Password: "; must not be empty + $prompt = $this->_string_shift($response, $length); + //$echo = $this->_string_shift($response) != chr(0); + foreach ($this->keyboard_requests_responses as $key => $value) { + if (substr($prompt, 0, strlen($key)) == $key) { + $responses[] = $value; + break; + } + } + } + } + + // see http://tools.ietf.org/html/rfc4256#section-3.2 + if (strlen($this->last_interactive_response)) { + $this->last_interactive_response = ''; + } else { + $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST'); + } + + if (!count($responses) && $num_prompts) { + $this->last_interactive_response = $orig; + return false; + } + + /* + After obtaining the requested information from the user, the client + MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message. + */ + // see http://tools.ietf.org/html/rfc4256#section-3.4 + $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses)); + for ($i = 0; $i < count($responses); $i++) { + $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]); + $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer'); + } + + if (!$this->_send_binary_packet($packet, $logged)) { + return false; + } + + $this->_updateLogHistory('UNKNOWN (61)', 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'); + + /* + After receiving the response, the server MUST send either an + SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another + SSH_MSG_USERAUTH_INFO_REQUEST message. + */ + // maybe phpseclib should force close the connection after x request / responses? unless something like that is done + // there could be an infinite loop of request / responses. + return $this->_keyboard_interactive_process(); + case NET_SSH2_MSG_USERAUTH_SUCCESS: + return true; + case NET_SSH2_MSG_USERAUTH_FAILURE: + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); + return false; + } + + return false; + } + + /** + * Login with an ssh-agent provided key + * + * @param string $username + * @param \phpseclib\System\SSH\Agent $agent + * @return bool + * @access private + */ + function _ssh_agent_login($username, $agent) + { + $this->agent = $agent; + $keys = $agent->requestIdentities(); + foreach ($keys as $key) { + if ($this->_privatekey_login($username, $key)) { + return true; + } + } + + return false; + } + + /** + * Login with an RSA private key + * + * @param string $username + * @param \phpseclib\Crypt\RSA $privatekey + * @return bool + * @access private + * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} + * by sending dummy SSH_MSG_IGNORE messages. + */ + function _privatekey_login($username, $privatekey) + { + // see http://tools.ietf.org/html/rfc4253#page-15 + $publickey = $privatekey->getPublicKey(RSA::PUBLIC_FORMAT_RAW); + if ($publickey === false) { + return false; + } + + $publickey = array( + 'e' => $publickey['e']->toBytes(true), + 'n' => $publickey['n']->toBytes(true) + ); + $publickey = pack( + 'Na*Na*Na*', + strlen('ssh-rsa'), + 'ssh-rsa', + strlen($publickey['e']), + $publickey['e'], + strlen($publickey['n']), + $publickey['n'] + ); + + $algos = ['rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa']; + if (isset($this->preferred['hostkey'])) { + $algos = array_intersect($this->preferred['hostkey'], $algos); + } + $algo = $this->_array_intersect_first($algos, $this->supported_private_key_algorithms); + + switch ($algo) { + case 'rsa-sha2-512': + $hash = 'sha512'; + $signatureType = 'rsa-sha2-512'; + break; + case 'rsa-sha2-256': + $hash = 'sha256'; + $signatureType = 'rsa-sha2-256'; + break; + //case 'ssh-rsa': + default: + $hash = 'sha1'; + $signatureType = 'ssh-rsa'; + } + + $part1 = pack( + 'CNa*Na*Na*', + NET_SSH2_MSG_USERAUTH_REQUEST, + strlen($username), + $username, + strlen('ssh-connection'), + 'ssh-connection', + strlen('publickey'), + 'publickey' + ); + $part2 = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($publickey), $publickey); + + $packet = $part1 . chr(0) . $part2; + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + + if (!strlen($response)) { + return false; + } + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_FAILURE: + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $auth_methods = explode(',', $this->_string_shift($response, $methodlistlen)); + if (in_array('publickey', $auth_methods) && substr($signatureType, 0, 9) == 'rsa-sha2-') { + $this->supported_private_key_algorithms = array_diff($this->supported_private_key_algorithms, array('rsa-sha2-256', 'rsa-sha2-512')); + return $this->_privatekey_login($username, $privatekey); + } + $this->auth_methods_to_continue = $auth_methods; + $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE'; + return false; + case NET_SSH2_MSG_USERAUTH_PK_OK: + // we'll just take it on faith that the public key blob and the public key algorithm name are as + // they should be + $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK'); + break; + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= self::MASK_LOGIN; + return true; + default: + user_error('Unexpected response to publickey authentication pt 1'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + + $packet = $part1 . chr(1) . $part2; + $privatekey->setSignatureMode(RSA::SIGNATURE_PKCS1); + $privatekey->setHash($hash); + $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet)); + $signature = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($signature), $signature); + $packet.= pack('Na*', strlen($signature), $signature); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + + if (!strlen($response)) { + return false; + } + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_FAILURE: + // either the login is bad or the server employs multi-factor authentication + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); + return false; + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= self::MASK_LOGIN; + return true; + } + + user_error('Unexpected response to publickey authentication pt 2'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + + /** + * Set Timeout + * + * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout. + * Setting $timeout to false or 0 will mean there is no timeout. + * + * @param mixed $timeout + * @access public + */ + function setTimeout($timeout) + { + $this->timeout = $this->curTimeout = $timeout; + } + + /** + * Set Keep Alive + * + * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number. + * + * @param int $interval + * @access public + */ + function setKeepAlive($interval) + { + $this->keepAlive = $interval; + } + + /** + * Get the output from stdError + * + * @access public + */ + function getStdError() + { + return $this->stdErrorLog; + } + + /** + * Execute Command + * + * If $callback is set to false then \phpseclib\Net\SSH2::_get_channel_packet(self::CHANNEL_EXEC) will need to be called manually. + * In all likelihood, this is not a feature you want to be taking advantage of. + * + * @param string $command + * @param Callback $callback + * @return string + * @access public + */ + function exec($command, $callback = null) + { + $this->curTimeout = $this->timeout; + $this->is_timeout = false; + $this->stdErrorLog = ''; + + if (!$this->isAuthenticated()) { + return false; + } + + if ($this->in_request_pty_exec) { + user_error('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.'); + return false; + } + + // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to + // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but, + // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway. + // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info + $this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size; + // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy + // uses 0x4000, that's what will be used here, as well. + $packet_size = 0x4000; + + $packet = pack( + 'CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, + strlen('session'), + 'session', + self::CHANNEL_EXEC, + $this->window_size_server_to_client[self::CHANNEL_EXEC], + $packet_size + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = $this->_get_channel_packet(self::CHANNEL_EXEC); + if ($response === false) { + return false; + } + + if ($this->request_pty === true) { + $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); + $packet = pack( + 'CNNa*CNa*N5a*', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL_EXEC], + strlen('pty-req'), + 'pty-req', + 1, + strlen('vt100'), + 'vt100', + $this->windowColumns, + $this->windowRows, + 0, + 0, + strlen($terminal_modes), + $terminal_modes + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; + if (!$this->_get_channel_packet(self::CHANNEL_EXEC)) { + user_error('Unable to request pseudo-terminal'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + + $this->in_request_pty_exec = true; + } + + // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things + // down. the one place where it might be desirable is if you're doing something like \phpseclib\Net\SSH2::exec('ping localhost &'). + // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then + // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but + // neither will your script. + + // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by + // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the + // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates. + $packet = pack( + 'CNNa*CNa*', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL_EXEC], + strlen('exec'), + 'exec', + 1, + strlen($command), + $command + ); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(self::CHANNEL_EXEC); + if ($response === false) { + return false; + } + + $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA; + + if ($callback === false || $this->in_request_pty_exec) { + return true; + } + + $output = ''; + while (true) { + $temp = $this->_get_channel_packet(self::CHANNEL_EXEC); + switch (true) { + case $temp === true: + return is_callable($callback) ? true : $output; + case $temp === false: + return false; + default: + if (is_callable($callback)) { + if (call_user_func($callback, $temp) === true) { + $this->_close_channel(self::CHANNEL_EXEC); + return true; + } + } else { + $output.= $temp; + } + } + } + } + + /** + * Creates an interactive shell + * + * @see self::read() + * @see self::write() + * @return bool + * @access private + */ + function _initShell() + { + if ($this->in_request_pty_exec === true) { + return true; + } + + $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size; + $packet_size = 0x4000; + + $packet = pack( + 'CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, + strlen('session'), + 'session', + self::CHANNEL_SHELL, + $this->window_size_server_to_client[self::CHANNEL_SHELL], + $packet_size + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = $this->_get_channel_packet(self::CHANNEL_SHELL); + if ($response === false) { + return false; + } + + $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); + $packet = pack( + 'CNNa*CNa*N5a*', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL_SHELL], + strlen('pty-req'), + 'pty-req', + 1, + strlen('vt100'), + 'vt100', + $this->windowColumns, + $this->windowRows, + 0, + 0, + strlen($terminal_modes), + $terminal_modes + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST; + + if (!$this->_get_channel_packet(self::CHANNEL_SHELL)) { + user_error('Unable to request pseudo-terminal'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + + $packet = pack( + 'CNNa*C', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL_SHELL], + strlen('shell'), + 'shell', + 1 + ); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_channel_packet(self::CHANNEL_SHELL); + if ($response === false) { + return false; + } + + $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA; + + $this->bitmap |= self::MASK_SHELL; + + return true; + } + + /** + * Return the channel to be used with read() / write() + * + * @see self::read() + * @see self::write() + * @return int + * @access public + */ + function _get_interactive_channel() + { + switch (true) { + case $this->in_subsystem: + return self::CHANNEL_SUBSYSTEM; + case $this->in_request_pty_exec: + return self::CHANNEL_EXEC; + default: + return self::CHANNEL_SHELL; + } + } + + /** + * Return an available open channel + * + * @return int + * @access public + */ + function _get_open_channel() + { + $channel = self::CHANNEL_EXEC; + do { + if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) { + return $channel; + } + } while ($channel++ < self::CHANNEL_SUBSYSTEM); + + return false; + } + + /** + * Returns the output of an interactive shell + * + * Returns when there's a match for $expect, which can take the form of a string literal or, + * if $mode == self::READ_REGEX, a regular expression. + * + * @see self::write() + * @param string $expect + * @param int $mode + * @return string|bool + * @access public + */ + function read($expect = '', $mode = self::READ_SIMPLE) + { + $this->curTimeout = $this->timeout; + $this->is_timeout = false; + + if (!$this->isAuthenticated()) { + user_error('Operation disallowed prior to login()'); + return false; + } + + if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { + user_error('Unable to initiate an interactive shell session'); + return false; + } + + $channel = $this->_get_interactive_channel(); + + if ($mode == self::READ_NEXT) { + return $this->_get_channel_packet($channel); + } + + $match = $expect; + while (true) { + if ($mode == self::READ_REGEX) { + preg_match($expect, substr($this->interactiveBuffer, -1024), $matches); + $match = isset($matches[0]) ? $matches[0] : ''; + } + $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false; + if ($pos !== false) { + return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match)); + } + $response = $this->_get_channel_packet($channel); + if (is_bool($response)) { + $this->in_request_pty_exec = false; + return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false; + } + + $this->interactiveBuffer.= $response; + } + } + + /** + * Inputs a command into an interactive shell. + * + * @see self::read() + * @param string $cmd + * @return bool + * @access public + */ + function write($cmd) + { + if (!$this->isAuthenticated()) { + user_error('Operation disallowed prior to login()'); + return false; + } + + if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { + user_error('Unable to initiate an interactive shell session'); + return false; + } + + return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd); + } + + /** + * Start a subsystem. + * + * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept + * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened. + * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and + * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented + * if there's sufficient demand for such a feature. + * + * @see self::stopSubsystem() + * @param string $subsystem + * @return bool + * @access public + */ + function startSubsystem($subsystem) + { + $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size; + + $packet = pack( + 'CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, + strlen('session'), + 'session', + self::CHANNEL_SUBSYSTEM, + $this->window_size, + 0x4000 + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM); + if ($response === false) { + return false; + } + + $packet = pack( + 'CNNa*CNa*', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL_SUBSYSTEM], + strlen('subsystem'), + 'subsystem', + 1, + strlen($subsystem), + $subsystem + ); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM); + + if ($response === false) { + return false; + } + + $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA; + + $this->bitmap |= self::MASK_SHELL; + $this->in_subsystem = true; + + return true; + } + + /** + * Stops a subsystem. + * + * @see self::startSubsystem() + * @return bool + * @access public + */ + function stopSubsystem() + { + $this->in_subsystem = false; + $this->_close_channel(self::CHANNEL_SUBSYSTEM); + return true; + } + + /** + * Closes a channel + * + * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call + * + * @access public + */ + function reset() + { + $this->_close_channel($this->_get_interactive_channel()); + } + + /** + * Is timeout? + * + * Did exec() or read() return because they timed out or because they encountered the end? + * + * @access public + */ + function isTimeout() + { + return $this->is_timeout; + } + + /** + * Disconnect + * + * @access public + */ + function disconnect() + { + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) { + fclose($this->realtime_log_file); + } + } + + /** + * Destructor. + * + * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call + * disconnect(). + * + * @access public + */ + function __destruct() + { + $this->disconnect(); + } + + /** + * Is the connection still active? + * + * @return bool + * @access public + */ + function isConnected() + { + return (bool) ($this->bitmap & self::MASK_CONNECTED); + } + + /** + * Have you successfully been logged in? + * + * @return bool + * @access public + */ + function isAuthenticated() + { + return (bool) ($this->bitmap & self::MASK_LOGIN); + } + + /** + * Pings a server connection, or tries to reconnect if the connection has gone down + * + * Inspired by http://php.net/manual/en/mysqli.ping.php + * + * @return bool + * @access public + */ + function ping() + { + if (!$this->isAuthenticated()) { + if (!empty($this->auth)) { + return $this->_reconnect(); + } + return false; + } + + $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size; + $packet_size = 0x4000; + $packet = pack( + 'CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, + strlen('session'), + 'session', + self::CHANNEL_KEEP_ALIVE, + $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE], + $packet_size + ); + + if (!@$this->_send_binary_packet($packet)) { + return $this->_reconnect(); + } + + $this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = @$this->_get_channel_packet(self::CHANNEL_KEEP_ALIVE); + if ($response !== false) { + $this->_close_channel(self::CHANNEL_KEEP_ALIVE); + return true; + } + + return $this->_reconnect(); + } + + /** + * In situ reconnect method + * + * @return boolean + * @access private + */ + function _reconnect() + { + $this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST); + $this->retry_connect = true; + if (!$this->_connect()) { + return false; + } + foreach ($this->auth as $auth) { + $result = call_user_func_array(array(&$this, 'login'), $auth); + } + return $result; + } + + /** + * Resets a connection for re-use + * + * @param int $reason + * @access private + */ + function _reset_connection($reason) + { + $this->_disconnect($reason); + $this->decrypt = $this->encrypt = false; + $this->decrypt_block_size = $this->encrypt_block_size = 8; + $this->hmac_check = $this->hmac_create = false; + $this->hmac_size = false; + $this->session_id = false; + $this->retry_connect = true; + $this->get_seq_no = $this->send_seq_no = 0; + } + + /** + * Gets Binary Packets + * + * See '6. Binary Packet Protocol' of rfc4253 for more info. + * + * @see self::_send_binary_packet() + * @return string + * @access private + */ + function _get_binary_packet($skip_channel_filter = false) + { + if ($skip_channel_filter) { + $read = array($this->fsock); + $write = $except = null; + + if (!$this->curTimeout) { + if ($this->keepAlive <= 0) { + @stream_select($read, $write, $except, null); + } else { + if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); + return $this->_get_binary_packet(true); + } + } + } else { + if ($this->curTimeout < 0) { + $this->is_timeout = true; + return true; + } + + $read = array($this->fsock); + $write = $except = null; + + $start = microtime(true); + + if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { + if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); + $elapsed = microtime(true) - $start; + $this->curTimeout-= $elapsed; + return $this->_get_binary_packet(true); + } + $elapsed = microtime(true) - $start; + $this->curTimeout-= $elapsed; + } + + $sec = (int)floor($this->curTimeout); + $usec = (int)(1000000 * ($this->curTimeout - $sec)); + + // on windows this returns a "Warning: Invalid CRT parameters detected" error + if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { + $this->is_timeout = true; + return true; + } + $elapsed = microtime(true) - $start; + $this->curTimeout-= $elapsed; + } + } + + if (!is_resource($this->fsock) || feof($this->fsock)) { + $this->bitmap = 0; + $str = 'Connection closed (by server) prematurely'; + if (isset($elapsed)) { + $str.= ' ' . $elapsed . 's'; + } + user_error($str); + return false; + } + + $start = microtime(true); + $raw = stream_get_contents($this->fsock, $this->decrypt_block_size); + + if (!strlen($raw)) { + user_error('No data received from server'); + return false; + } + + if ($this->decrypt !== false) { + $raw = $this->decrypt->decrypt($raw); + } + if ($raw === false) { + user_error('Unable to decrypt content'); + return false; + } + + if (strlen($raw) < 5) { + return false; + } + extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5))); + + $remaining_length = $packet_length + 4 - $this->decrypt_block_size; + + // quoting , + // "implementations SHOULD check that the packet length is reasonable" + // PuTTY uses 0x9000 as the actual max packet size and so to shall we + if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) { + if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decrypt->name) && !($this->bitmap & SSH2::MASK_LOGIN)) { + $this->bad_key_size_fix = true; + $this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + return false; + } + user_error('Invalid size'); + return false; + } + + $buffer = ''; + while ($remaining_length > 0) { + $temp = stream_get_contents($this->fsock, $remaining_length); + if ($temp === false || feof($this->fsock)) { + $this->bitmap = 0; + user_error('Error reading from socket'); + return false; + } + $buffer.= $temp; + $remaining_length-= strlen($temp); + } + + $stop = microtime(true); + if (strlen($buffer)) { + $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer; + } + + $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1); + $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty + + if ($this->hmac_check !== false) { + $hmac = stream_get_contents($this->fsock, $this->hmac_size); + if ($hmac === false || strlen($hmac) != $this->hmac_size) { + $this->bitmap = 0; + user_error('Error reading socket'); + return false; + } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) { + user_error('Invalid HMAC'); + return false; + } + } + + switch ($this->decompress) { + case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH: + if (!$this->isAuthenticated()) { + break; + } + case self::NET_SSH2_COMPRESSION_ZLIB: + if ($this->regenerate_decompression_context) { + $this->regenerate_decompression_context = false; + + $cmf = ord($payload[0]); + $cm = $cmf & 0x0F; + if ($cm != 8) { // deflate + user_error("Only CM = 8 ('deflate') is supported ($cm)"); + } + $cinfo = ($cmf & 0xF0) >> 4; + if ($cinfo > 7) { + user_error("CINFO above 7 is not allowed ($cinfo)"); + } + $windowSize = 1 << ($cinfo + 8); + + $flg = ord($payload[1]); + //$fcheck = $flg && 0x0F; + if ((($cmf << 8) | $flg) % 31) { + user_error('fcheck failed'); + } + $fdict = boolval($flg & 0x20); + $flevel = ($flg & 0xC0) >> 6; + + $this->decompress_context = inflate_init(ZLIB_ENCODING_RAW, array('window' => $cinfo + 8)); + $payload = substr($payload, 2); + } + if ($this->decompress_context) { + $payload = inflate_add($this->decompress_context, $payload, ZLIB_PARTIAL_FLUSH); + } + } + + $this->get_seq_no++; + + if (defined('NET_SSH2_LOGGING')) { + $current = microtime(true); + $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')'; + $message_number = '<- ' . $message_number . + ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; + $this->_append_log($message_number, $payload); + $this->last_packet = $current; + } + + return $this->_filter($payload, $skip_channel_filter); + } + + /** + * Filter Binary Packets + * + * Because some binary packets need to be ignored... + * + * @see self::_get_binary_packet() + * @return string + * @access private + */ + function _filter($payload, $skip_channel_filter) + { + switch (ord($payload[0])) { + case NET_SSH2_MSG_DISCONNECT: + $this->_string_shift($payload, 1); + if (strlen($payload) < 8) { + return false; + } + extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8))); + $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length); + $this->bitmap = 0; + return false; + case NET_SSH2_MSG_IGNORE: + $payload = $this->_get_binary_packet($skip_channel_filter); + break; + case NET_SSH2_MSG_DEBUG: + $this->_string_shift($payload, 2); + if (strlen($payload) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($payload, 4))); + $this->errors[] = 'SSH_MSG_DEBUG: ' . $this->_string_shift($payload, $length); + $payload = $this->_get_binary_packet($skip_channel_filter); + break; + case NET_SSH2_MSG_UNIMPLEMENTED: + return false; + case NET_SSH2_MSG_KEXINIT: + if ($this->session_id !== false) { + $this->send_kex_first = false; + if (!$this->_key_exchange($payload)) { + $this->bitmap = 0; + return false; + } + $payload = $this->_get_binary_packet($skip_channel_filter); + } + } + + // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in + if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { + $this->_string_shift($payload, 1); + if (strlen($payload) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($payload, 4))); + $this->banner_message = $this->_string_shift($payload, $length); + $payload = $this->_get_binary_packet(); + } + + // only called when we've already logged in + if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) { + if (is_bool($payload)) { + return $payload; + } + + switch (ord($payload[0])) { + case NET_SSH2_MSG_CHANNEL_REQUEST: + if (strlen($payload) == 31) { + extract(unpack('cpacket_type/Nchannel/Nlength', $payload)); + if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) { + if (ord(substr($payload, 9 + $length))) { // want reply + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel])); + } + $payload = $this->_get_binary_packet($skip_channel_filter); + } + } + break; + case NET_SSH2_MSG_CHANNEL_DATA: + case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: + case NET_SSH2_MSG_CHANNEL_CLOSE: + case NET_SSH2_MSG_CHANNEL_EOF: + if (!$skip_channel_filter && !empty($this->server_channels)) { + $this->binary_packet_buffer = $payload; + $this->_get_channel_packet(true); + $payload = $this->_get_binary_packet(); + } + break; + case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4 + if (strlen($payload) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($payload, 4))); + $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . $this->_string_shift($payload, $length); + + if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) { + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + + $payload = $this->_get_binary_packet($skip_channel_filter); + break; + case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1 + $this->_string_shift($payload, 1); + if (strlen($payload) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($payload, 4))); + $data = $this->_string_shift($payload, $length); + if (strlen($payload) < 4) { + return false; + } + extract(unpack('Nserver_channel', $this->_string_shift($payload, 4))); + switch ($data) { + case 'auth-agent': + case 'auth-agent@openssh.com': + if (isset($this->agent)) { + $new_channel = self::CHANNEL_AGENT_FORWARD; + + if (strlen($payload) < 8) { + return false; + } + extract(unpack('Nremote_window_size', $this->_string_shift($payload, 4))); + extract(unpack('Nremote_maximum_packet_size', $this->_string_shift($payload, 4))); + + $this->packet_size_client_to_server[$new_channel] = $remote_window_size; + $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size; + $this->window_size_client_to_server[$new_channel] = $this->window_size; + + $packet_size = 0x4000; + + $packet = pack( + 'CN4', + NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, + $server_channel, + $new_channel, + $packet_size, + $packet_size + ); + + $this->server_channels[$new_channel] = $server_channel; + $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION; + if (!$this->_send_binary_packet($packet)) { + return false; + } + } + break; + default: + $packet = pack( + 'CN3a*Na*', + NET_SSH2_MSG_REQUEST_FAILURE, + $server_channel, + NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, + 0, + '', + 0, + '' + ); + + if (!$this->_send_binary_packet($packet)) { + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + } + $payload = $this->_get_binary_packet($skip_channel_filter); + break; + case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST: + $this->_string_shift($payload, 1); + if (strlen($payload) < 8) { + return false; + } + extract(unpack('Nchannel', $this->_string_shift($payload, 4))); + extract(unpack('Nwindow_size', $this->_string_shift($payload, 4))); + $this->window_size_client_to_server[$channel]+= $window_size; + + $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet($skip_channel_filter); + } + } + + return $payload; + } + + /** + * Enable Quiet Mode + * + * Suppress stderr from output + * + * @access public + */ + function enableQuietMode() + { + $this->quiet_mode = true; + } + + /** + * Disable Quiet Mode + * + * Show stderr in output + * + * @access public + */ + function disableQuietMode() + { + $this->quiet_mode = false; + } + + /** + * Returns whether Quiet Mode is enabled or not + * + * @see self::enableQuietMode() + * @see self::disableQuietMode() + * @access public + * @return bool + */ + function isQuietModeEnabled() + { + return $this->quiet_mode; + } + + /** + * Enable request-pty when using exec() + * + * @access public + */ + function enablePTY() + { + $this->request_pty = true; + } + + /** + * Disable request-pty when using exec() + * + * @access public + */ + function disablePTY() + { + if ($this->in_request_pty_exec) { + $this->_close_channel(self::CHANNEL_EXEC); + $this->in_request_pty_exec = false; + } + $this->request_pty = false; + } + + /** + * Returns whether request-pty is enabled or not + * + * @see self::enablePTY() + * @see self::disablePTY() + * @access public + * @return bool + */ + function isPTYEnabled() + { + return $this->request_pty; + } + + /** + * Gets channel data + * + * Returns the data as a string if it's available and false if not. + * + * @param int $client_channel + * @param bool $skip_extended + * @return mixed|bool + * @access private + */ + function _get_channel_packet($client_channel, $skip_extended = false) + { + if (!empty($this->channel_buffers[$client_channel])) { + switch ($this->channel_status[$client_channel]) { + case NET_SSH2_MSG_CHANNEL_REQUEST: + foreach ($this->channel_buffers[$client_channel] as $i => $packet) { + switch (ord($packet[0])) { + case NET_SSH2_MSG_CHANNEL_SUCCESS: + case NET_SSH2_MSG_CHANNEL_FAILURE: + unset($this->channel_buffers[$client_channel][$i]); + return substr($packet, 1); + } + } + break; + default: + return substr(array_shift($this->channel_buffers[$client_channel]), 1); + } + } + + while (true) { + if ($this->binary_packet_buffer !== false) { + $response = $this->binary_packet_buffer; + $this->binary_packet_buffer = false; + } else { + $response = $this->_get_binary_packet(true); + if ($response === true && $this->is_timeout) { + if ($client_channel == self::CHANNEL_EXEC && !$this->request_pty) { + $this->_close_channel($client_channel); + } + return true; + } + if ($response === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + } + + if ($client_channel == -1 && $response === true) { + return true; + } + if (!strlen($response)) { + return false; + } + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + if (strlen($response) < 4) { + return false; + } + if ($type == NET_SSH2_MSG_CHANNEL_OPEN) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + } else { + extract(unpack('Nchannel', $this->_string_shift($response, 4))); + } + + // will not be setup yet on incoming channel open request + if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) { + $this->window_size_server_to_client[$channel]-= strlen($response); + + // resize the window, if appropriate + if ($this->window_size_server_to_client[$channel] < 0) { + // PuTTY does something more analogous to the following: + //if ($this->window_size_server_to_client[$channel] < 0x3FFFFFFF) { + $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize); + if (!$this->_send_binary_packet($packet)) { + return false; + } + $this->window_size_server_to_client[$channel]+= $this->window_resize; + } + + switch ($type) { + case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: + /* + if ($client_channel == self::CHANNEL_EXEC) { + $this->_send_channel_packet($client_channel, chr(0)); + } + */ + // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR + if (strlen($response) < 8) { + return false; + } + extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8))); + $data = $this->_string_shift($response, $length); + $this->stdErrorLog.= $data; + if ($skip_extended || $this->quiet_mode) { + continue 2; + } + if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) { + return $data; + } + $this->channel_buffers[$channel][] = chr($type) . $data; + + continue 2; + case NET_SSH2_MSG_CHANNEL_REQUEST: + if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) { + continue 2; + } + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $value = $this->_string_shift($response, $length); + switch ($value) { + case 'exit-signal': + $this->_string_shift($response, 1); + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length); + $this->_string_shift($response, 1); + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + if ($length) { + $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length); + } + + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); + + $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; + + continue 3; + case 'exit-status': + if (strlen($response) < 5) { + return false; + } + extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5))); + $this->exit_status = $exit_status; + + // "The client MAY ignore these messages." + // -- http://tools.ietf.org/html/rfc4254#section-6.10 + + continue 3; + default: + // "Some systems may not implement signals, in which case they SHOULD ignore this message." + // -- http://tools.ietf.org/html/rfc4254#section-6.9 + continue 3; + } + } + + switch ($this->channel_status[$channel]) { + case NET_SSH2_MSG_CHANNEL_OPEN: + switch ($type) { + case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nserver_channel', $this->_string_shift($response, 4))); + $this->server_channels[$channel] = $server_channel; + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nwindow_size', $this->_string_shift($response, 4))); + if ($window_size < 0) { + $window_size&= 0x7FFFFFFF; + $window_size+= 0x80000000; + } + $this->window_size_client_to_server[$channel] = $window_size; + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4)); + $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server']; + $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended); + $this->_on_channel_open(); + return $result; + case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: + user_error('Unable to open channel'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + default: + if ($client_channel == $channel) { + user_error('Unexpected response to open request'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + return $this->_get_channel_packet($client_channel, $skip_extended); + } + break; + case NET_SSH2_MSG_CHANNEL_REQUEST: + switch ($type) { + case NET_SSH2_MSG_CHANNEL_SUCCESS: + return true; + case NET_SSH2_MSG_CHANNEL_FAILURE: + return false; + case NET_SSH2_MSG_CHANNEL_DATA: + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $data = $this->_string_shift($response, $length); + $this->channel_buffers[$channel][] = chr($type) . $data; + return $this->_get_channel_packet($client_channel, $skip_extended); + default: + user_error('Unable to fulfill channel request'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + case NET_SSH2_MSG_CHANNEL_CLOSE: + return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended); + } + } + + // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA + + switch ($type) { + case NET_SSH2_MSG_CHANNEL_DATA: + /* + if ($channel == self::CHANNEL_EXEC) { + // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server + // this actually seems to make things twice as fast. more to the point, the message right after + // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise. + // in OpenSSH it slows things down but only by a couple thousandths of a second. + $this->_send_channel_packet($channel, chr(0)); + } + */ + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $data = $this->_string_shift($response, $length); + + if ($channel == self::CHANNEL_AGENT_FORWARD) { + $agent_response = $this->agent->_forward_data($data); + if (!is_bool($agent_response)) { + $this->_send_channel_packet($channel, $agent_response); + } + break; + } + + if ($client_channel == $channel) { + return $data; + } + $this->channel_buffers[$channel][] = chr($type) . $data; + break; + case NET_SSH2_MSG_CHANNEL_CLOSE: + $this->curTimeout = 5; + + if ($this->bitmap & self::MASK_SHELL) { + $this->bitmap&= ~self::MASK_SHELL; + } + if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); + } + + $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE; + if ($client_channel == $channel) { + return true; + } + case NET_SSH2_MSG_CHANNEL_EOF: + break; + default: + user_error("Error reading channel data ($type)"); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + } + } + + /** + * Sends Binary Packets + * + * See '6. Binary Packet Protocol' of rfc4253 for more info. + * + * @param string $data + * @param string $logged + * @see self::_get_binary_packet() + * @return bool + * @access private + */ + function _send_binary_packet($data, $logged = null) + { + if (!is_resource($this->fsock) || feof($this->fsock)) { + $this->bitmap = 0; + user_error('Connection closed prematurely'); + return false; + } + + if (!isset($logged)) { + $logged = $data; + } + + switch ($this->compress) { + case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH: + if (!$this->isAuthenticated()) { + break; + } + case self::NET_SSH2_COMPRESSION_ZLIB: + if (!$this->regenerate_compression_context) { + $header = ''; + } else { + $this->regenerate_compression_context = false; + $this->compress_context = deflate_init(ZLIB_ENCODING_RAW, array('window' => 15)); + $header = "\x78\x9C"; + } + if ($this->compress_context) { + $data = $header . deflate_add($this->compress_context, $data, ZLIB_PARTIAL_FLUSH); + } + } + + // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9 + $packet_length = strlen($data) + 9; + // round up to the nearest $this->encrypt_block_size + $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size; + // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length + $padding_length = $packet_length - strlen($data) - 5; + $padding = Random::string($padding_length); + + // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself + $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding); + + $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : ''; + $this->send_seq_no++; + + if ($this->encrypt !== false) { + $packet = $this->encrypt->encrypt($packet); + } + + $packet.= $hmac; + + $start = microtime(true); + $result = strlen($packet) == @fputs($this->fsock, $packet); + $stop = microtime(true); + + if (defined('NET_SSH2_LOGGING')) { + $current = microtime(true); + $message_number = isset($this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')'; + $message_number = '-> ' . $message_number . + ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; + $this->_append_log($message_number, $logged); + $this->last_packet = $current; + } + + return $result; + } + + /** + * Logs data packets + * + * Makes sure that only the last 1MB worth of packets will be logged + * + * @param string $message_number + * @param string $message + * @access private + */ + function _append_log($message_number, $message) + { + // remove the byte identifying the message type from all but the first two messages (ie. the identification strings) + if (strlen($message_number) > 2) { + $this->_string_shift($message); + } + + switch (NET_SSH2_LOGGING) { + // useful for benchmarks + case self::LOG_SIMPLE: + $this->message_number_log[] = $message_number; + break; + // the most useful log for SSH2 + case self::LOG_COMPLEX: + $this->message_number_log[] = $message_number; + $this->log_size+= strlen($message); + $this->message_log[] = $message; + while ($this->log_size > self::LOG_MAX_SIZE) { + $this->log_size-= strlen(array_shift($this->message_log)); + array_shift($this->message_number_log); + } + break; + // dump the output out realtime; packets may be interspersed with non packets, + // passwords won't be filtered out and select other packets may not be correctly + // identified + case self::LOG_REALTIME: + switch (PHP_SAPI) { + case 'cli': + $start = $stop = "\r\n"; + break; + default: + $start = '
      ';
      +                        $stop = '
      '; + } + echo $start . $this->_format_log(array($message), array($message_number)) . $stop; + @flush(); + @ob_flush(); + break; + // basically the same thing as self::LOG_REALTIME with the caveat that self::LOG_REALTIME_FILE + // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE. + // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily + // at the beginning of the file + case self::LOG_REALTIME_FILE: + if (!isset($this->realtime_log_file)) { + // PHP doesn't seem to like using constants in fopen() + $filename = self::LOG_REALTIME_FILENAME; + $fp = fopen($filename, 'w'); + $this->realtime_log_file = $fp; + } + if (!is_resource($this->realtime_log_file)) { + break; + } + $entry = $this->_format_log(array($message), array($message_number)); + if ($this->realtime_log_wrap) { + $temp = "<<< START >>>\r\n"; + $entry.= $temp; + fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp)); + } + $this->realtime_log_size+= strlen($entry); + if ($this->realtime_log_size > self::LOG_MAX_SIZE) { + fseek($this->realtime_log_file, 0); + $this->realtime_log_size = strlen($entry); + $this->realtime_log_wrap = true; + } + fputs($this->realtime_log_file, $entry); + } + } + + /** + * Sends channel data + * + * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate + * + * @param int $client_channel + * @param string $data + * @return bool + * @access private + */ + function _send_channel_packet($client_channel, $data) + { + while (strlen($data)) { + if (!$this->window_size_client_to_server[$client_channel]) { + $this->bitmap^= self::MASK_WINDOW_ADJUST; + // using an invalid channel will let the buffers be built up for the valid channels + $this->_get_channel_packet(-1); + $this->bitmap^= self::MASK_WINDOW_ADJUST; + } + + /* The maximum amount of data allowed is determined by the maximum + packet size for the channel, and the current window size, whichever + is smaller. + -- http://tools.ietf.org/html/rfc4254#section-5.2 */ + $max_size = min( + $this->packet_size_client_to_server[$client_channel], + $this->window_size_client_to_server[$client_channel] + ); + + $temp = $this->_string_shift($data, $max_size); + $packet = pack( + 'CN2a*', + NET_SSH2_MSG_CHANNEL_DATA, + $this->server_channels[$client_channel], + strlen($temp), + $temp + ); + $this->window_size_client_to_server[$client_channel]-= strlen($temp); + if (!$this->_send_binary_packet($packet)) { + return false; + } + } + + return true; + } + + /** + * Closes and flushes a channel + * + * \phpseclib\Net\SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server + * and for SFTP channels are presumably closed when the client disconnects. This functions is intended + * for SCP more than anything. + * + * @param int $client_channel + * @param bool $want_reply + * @return bool + * @access private + */ + function _close_channel($client_channel, $want_reply = false) + { + // see http://tools.ietf.org/html/rfc4254#section-5.3 + + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); + + if (!$want_reply) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); + } + + $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE; + + $this->curTimeout = 5; + + while (!is_bool($this->_get_channel_packet($client_channel))) { + } + + if ($this->is_timeout) { + $this->disconnect(); + } + + if ($want_reply) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); + } + + if ($this->bitmap & self::MASK_SHELL) { + $this->bitmap&= ~self::MASK_SHELL; + } + } + + /** + * Disconnect + * + * @param int $reason + * @return bool + * @access private + */ + function _disconnect($reason) + { + if ($this->bitmap & self::MASK_CONNECTED) { + $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, ''); + $this->_send_binary_packet($data); + } + + $this->bitmap = 0; + if (is_resource($this->fsock) && get_resource_type($this->fsock) == 'stream') { + fclose($this->fsock); + } + + return false; + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * Define Array + * + * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of + * named constants from it, using the value as the name of the constant and the index as the value of the constant. + * If any of the constants that would be defined already exists, none of the constants will be defined. + * + * @access private + */ + function _define_array() + { + $args = func_get_args(); + foreach ($args as $arg) { + foreach ($arg as $key => $value) { + if (!defined($value)) { + define($value, $key); + } else { + break 2; + } + } + } + } + + /** + * Returns a log of the packets that have been sent and received. + * + * Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING') + * + * @access public + * @return array|false|string + */ + function getLog() + { + if (!defined('NET_SSH2_LOGGING')) { + return false; + } + + switch (NET_SSH2_LOGGING) { + case self::LOG_SIMPLE: + return $this->message_number_log; + case self::LOG_COMPLEX: + $log = $this->_format_log($this->message_log, $this->message_number_log); + return PHP_SAPI == 'cli' ? $log : '
      ' . $log . '
      '; + default: + return false; + } + } + + /** + * Formats a log for printing + * + * @param array $message_log + * @param array $message_number_log + * @access private + * @return string + */ + function _format_log($message_log, $message_number_log) + { + $output = ''; + for ($i = 0; $i < count($message_log); $i++) { + $output.= $message_number_log[$i] . "\r\n"; + $current_log = $message_log[$i]; + $j = 0; + do { + if (strlen($current_log)) { + $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 '; + } + $fragment = $this->_string_shift($current_log, $this->log_short_width); + $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary)); + // replace non ASCII printable characters with dots + // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters + // also replace < with a . since < messes up the output on web browsers + $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment); + $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n"; + $j++; + } while (strlen($current_log)); + $output.= "\r\n"; + } + + return $output; + } + + /** + * Helper function for _format_log + * + * For use with preg_replace_callback() + * + * @param array $matches + * @access private + * @return string + */ + function _format_log_helper($matches) + { + return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT); + } + + /** + * Helper function for agent->_on_channel_open() + * + * Used when channels are created to inform agent + * of said channel opening. Must be called after + * channel open confirmation received + * + * @access private + */ + function _on_channel_open() + { + if (isset($this->agent)) { + $this->agent->_on_channel_open($this); + } + } + + /** + * Returns the first value of the intersection of two arrays or false if + * the intersection is empty. The order is defined by the first parameter. + * + * @param array $array1 + * @param array $array2 + * @return mixed False if intersection is empty, else intersected value. + * @access private + */ + function _array_intersect_first($array1, $array2) + { + foreach ($array1 as $value) { + if (in_array($value, $array2)) { + return $value; + } + } + return false; + } + + /** + * Returns all errors + * + * @return string[] + * @access public + */ + function getErrors() + { + return $this->errors; + } + + /** + * Returns the last error + * + * @return string + * @access public + */ + function getLastError() + { + $count = count($this->errors); + + if ($count > 0) { + return $this->errors[$count - 1]; + } + } + + /** + * Return the server identification. + * + * @return string + * @access public + */ + function getServerIdentification() + { + $this->_connect(); + + return $this->server_identifier; + } + + /** + * Return a list of the key exchange algorithms the server supports. + * + * @return array + * @access public + */ + function getKexAlgorithms() + { + $this->_connect(); + + return $this->kex_algorithms; + } + + /** + * Return a list of the host key (public key) algorithms the server supports. + * + * @return array + * @access public + */ + function getServerHostKeyAlgorithms() + { + $this->_connect(); + + return $this->server_host_key_algorithms; + } + + /** + * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client. + * + * @return array + * @access public + */ + function getEncryptionAlgorithmsClient2Server() + { + $this->_connect(); + + return $this->encryption_algorithms_client_to_server; + } + + /** + * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client. + * + * @return array + * @access public + */ + function getEncryptionAlgorithmsServer2Client() + { + $this->_connect(); + + return $this->encryption_algorithms_server_to_client; + } + + /** + * Return a list of the MAC algorithms the server supports, when receiving stuff from the client. + * + * @return array + * @access public + */ + function getMACAlgorithmsClient2Server() + { + $this->_connect(); + + return $this->mac_algorithms_client_to_server; + } + + /** + * Return a list of the MAC algorithms the server supports, when sending stuff to the client. + * + * @return array + * @access public + */ + function getMACAlgorithmsServer2Client() + { + $this->_connect(); + + return $this->mac_algorithms_server_to_client; + } + + /** + * Return a list of the compression algorithms the server supports, when receiving stuff from the client. + * + * @return array + * @access public + */ + function getCompressionAlgorithmsClient2Server() + { + $this->_connect(); + + return $this->compression_algorithms_client_to_server; + } + + /** + * Return a list of the compression algorithms the server supports, when sending stuff to the client. + * + * @return array + * @access public + */ + function getCompressionAlgorithmsServer2Client() + { + $this->_connect(); + + return $this->compression_algorithms_server_to_client; + } + + /** + * Return a list of the languages the server supports, when sending stuff to the client. + * + * @return array + * @access public + */ + function getLanguagesServer2Client() + { + $this->_connect(); + + return $this->languages_server_to_client; + } + + /** + * Return a list of the languages the server supports, when receiving stuff from the client. + * + * @return array + * @access public + */ + function getLanguagesClient2Server() + { + $this->_connect(); + + return $this->languages_client_to_server; + } + + /** + * Returns a list of algorithms the server supports + * + * @return array + * @access public + */ + function getServerAlgorithms() + { + $this->_connect(); + + return array( + 'kex' => $this->kex_algorithms, + 'hostkey' => $this->server_host_key_algorithms, + 'client_to_server' => array( + 'crypt' => $this->encryption_algorithms_client_to_server, + 'mac' => $this->mac_algorithms_client_to_server, + 'comp' => $this->compression_algorithms_client_to_server, + 'lang' => $this->languages_client_to_server + ), + 'server_to_client' => array( + 'crypt' => $this->encryption_algorithms_server_to_client, + 'mac' => $this->mac_algorithms_server_to_client, + 'comp' => $this->compression_algorithms_server_to_client, + 'lang' => $this->languages_server_to_client + ) + ); + } + + /** + * Returns a list of KEX algorithms that phpseclib supports + * + * @return array + * @access public + */ + function getSupportedKEXAlgorithms() + { + $kex_algorithms = array( + // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using + // Curve25519. See doc/curve25519-sha256@libssh.org.txt in the + // libssh repository for more information. + 'curve25519-sha256@libssh.org', + + 'diffie-hellman-group-exchange-sha256',// RFC 4419 + 'diffie-hellman-group-exchange-sha1', // RFC 4419 + + // Diffie-Hellman Key Agreement (DH) using integer modulo prime + // groups. + 'diffie-hellman-group14-sha1', // REQUIRED + 'diffie-hellman-group1-sha1', // REQUIRED + ); + + if (!function_exists('sodium_crypto_box_publickey_from_secretkey')) { + $kex_algorithms = array_diff( + $kex_algorithms, + array('curve25519-sha256@libssh.org') + ); + } + + return $kex_algorithms; + } + + /** + * Returns a list of host key algorithms that phpseclib supports + * + * @return array + * @access public + */ + function getSupportedHostKeyAlgorithms() + { + return array( + 'rsa-sha2-256', // RFC 8332 + 'rsa-sha2-512', // RFC 8332 + 'ssh-rsa', // RECOMMENDED sign Raw RSA Key + 'ssh-dss' // REQUIRED sign Raw DSS Key + ); + } + + /** + * Returns a list of symmetric key algorithms that phpseclib supports + * + * @return array + * @access public + */ + function getSupportedEncryptionAlgorithms() + { + $algos = array( + // from : + 'arcfour256', + 'arcfour128', + + //'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key + + // CTR modes from : + 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key + 'aes192-ctr', // RECOMMENDED AES with 192-bit key + 'aes256-ctr', // RECOMMENDED AES with 256-bit key + + 'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key + 'twofish192-ctr', // OPTIONAL Twofish with 192-bit key + 'twofish256-ctr', // OPTIONAL Twofish with 256-bit key + + 'aes128-cbc', // RECOMMENDED AES with a 128-bit key + 'aes192-cbc', // OPTIONAL AES with a 192-bit key + 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key + + 'twofish128-cbc', // OPTIONAL Twofish with a 128-bit key + 'twofish192-cbc', // OPTIONAL Twofish with a 192-bit key + 'twofish256-cbc', + 'twofish-cbc', // OPTIONAL alias for "twofish256-cbc" + // (this is being retained for historical reasons) + + 'blowfish-ctr', // OPTIONAL Blowfish in SDCTR mode + + 'blowfish-cbc', // OPTIONAL Blowfish in CBC mode + + '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode + + '3des-cbc', // REQUIRED three-key 3DES in CBC mode + + //'none' // OPTIONAL no encryption; NOT RECOMMENDED + ); + + if ($this->crypto_engine) { + $engines = array($this->crypto_engine); + } else { + $engines = array( + Base::ENGINE_OPENSSL, + Base::ENGINE_MCRYPT, + Base::ENGINE_INTERNAL + ); + } + + $ciphers = array(); + foreach ($engines as $engine) { + foreach ($algos as $algo) { + $obj = $this->_encryption_algorithm_to_crypt_instance($algo); + if ($obj instanceof Rijndael) { + $obj->setKeyLength(preg_replace('#[^\d]#', '', $algo)); + } + switch ($algo) { + case 'arcfour128': + case 'arcfour256': + if ($engine != Base::ENGINE_INTERNAL) { + continue 2; + } + } + if ($obj->isValidEngine($engine)) { + $algos = array_diff($algos, array($algo)); + $ciphers[] = $algo; + } + } + } + + return $ciphers; + } + + /** + * Returns a list of MAC algorithms that phpseclib supports + * + * @return array + * @access public + */ + function getSupportedMACAlgorithms() + { + return array( + // from : + 'hmac-sha2-256',// RECOMMENDED HMAC-SHA256 (digest length = key length = 32) + + 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20) + 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20) + 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16) + 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16) + //'none' // OPTIONAL no MAC; NOT RECOMMENDED + ); + } + + /** + * Returns a list of compression algorithms that phpseclib supports + * + * @return array + * @access public + */ + function getSupportedCompressionAlgorithms() + { + $algos = array('none'); // REQUIRED no compression + if (function_exists('deflate_init')) { + $algos[] = 'zlib@openssh.com'; // https://datatracker.ietf.org/doc/html/draft-miller-secsh-compression-delayed + $algos[] = 'zlib'; + } + return $algos; + } + + /** + * Return list of negotiated algorithms + * + * Uses the same format as https://www.php.net/ssh2-methods-negotiated + * + * @return array + * @access public + */ + function getAlgorithmsNegotiated() + { + $this->_connect(); + + $compression_map = array( + self::NET_SSH2_COMPRESSION_NONE => 'none', + self::NET_SSH2_COMPRESSION_ZLIB => 'zlib', + self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH => 'zlib@openssh.com' + ); + + return array( + 'kex' => $this->kex_algorithm, + 'hostkey' => $this->signature_format, + 'client_to_server' => array( + 'crypt' => $this->encrypt->name, + 'mac' => $this->hmac_create->name, + 'comp' => $compression_map[$this->compress], + ), + 'server_to_client' => array( + 'crypt' => $this->decrypt->name, + 'mac' => $this->hmac_check->name, + 'comp' => $compression_map[$this->decompress], + ) + ); + } + + /** + * Accepts an associative array with up to four parameters as described at + * + * + * @param array $methods + * @access public + */ + function setPreferredAlgorithms($methods) + { + $preferred = $methods; + + if (isset($preferred['kex'])) { + $preferred['kex'] = array_intersect( + $preferred['kex'], + $this->getSupportedKEXAlgorithms() + ); + } + + if (isset($preferred['hostkey'])) { + $preferred['hostkey'] = array_intersect( + $preferred['hostkey'], + $this->getSupportedHostKeyAlgorithms() + ); + } + + $keys = array('client_to_server', 'server_to_client'); + foreach ($keys as $key) { + if (isset($preferred[$key])) { + $a = &$preferred[$key]; + if (isset($a['crypt'])) { + $a['crypt'] = array_intersect( + $a['crypt'], + $this->getSupportedEncryptionAlgorithms() + ); + } + if (isset($a['comp'])) { + $a['comp'] = array_intersect( + $a['comp'], + $this->getSupportedCompressionAlgorithms() + ); + } + if (isset($a['mac'])) { + $a['mac'] = array_intersect( + $a['mac'], + $this->getSupportedMACAlgorithms() + ); + } + } + } + + $keys = array( + 'kex', + 'hostkey', + 'client_to_server/crypt', + 'client_to_server/comp', + 'client_to_server/mac', + 'server_to_client/crypt', + 'server_to_client/comp', + 'server_to_client/mac', + ); + foreach ($keys as $key) { + $p = $preferred; + $m = $methods; + + $subkeys = explode('/', $key); + foreach ($subkeys as $subkey) { + if (!isset($p[$subkey])) { + continue 2; + } + $p = $p[$subkey]; + $m = $m[$subkey]; + } + + if (count($p) != count($m)) { + $diff = array_diff($m, $p); + $msg = count($diff) == 1 ? + ' is not a supported algorithm' : + ' are not supported algorithms'; + user_error(implode(', ', $diff) . $msg); + return false; + } + } + + $this->preferred = $preferred; + } + + /** + * Returns the banner message. + * + * Quoting from the RFC, "in some jurisdictions, sending a warning message before + * authentication may be relevant for getting legal protection." + * + * @return string + * @access public + */ + function getBannerMessage() + { + return $this->banner_message; + } + + /** + * Returns the server public host key. + * + * Caching this the first time you connect to a server and checking the result on subsequent connections + * is recommended. Returns false if the server signature is not signed correctly with the public host key. + * + * @return mixed + * @access public + */ + function getServerPublicHostKey() + { + if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { + if (!$this->_connect()) { + return false; + } + } + + $signature = $this->signature; + $server_public_host_key = $this->server_public_host_key; + + if (strlen($server_public_host_key) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4))); + $this->_string_shift($server_public_host_key, $length); + + if ($this->signature_validated) { + return $this->bitmap ? + $this->signature_format . ' ' . base64_encode($this->server_public_host_key) : + false; + } + + $this->signature_validated = true; + + switch ($this->signature_format) { + case 'ssh-dss': + $zero = new BigInteger(); + + if (strlen($server_public_host_key) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $p = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + + if (strlen($server_public_host_key) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $q = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + + if (strlen($server_public_host_key) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $g = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + + if (strlen($server_public_host_key) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $y = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + + /* The value for 'dss_signature_blob' is encoded as a string containing + r, followed by s (which are 160-bit integers, without lengths or + padding, unsigned, and in network byte order). */ + $temp = unpack('Nlength', $this->_string_shift($signature, 4)); + if ($temp['length'] != 40) { + user_error('Invalid signature'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $r = new BigInteger($this->_string_shift($signature, 20), 256); + $s = new BigInteger($this->_string_shift($signature, 20), 256); + + switch (true) { + case $r->equals($zero): + case $r->compare($q) >= 0: + case $s->equals($zero): + case $s->compare($q) >= 0: + user_error('Invalid signature'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $w = $s->modInverse($q); + + $u1 = $w->multiply(new BigInteger(sha1($this->exchange_hash), 16)); + list(, $u1) = $u1->divide($q); + + $u2 = $w->multiply($r); + list(, $u2) = $u2->divide($q); + + $g = $g->modPow($u1, $p); + $y = $y->modPow($u2, $p); + + $v = $g->multiply($y); + list(, $v) = $v->divide($p); + list(, $v) = $v->divide($q); + + if (!$v->equals($r)) { + user_error('Bad server signature'); + return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + } + + break; + case 'ssh-rsa': + case 'rsa-sha2-256': + case 'rsa-sha2-512': + if (strlen($server_public_host_key) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $e = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + + if (strlen($server_public_host_key) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $rawN = $this->_string_shift($server_public_host_key, $temp['length']); + $n = new BigInteger($rawN, -256); + $nLength = strlen(ltrim($rawN, "\0")); + + /* + if (strlen($signature) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($signature, 4)); + $signature = $this->_string_shift($signature, $temp['length']); + + $rsa = new RSA(); + switch ($this->signature_format) { + case 'rsa-sha2-512': + $hash = 'sha512'; + break; + case 'rsa-sha2-256': + $hash = 'sha256'; + break; + //case 'ssh-rsa': + default: + $hash = 'sha1'; + } + $rsa->setHash($hash); + $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1); + $rsa->loadKey(array('e' => $e, 'n' => $n), RSA::PUBLIC_FORMAT_RAW); + + if (!$rsa->verify($this->exchange_hash, $signature)) { + user_error('Bad server signature'); + return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + } + */ + + if (strlen($signature) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($signature, 4)); + $s = new BigInteger($this->_string_shift($signature, $temp['length']), 256); + + // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the + // following URL: + // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf + + // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source. + + if ($s->compare(new BigInteger()) < 0 || $s->compare($n->subtract(new BigInteger(1))) > 0) { + user_error('Invalid signature'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $s = $s->modPow($e, $n); + $s = $s->toBytes(); + + switch ($this->signature_format) { + case 'rsa-sha2-512': + $hash = 'sha512'; + break; + case 'rsa-sha2-256': + $hash = 'sha256'; + break; + //case 'ssh-rsa': + default: + $hash = 'sha1'; + } + $hashObj = new Hash($hash); + switch ($this->signature_format) { + case 'rsa-sha2-512': + $h = pack('N5a*', 0x00305130, 0x0D060960, 0x86480165, 0x03040203, 0x05000440, $hashObj->hash($this->exchange_hash)); + break; + case 'rsa-sha2-256': + $h = pack('N5a*', 0x00303130, 0x0D060960, 0x86480165, 0x03040201, 0x05000420, $hashObj->hash($this->exchange_hash)); + break; + //case 'ssh-rsa': + default: + $hash = 'sha1'; + $h = pack('N4a*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, $hashObj->hash($this->exchange_hash)); + } + $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h; + + if ($s != $h) { + user_error('Bad server signature'); + return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + } + break; + default: + user_error('Unsupported signature format'); + return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + } + + return $this->signature_format . ' ' . base64_encode($this->server_public_host_key); + } + + /** + * Returns the exit status of an SSH command or false. + * + * @return false|int + * @access public + */ + function getExitStatus() + { + if (is_null($this->exit_status)) { + return false; + } + return $this->exit_status; + } + + /** + * Returns the number of columns for the terminal window size. + * + * @return int + * @access public + */ + function getWindowColumns() + { + return $this->windowColumns; + } + + /** + * Returns the number of rows for the terminal window size. + * + * @return int + * @access public + */ + function getWindowRows() + { + return $this->windowRows; + } + + /** + * Sets the number of columns for the terminal window size. + * + * @param int $value + * @access public + */ + function setWindowColumns($value) + { + $this->windowColumns = $value; + } + + /** + * Sets the number of rows for the terminal window size. + * + * @param int $value + * @access public + */ + function setWindowRows($value) + { + $this->windowRows = $value; + } + + /** + * Sets the number of columns and rows for the terminal window size. + * + * @param int $columns + * @param int $rows + * @access public + */ + function setWindowSize($columns = 80, $rows = 24) + { + $this->windowColumns = $columns; + $this->windowRows = $rows; + } + + /** + * Update packet types in log history + * + * @param string $old + * @param string $new + * @access private + */ + function _updateLogHistory($old, $new) + { + if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) { + $this->message_number_log[count($this->message_number_log) - 1] = str_replace( + $old, + $new, + $this->message_number_log[count($this->message_number_log) - 1] + ); + } + } + + /** + * Return the list of authentication methods that may productively continue authentication. + * + * @see https://tools.ietf.org/html/rfc4252#section-5.1 + * @return array|null + */ + function getAuthMethodsToContinue() + { + return $this->auth_methods_to_continue; + } + + /** + * Enables "smart" multi-factor authentication (MFA) + */ + function enableSmartMFA() + { + $this->smartMFA = true; + } + + /** + * Disables "smart" multi-factor authentication (MFA) + */ + function disableSmartMFA() + { + $this->smartMFA = false; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php b/msd/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php new file mode 100644 index 0000000..f26fcbb --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php @@ -0,0 +1,361 @@ + + * login('username', $agent)) { + * exit('Login Failed'); + * } + * + * echo $ssh->exec('pwd'); + * echo $ssh->exec('ls -la'); + * ?> + * + * + * @category System + * @package SSH\Agent + * @author Jim Wigginton + * @copyright 2014 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + * @internal See http://api.libssh.org/rfc/PROTOCOL.agent + */ + +namespace phpseclib\System\SSH; + +use phpseclib\Crypt\RSA; +use phpseclib\System\SSH\Agent\Identity; + +/** + * Pure-PHP ssh-agent client identity factory + * + * requestIdentities() method pumps out \phpseclib\System\SSH\Agent\Identity objects + * + * @package SSH\Agent + * @author Jim Wigginton + * @access public + */ +class Agent +{ + /**#@+ + * Message numbers + * + * @access private + */ + // to request SSH1 keys you have to use SSH_AGENTC_REQUEST_RSA_IDENTITIES (1) + const SSH_AGENTC_REQUEST_IDENTITIES = 11; + // this is the SSH2 response; the SSH1 response is SSH_AGENT_RSA_IDENTITIES_ANSWER (2). + const SSH_AGENT_IDENTITIES_ANSWER = 12; + // the SSH1 request is SSH_AGENTC_RSA_CHALLENGE (3) + const SSH_AGENTC_SIGN_REQUEST = 13; + // the SSH1 response is SSH_AGENT_RSA_RESPONSE (4) + const SSH_AGENT_SIGN_RESPONSE = 14; + /**#@-*/ + + /**@+ + * Agent forwarding status + * + * @access private + */ + // no forwarding requested and not active + const FORWARD_NONE = 0; + // request agent forwarding when opportune + const FORWARD_REQUEST = 1; + // forwarding has been request and is active + const FORWARD_ACTIVE = 2; + /**#@-*/ + + /** + * Unused + */ + const SSH_AGENT_FAILURE = 5; + + /** + * Socket Resource + * + * @var resource + * @access private + */ + var $fsock; + + /** + * Agent forwarding status + * + * @access private + */ + var $forward_status = self::FORWARD_NONE; + + /** + * Buffer for accumulating forwarded authentication + * agent data arriving on SSH data channel destined + * for agent unix socket + * + * @access private + */ + var $socket_buffer = ''; + + /** + * Tracking the number of bytes we are expecting + * to arrive for the agent socket on the SSH data + * channel + */ + var $expected_bytes = 0; + + /** + * Default Constructor + * + * @return \phpseclib\System\SSH\Agent + * @access public + */ + function __construct($address = null) + { + if (!$address) { + switch (true) { + case isset($_SERVER['SSH_AUTH_SOCK']): + $address = $_SERVER['SSH_AUTH_SOCK']; + break; + case isset($_ENV['SSH_AUTH_SOCK']): + $address = $_ENV['SSH_AUTH_SOCK']; + break; + default: + user_error('SSH_AUTH_SOCK not found'); + return false; + } + } + + if (in_array('unix', stream_get_transports())) { + $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr); + if (!$this->fsock) { + user_error("Unable to connect to ssh-agent (Error $errno: $errstr)"); + } + } else { + if (substr($address, 0, 9) != '\\\\.\\pipe\\' || strpos(substr($address, 9), '\\') !== false) { + user_error('Address is not formatted as a named pipe should be'); + } else { + $this->fsock = fopen($address, 'r+b'); + if (!$this->fsock) { + user_error('Unable to open address'); + } + } + } + } + + /** + * Request Identities + * + * See "2.5.2 Requesting a list of protocol 2 keys" + * Returns an array containing zero or more \phpseclib\System\SSH\Agent\Identity objects + * + * @return array + * @access public + */ + function requestIdentities() + { + if (!$this->fsock) { + return array(); + } + + $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); + if (strlen($packet) != fputs($this->fsock, $packet)) { + user_error('Connection closed while requesting identities'); + return array(); + } + + $temp = fread($this->fsock, 4); + if (strlen($temp) != 4) { + user_error('Connection closed while requesting identities'); + return array(); + } + $length = current(unpack('N', $temp)); + $type = ord(fread($this->fsock, 1)); + if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { + user_error('Unable to request identities'); + return array(); + } + + $identities = array(); + $temp = fread($this->fsock, 4); + if (strlen($temp) != 4) { + user_error('Connection closed while requesting identities'); + return array(); + } + $keyCount = current(unpack('N', $temp)); + for ($i = 0; $i < $keyCount; $i++) { + $temp = fread($this->fsock, 4); + if (strlen($temp) != 4) { + user_error('Connection closed while requesting identities'); + return array(); + } + $length = current(unpack('N', $temp)); + $key_blob = fread($this->fsock, $length); + if (strlen($key_blob) != $length) { + user_error('Connection closed while requesting identities'); + return array(); + } + $key_str = 'ssh-rsa ' . base64_encode($key_blob); + $temp = fread($this->fsock, 4); + if (strlen($temp) != 4) { + user_error('Connection closed while requesting identities'); + return array(); + } + $length = current(unpack('N', $temp)); + if ($length) { + $temp = fread($this->fsock, $length); + if (strlen($temp) != $length) { + user_error('Connection closed while requesting identities'); + return array(); + } + $key_str.= ' ' . $temp; + } + $length = current(unpack('N', substr($key_blob, 0, 4))); + $key_type = substr($key_blob, 4, $length); + switch ($key_type) { + case 'ssh-rsa': + $key = new RSA(); + $key->loadKey($key_str); + break; + case 'ssh-dss': + // not currently supported + break; + } + // resources are passed by reference by default + if (isset($key)) { + $identity = new Identity($this->fsock); + $identity->setPublicKey($key); + $identity->setPublicKeyBlob($key_blob); + $identities[] = $identity; + unset($key); + } + } + + return $identities; + } + + /** + * Signal that agent forwarding should + * be requested when a channel is opened + * + * @return bool + * @access public + */ + function startSSHForwarding() + { + if ($this->forward_status == self::FORWARD_NONE) { + $this->forward_status = self::FORWARD_REQUEST; + } + } + + /** + * Request agent forwarding of remote server + * + * @param Net_SSH2 $ssh + * @return bool + * @access private + */ + function _request_forwarding($ssh) + { + $request_channel = $ssh->_get_open_channel(); + if ($request_channel === false) { + return false; + } + + $packet = pack( + 'CNNa*C', + NET_SSH2_MSG_CHANNEL_REQUEST, + $ssh->server_channels[$request_channel], + strlen('auth-agent-req@openssh.com'), + 'auth-agent-req@openssh.com', + 1 + ); + + $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST; + + if (!$ssh->_send_binary_packet($packet)) { + return false; + } + + $response = $ssh->_get_channel_packet($request_channel); + if ($response === false) { + return false; + } + + $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN; + $this->forward_status = self::FORWARD_ACTIVE; + + return true; + } + + /** + * On successful channel open + * + * This method is called upon successful channel + * open to give the SSH Agent an opportunity + * to take further action. i.e. request agent forwarding + * + * @param Net_SSH2 $ssh + * @access private + */ + function _on_channel_open($ssh) + { + if ($this->forward_status == self::FORWARD_REQUEST) { + $this->_request_forwarding($ssh); + } + } + + /** + * Forward data to SSH Agent and return data reply + * + * @param string $data + * @return data from SSH Agent + * @access private + */ + function _forward_data($data) + { + if ($this->expected_bytes > 0) { + $this->socket_buffer.= $data; + $this->expected_bytes -= strlen($data); + } else { + $agent_data_bytes = current(unpack('N', $data)); + $current_data_bytes = strlen($data); + $this->socket_buffer = $data; + if ($current_data_bytes != $agent_data_bytes + 4) { + $this->expected_bytes = ($agent_data_bytes + 4) - $current_data_bytes; + return false; + } + } + + if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) { + user_error('Connection closed attempting to forward data to SSH agent'); + return false; + } + + $this->socket_buffer = ''; + $this->expected_bytes = 0; + + $temp = fread($this->fsock, 4); + if (strlen($temp) != 4) { + user_error('Connection closed while reading data response'); + return false; + } + $agent_reply_bytes = current(unpack('N', $temp)); + + $agent_reply_data = fread($this->fsock, $agent_reply_bytes); + if (strlen($agent_reply_data) != $agent_reply_bytes) { + user_error('Connection closed while reading data response'); + return false; + } + $agent_reply_data = current(unpack('a*', $agent_reply_data)); + + return pack('Na*', $agent_reply_bytes, $agent_reply_data); + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php b/msd/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php new file mode 100644 index 0000000..542502c --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php @@ -0,0 +1,241 @@ + + * @copyright 2009 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + * @internal See http://api.libssh.org/rfc/PROTOCOL.agent + */ + +namespace phpseclib\System\SSH\Agent; + +use phpseclib\System\SSH\Agent; + +/** + * Pure-PHP ssh-agent client identity object + * + * Instantiation should only be performed by \phpseclib\System\SSH\Agent class. + * This could be thought of as implementing an interface that phpseclib\Crypt\RSA + * implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something. + * The methods in this interface would be getPublicKey and sign since those are the + * methods phpseclib looks for to perform public key authentication. + * + * @package SSH\Agent + * @author Jim Wigginton + * @access internal + */ +class Identity +{ + /**@+ + * Signature Flags + * + * See https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3 + * + * @access private + */ + const SSH_AGENT_RSA2_256 = 2; + const SSH_AGENT_RSA2_512 = 4; + /**#@-*/ + + /** + * Key Object + * + * @var \phpseclib\Crypt\RSA + * @access private + * @see self::getPublicKey() + */ + var $key; + + /** + * Key Blob + * + * @var string + * @access private + * @see self::sign() + */ + var $key_blob; + + /** + * Socket Resource + * + * @var resource + * @access private + * @see self::sign() + */ + var $fsock; + + /** + * Signature flags + * + * @var int + * @access private + * @see self::sign() + * @see self::setHash() + */ + var $flags = 0; + + /** + * Default Constructor. + * + * @param resource $fsock + * @return \phpseclib\System\SSH\Agent\Identity + * @access private + */ + function __construct($fsock) + { + $this->fsock = $fsock; + } + + /** + * Set Public Key + * + * Called by \phpseclib\System\SSH\Agent::requestIdentities() + * + * @param \phpseclib\Crypt\RSA $key + * @access private + */ + function setPublicKey($key) + { + $this->key = $key; + $this->key->setPublicKey(); + } + + /** + * Set Public Key + * + * Called by \phpseclib\System\SSH\Agent::requestIdentities(). The key blob could be extracted from $this->key + * but this saves a small amount of computation. + * + * @param string $key_blob + * @access private + */ + function setPublicKeyBlob($key_blob) + { + $this->key_blob = $key_blob; + } + + /** + * Get Public Key + * + * Wrapper for $this->key->getPublicKey() + * + * @param int $format optional + * @return mixed + * @access public + */ + function getPublicKey($format = null) + { + return !isset($format) ? $this->key->getPublicKey() : $this->key->getPublicKey($format); + } + + /** + * Set Signature Mode + * + * Doesn't do anything as ssh-agent doesn't let you pick and choose the signature mode. ie. + * ssh-agent's only supported mode is \phpseclib\Crypt\RSA::SIGNATURE_PKCS1 + * + * @param int $mode + * @access public + */ + function setSignatureMode($mode) + { + } + + /** + * Set Hash + * + * ssh-agent doesn't support using hashes for RSA other than SHA1 + * + * @param string $hash + * @access public + */ + function setHash($hash) + { + $this->flags = 0; + switch ($hash) { + case 'sha1': + break; + case 'sha256': + $this->flags = self::SSH_AGENT_RSA2_256; + break; + case 'sha512': + $this->flags = self::SSH_AGENT_RSA2_512; + break; + default: + user_error('The only supported hashes for RSA are sha1, sha256 and sha512'); + } + } + + /** + * Create a signature + * + * See "2.6.2 Protocol 2 private key signature request" + * + * @param string $message + * @return string + * @access public + */ + function sign($message) + { + // the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE + $packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, $this->flags); + $packet = pack('Na*', strlen($packet), $packet); + if (strlen($packet) != fputs($this->fsock, $packet)) { + user_error('Connection closed during signing'); + return false; + } + + $temp = fread($this->fsock, 4); + if (strlen($temp) != 4) { + user_error('Connection closed during signing'); + return false; + } + $length = current(unpack('N', $temp)); + $type = ord(fread($this->fsock, 1)); + if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) { + user_error('Unable to retrieve signature'); + return false; + } + + $signature_blob = fread($this->fsock, $length - 1); + if (strlen($signature_blob) != $length - 1) { + user_error('Connection closed during signing'); + return false; + } + $length = current(unpack('N', $this->_string_shift($signature_blob, 4))); + if ($length != strlen($signature_blob)) { + user_error('Malformed signature blob'); + } + $length = current(unpack('N', $this->_string_shift($signature_blob, 4))); + if ($length > strlen($signature_blob) + 4) { + user_error('Malformed signature blob'); + } + $type = $this->_string_shift($signature_blob, $length); + $this->_string_shift($signature_blob, 4); + + return $signature_blob; + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } +} diff --git a/msd/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php b/msd/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php new file mode 100644 index 0000000..73c5d32 --- /dev/null +++ b/msd/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php @@ -0,0 +1,17 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param mixed[] $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } +} diff --git a/msd/vendor/psr/log/Psr/Log/InvalidArgumentException.php b/msd/vendor/psr/log/Psr/Log/InvalidArgumentException.php new file mode 100644 index 0000000..8e76d23 --- /dev/null +++ b/msd/vendor/psr/log/Psr/Log/InvalidArgumentException.php @@ -0,0 +1,7 @@ +logger = $logger; + } +} diff --git a/msd/vendor/psr/log/Psr/Log/LoggerInterface.php b/msd/vendor/psr/log/Psr/Log/LoggerInterface.php new file mode 100644 index 0000000..c020c90 --- /dev/null +++ b/msd/vendor/psr/log/Psr/Log/LoggerInterface.php @@ -0,0 +1,125 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + abstract public function log($level, $message, array $context = array()); +} diff --git a/msd/vendor/psr/log/Psr/Log/NullLogger.php b/msd/vendor/psr/log/Psr/Log/NullLogger.php new file mode 100644 index 0000000..34347cd --- /dev/null +++ b/msd/vendor/psr/log/Psr/Log/NullLogger.php @@ -0,0 +1,30 @@ +logger) { }` + * blocks. + */ +class NullLogger extends AbstractLogger +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + public function log($level, $message, array $context = array()) + { + // noop + } +} diff --git a/msd/vendor/psr/log/Psr/Log/Test/DummyTest.php b/msd/vendor/psr/log/Psr/Log/Test/DummyTest.php new file mode 100644 index 0000000..37ac246 --- /dev/null +++ b/msd/vendor/psr/log/Psr/Log/Test/DummyTest.php @@ -0,0 +1,18 @@ + ". + * + * Example ->error('Foo') would yield "error Foo". + * + * @return string[] + */ + abstract public function getLogs(); + + public function testImplements() + { + $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger()); + } + + /** + * @dataProvider provideLevelsAndMessages + */ + public function testLogsAtAllLevels($level, $message) + { + $logger = $this->getLogger(); + $logger->{$level}($message, array('user' => 'Bob')); + $logger->log($level, $message, array('user' => 'Bob')); + + $expected = array( + $level.' message of level '.$level.' with context: Bob', + $level.' message of level '.$level.' with context: Bob', + ); + $this->assertEquals($expected, $this->getLogs()); + } + + public function provideLevelsAndMessages() + { + return array( + LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), + LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'), + LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'), + LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'), + LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'), + LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'), + LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'), + LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'), + ); + } + + /** + * @expectedException \Psr\Log\InvalidArgumentException + */ + public function testThrowsOnInvalidLevel() + { + $logger = $this->getLogger(); + $logger->log('invalid level', 'Foo'); + } + + public function testContextReplacement() + { + $logger = $this->getLogger(); + $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); + + $expected = array('info {Message {nothing} Bob Bar a}'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testObjectCastToString() + { + if (method_exists($this, 'createPartialMock')) { + $dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString')); + } else { + $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString')); + } + $dummy->expects($this->once()) + ->method('__toString') + ->will($this->returnValue('DUMMY')); + + $this->getLogger()->warning($dummy); + + $expected = array('warning DUMMY'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testContextCanContainAnything() + { + $closed = fopen('php://memory', 'r'); + fclose($closed); + + $context = array( + 'bool' => true, + 'null' => null, + 'string' => 'Foo', + 'int' => 0, + 'float' => 0.5, + 'nested' => array('with object' => new DummyTest), + 'object' => new \DateTime, + 'resource' => fopen('php://memory', 'r'), + 'closed' => $closed, + ); + + $this->getLogger()->warning('Crazy context data', $context); + + $expected = array('warning Crazy context data'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testContextExceptionKeyCanBeExceptionOrOtherValues() + { + $logger = $this->getLogger(); + $logger->warning('Random message', array('exception' => 'oops')); + $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); + + $expected = array( + 'warning Random message', + 'critical Uncaught Exception!' + ); + $this->assertEquals($expected, $this->getLogs()); + } +} diff --git a/msd/vendor/psr/log/Psr/Log/Test/TestLogger.php b/msd/vendor/psr/log/Psr/Log/Test/TestLogger.php new file mode 100644 index 0000000..0a108f4 --- /dev/null +++ b/msd/vendor/psr/log/Psr/Log/Test/TestLogger.php @@ -0,0 +1,147 @@ + $level, + 'message' => $message, + 'context' => $context, + ]; + + $this->recordsByLevel[$record['level']][] = $record; + $this->records[] = $record; + } + + public function hasRecords($level) + { + return isset($this->recordsByLevel[$level]); + } + + public function hasRecord($record, $level) + { + if (is_string($record)) { + $record = ['message' => $record]; + } + return $this->hasRecordThatPasses(function ($rec) use ($record) { + if ($rec['message'] !== $record['message']) { + return false; + } + if (isset($record['context']) && $rec['context'] !== $record['context']) { + return false; + } + return true; + }, $level); + } + + public function hasRecordThatContains($message, $level) + { + return $this->hasRecordThatPasses(function ($rec) use ($message) { + return strpos($rec['message'], $message) !== false; + }, $level); + } + + public function hasRecordThatMatches($regex, $level) + { + return $this->hasRecordThatPasses(function ($rec) use ($regex) { + return preg_match($regex, $rec['message']) > 0; + }, $level); + } + + public function hasRecordThatPasses(callable $predicate, $level) + { + if (!isset($this->recordsByLevel[$level])) { + return false; + } + foreach ($this->recordsByLevel[$level] as $i => $rec) { + if (call_user_func($predicate, $rec, $i)) { + return true; + } + } + return false; + } + + public function __call($method, $args) + { + if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { + $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; + $level = strtolower($matches[2]); + if (method_exists($this, $genericMethod)) { + $args[] = $level; + return call_user_func_array([$this, $genericMethod], $args); + } + } + throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()'); + } + + public function reset() + { + $this->records = []; + $this->recordsByLevel = []; + } +} diff --git a/msd/vendor/psr/log/README.md b/msd/vendor/psr/log/README.md new file mode 100644 index 0000000..a9f20c4 --- /dev/null +++ b/msd/vendor/psr/log/README.md @@ -0,0 +1,58 @@ +PSR Log +======= + +This repository holds all interfaces/classes/traits related to +[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). + +Note that this is not a logger of its own. It is merely an interface that +describes a logger. See the specification for more details. + +Installation +------------ + +```bash +composer require psr/log +``` + +Usage +----- + +If you need a logger, you can use the interface like this: + +```php +logger = $logger; + } + + public function doSomething() + { + if ($this->logger) { + $this->logger->info('Doing work'); + } + + try { + $this->doSomethingElse(); + } catch (Exception $exception) { + $this->logger->error('Oh no!', array('exception' => $exception)); + } + + // do something useful + } +} +``` + +You can then pick one of the implementations of the interface to get a logger. + +If you want to implement the interface, you can require this package and +implement `Psr\Log\LoggerInterface` in your code. Please read the +[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) +for details. diff --git a/msd/vendor/psr/log/composer.json b/msd/vendor/psr/log/composer.json new file mode 100644 index 0000000..ca05695 --- /dev/null +++ b/msd/vendor/psr/log/composer.json @@ -0,0 +1,26 @@ +{ + "name": "psr/log", + "description": "Common interface for logging libraries", + "keywords": ["psr", "psr-3", "log"], + "homepage": "https://github.com/php-fig/log", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + } +} diff --git a/msd/vendor/psr/simple-cache/src/CacheException.php b/msd/vendor/psr/simple-cache/src/CacheException.php new file mode 100644 index 0000000..4e5a06d --- /dev/null +++ b/msd/vendor/psr/simple-cache/src/CacheException.php @@ -0,0 +1,10 @@ + value pairs. Cache keys that do not exist or are stale will have $default as value. + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if $keys is neither an array nor a Traversable, + * or if any of the $keys are not a legal value. + */ + public function getMultiple($keys, $default = null); + + /** + * Persists a set of key => value pairs in the cache, with an optional TTL. + * + * @param iterable $values A list of key => value pairs for a multiple-set operation. + * @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and + * the driver supports TTL then the library may set a default value + * for it or let the driver take care of that. + * + * @return bool True on success and false on failure. + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if $values is neither an array nor a Traversable, + * or if any of the $values are not a legal value. + */ + public function setMultiple($values, $ttl = null); + + /** + * Deletes multiple cache items in a single operation. + * + * @param iterable $keys A list of string-based keys to be deleted. + * + * @return bool True if the items were successfully removed. False if there was an error. + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if $keys is neither an array nor a Traversable, + * or if any of the $keys are not a legal value. + */ + public function deleteMultiple($keys); + + /** + * Determines whether an item is present in the cache. + * + * NOTE: It is recommended that has() is only to be used for cache warming type purposes + * and not to be used within your live applications operations for get/set, as this method + * is subject to a race condition where your has() will return true and immediately after, + * another script can remove it making the state of your app out of date. + * + * @param string $key The cache item key. + * + * @return bool + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if the $key string is not a legal value. + */ + public function has($key); +} diff --git a/msd/vendor/psr/simple-cache/src/InvalidArgumentException.php b/msd/vendor/psr/simple-cache/src/InvalidArgumentException.php new file mode 100644 index 0000000..f97bca0 --- /dev/null +++ b/msd/vendor/psr/simple-cache/src/InvalidArgumentException.php @@ -0,0 +1,13 @@ + + +RUN apt-get update && apt-get install -y libzip-dev libcurl4-openssl-dev +RUN docker-php-ext-install -j$(nproc) zip curl + +ADD ./vendor /var/www/html/vendor +ADD ./example /var/www/html/example +ADD ./src /var/www/html/src + +RUN chown -R www-data:www-data /var/www/html/example diff --git a/msd/vendor/visualappeal/php-auto-update/LICENSE.md b/msd/vendor/visualappeal/php-auto-update/LICENSE.md new file mode 100644 index 0000000..d32dac9 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2017 VisualAppeal GbR + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/msd/vendor/visualappeal/php-auto-update/README.md b/msd/vendor/visualappeal/php-auto-update/README.md new file mode 100644 index 0000000..2d17b28 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/README.md @@ -0,0 +1,29 @@ +[![PHPUnit](https://github.com/VisualAppeal/PHP-Auto-Update/actions/workflows/phpunit.yml/badge.svg)](https://github.com/VisualAppeal/PHP-Auto-Update/actions/workflows/phpunit.yml) + +With this library your users can automatically update their instance of your application to the newest version. I created it as a proof of concept and don't know if it is used somewhere. So please use this library with caution because it can potentially make your users software nonfunctional if something goes wrong. + +## Installation + +* Install the library via composer [visualappeal/php-auto-update](https://packagist.org/packages/visualappeal/php-auto-update) +* Create an update file/method in your application with your update routine (see `example/client/update/index.php`) +* Create a `update.json` or `update.ini` on your server (where the client should get the updates, see `example/server/update.json` or `example/server/update.ini`) + +**Important: Please notice that PHP needs write permissions to update the files on the webserver** + +## Example + +You can start an example docker container via `docker-compose up` and see the example by visiting `http://127.0.0.1:8080/example/client/` + +## Client + +### Caching + +The library supports the `desarrolla2/cache` component, and you should use it! Otherwise, the client will download the update ini/json file on every request. + +## Server + +Your server needs at least one file which will be downloaded from the client to check for updates. This can be a json or an ini file. See `example/server/` for examples. The ini section key respectively the json key is the version. This library uses semantic versioning to compare the versions. See [semver.org](http://semver.org/) for details. The ini/json value is the absolute url to the update zip file. Since the library supports incremental updates, the zip file only need to contain the changes since the last version. The zip files do not need to be placed on the same server, they can be uploaded to S3 or another cloud storage, too. + +## Documentation + +For the documentation see the comments in `src/AutoUpdate.php` or the example in the `example` directory. diff --git a/msd/vendor/visualappeal/php-auto-update/SECURITY.md b/msd/vendor/visualappeal/php-auto-update/SECURITY.md new file mode 100644 index 0000000..fce4ae9 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/SECURITY.md @@ -0,0 +1,16 @@ +# Security Policy + +## Supported Versions + +The following php versions are currently supported: + +| Version | Supported | +| ------- | ------------------ | +| 7.2 | :white_check_mark: | +| 7.3 | :white_check_mark: | +| 7.4 | :white_check_mark: | +| 8.0 | :white_check_mark: | + +## Reporting a Vulnerability + +If you find a security vulnerability, please write an email to tim@visualappeal.de. diff --git a/msd/vendor/visualappeal/php-auto-update/composer.json b/msd/vendor/visualappeal/php-auto-update/composer.json new file mode 100644 index 0000000..9136586 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/composer.json @@ -0,0 +1,37 @@ +{ + "name": "visualappeal/php-auto-update", + "description": "Autoupdater for PHP", + "license": "MIT", + "authors": [ + { + "name": "Tim Helfensdörfer", + "email": "tim@visualappeal.de" + } + ], + "minimum-stability": "stable", + "require": { + "ext-json": "*", + "ext-curl": "*", + "ext-zip": "*", + "php": ">=7.2.0", + "desarrolla2/cache": "^3.0", + "monolog/monolog": "^2.1", + "composer/semver": "^3.0", + "psr/log": "1.1.4" + }, + "require-dev": { + "roave/security-advisories": "dev-master", + "phpunit/phpunit": "^9.5" + }, + "autoload": { + "psr-4": { + "VisualAppeal\\": "src/" + } + }, + "scripts": { + "test": "phpunit -c tests/UnitTests.xml" + }, + "config": { + "sort-packages": true + } +} diff --git a/msd/vendor/visualappeal/php-auto-update/docker-compose.yaml b/msd/vendor/visualappeal/php-auto-update/docker-compose.yaml new file mode 100644 index 0000000..c421220 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/docker-compose.yaml @@ -0,0 +1,7 @@ +version: '3' + +services: + app: + build: . + ports: + - 8080:80 diff --git a/msd/vendor/visualappeal/php-auto-update/example/client/files/file1.php b/msd/vendor/visualappeal/php-auto-update/example/client/files/file1.php new file mode 100644 index 0000000..b1eb7c9 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/example/client/files/file1.php @@ -0,0 +1 @@ +This is file 1. Version 0.1.0 diff --git a/msd/vendor/visualappeal/php-auto-update/example/client/files/file2.php b/msd/vendor/visualappeal/php-auto-update/example/client/files/file2.php new file mode 100644 index 0000000..9ad790f --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/example/client/files/file2.php @@ -0,0 +1 @@ +This is file 2. Version 0.1.0 diff --git a/msd/vendor/visualappeal/php-auto-update/example/client/index.php b/msd/vendor/visualappeal/php-auto-update/example/client/index.php new file mode 100644 index 0000000..b664537 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/example/client/index.php @@ -0,0 +1,19 @@ + + + + + PHP Auto Update + + + + +
      +

      This is the test index.

      + +

      Update now!

      + +

      Contents of somefile.php:

      +
      +
      + + diff --git a/msd/vendor/visualappeal/php-auto-update/example/client/somefile.php b/msd/vendor/visualappeal/php-auto-update/example/client/somefile.php new file mode 100644 index 0000000..1675ba4 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/example/client/somefile.php @@ -0,0 +1 @@ +This is some file. Version 0.1.0 diff --git a/msd/vendor/visualappeal/php-auto-update/example/client/update/.gitignore b/msd/vendor/visualappeal/php-auto-update/example/client/update/.gitignore new file mode 100644 index 0000000..ed892ce --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/example/client/update/.gitignore @@ -0,0 +1,2 @@ +!.gitignore +update.log diff --git a/msd/vendor/visualappeal/php-auto-update/src/AutoUpdate.php b/msd/vendor/visualappeal/php-auto-update/src/AutoUpdate.php new file mode 100644 index 0000000..19ce212 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/src/AutoUpdate.php @@ -0,0 +1,1019 @@ +log = new Logger('auto-update'); + + $this->setTempDir($tempDir ?? (__DIR__ . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR)); + $this->setInstallDir($installDir ?? (__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR)); + + $this->latestVersion = '0.0.0'; + $this->currentVersion = '0.0.0'; + + // Init cache + $this->cache = new NotCache(); + + ini_set('max_execution_time', $maxExecutionTime); + } + + /** + * Set the temporary download directory. + * + * @param string $dir + * @return bool + */ + public function setTempDir(string $dir): bool + { + $dir = $this->addTrailingSlash($dir); + + if (!is_dir($dir)) { + $this->log->debug(sprintf('Creating new temporary directory "%s"', $dir)); + + if (!mkdir($dir, 0755, true) && !is_dir($dir)) { + $this->log->critical(sprintf('Could not create temporary directory "%s"', $dir)); + + return false; + } + } + + $this->tempDir = $dir; + + return true; + } + + /** + * Set the installation directory. + * + * @param string $dir + * @return bool + */ + public function setInstallDir(string $dir): bool + { + $dir = $this->addTrailingSlash($dir); + + if (!is_dir($dir)) { + $this->log->debug(sprintf('Creating new install directory "%s"', $dir)); + + if (!mkdir($dir, 0755, true) && !is_dir($dir)) { + $this->log->critical(sprintf('Could not create install directory "%s"', $dir)); + + return false; + } + } + + $this->installDir = $dir; + + return true; + } + + /** + * Set the update filename. + * + * @param string $updateFile + * @return AutoUpdate + */ + public function setUpdateFile(string $updateFile): AutoUpdate + { + $this->updateFile = $updateFile; + + return $this; + } + + /** + * Set the update filename. + * + * @param string $updateUrl + * @return AutoUpdate + */ + public function setUpdateUrl(string $updateUrl): AutoUpdate + { + $this->updateUrl = $updateUrl; + + return $this; + } + + /** + * Set the update branch. + * + * @param string branch + * @return AutoUpdate + */ + public function setBranch($branch): AutoUpdate + { + $this->branch = $branch; + + return $this; + } + + /** + * Set the cache component. + * + * @param CacheInterface $adapter See https://github.com/desarrolla2/Cache + * @param int $ttl + * @return AutoUpdate + */ + public function setCache(CacheInterface $adapter, int $ttl): AutoUpdate + { + $this->cache = $adapter; + $this->cacheTtl = $ttl; + + return $this; + } + + /** + * Set the version of the current installed software. + * + * @param string $currentVersion + * @return AutoUpdate + */ + public function setCurrentVersion(string $currentVersion): AutoUpdate + { + $this->currentVersion = $currentVersion; + + return $this; + } + + /** + * Set username and password for basic authentication. + * + * @param string $username + * @param string $password + * @return AutoUpdate + */ + public function setBasicAuth(string $username, string $password): AutoUpdate + { + $this->username = $username; + $this->password = $password; + + return $this; + } + + /** + * Set authentication header if username and password exist. + * + * @return null|resource + */ + private function useBasicAuth() + { + if ($this->username && $this->password) { + return stream_context_create(array( + 'http' => array( + 'header' => "Authorization: Basic " . base64_encode("$this->username:$this->password") + ) + )); + } + + return null; + } + + /** + * Replace the logger internally used by the given logger instance. + * + * @param LoggerInterface $logger + * @return AutoUpdate + */ + public function setLogger(LoggerInterface $logger): AutoUpdate + { + $this->log = $logger; + + return $this; + } + + /** + * Get the name of the latest version. + * + * @return string + */ + public function getLatestVersion(): string + { + return $this->latestVersion; + } + + /** + * Get an array of versions which will be installed. + * + * @return array + */ + public function getVersionsToUpdate(): array + { + if (count($this->updates) > 0) { + return array_map(static function ($update) { + return $update['version']; + }, $this->updates); + } + + return []; + } + + /** + * Get the results of the last simulation. + * + * @return array + */ + public function getSimulationResults(): array + { + return $this->simulationResults; + } + + /** + * @return bool + */ + public function getSslVerifyHost(): bool + { + return $this->sslVerifyHost; + } + + /** + * @param bool $sslVerifyHost + * @return AutoUpdate + */ + public function setSslVerifyHost(bool $sslVerifyHost): AutoUpdate + { + $this->sslVerifyHost = $sslVerifyHost; + + return $this; + } + + /** + * Check for a new version + * + * @param int $timeout Download timeout in seconds (Only applied for downloads via curl) + * @return int|bool + * true: New version is available + * false: Error while checking for update + * int: Status code (i.e. AutoUpdate::NO_UPDATE_AVAILABLE) + * @throws DownloadException + * @throws InvalidArgumentException + * @throws ParserException + */ + public function checkUpdate(int $timeout = 10) + { + $this->log->notice('Checking for a new update...'); + + // Reset previous updates + $this->latestVersion = '0.0.0'; + $this->updates = []; + + $versions = $this->cache->get('update-versions'); + + // Create absolute url to update file + $updateFile = $this->updateUrl . '/' . $this->updateFile; + if (!empty($this->branch)) { + $updateFile .= '.' . $this->branch; + } + + // Check if cache is empty + if ($versions === null || $versions === false) { + $this->log->debug(sprintf('Get new updates from %s', $updateFile)); + + // Read update file from update server + if (function_exists('curl_version') && $this->isValidUrl($updateFile)) { + $update = $this->downloadCurl($updateFile, $timeout); + + if ($update === false) { + $this->log->error(sprintf('Could not download update file "%s" via curl!', $updateFile)); + + throw new DownloadException($updateFile); + } + } else { + $update = @file_get_contents($updateFile, false, $this->useBasicAuth()); + + if ($update === false) { + $this->log->error(sprintf('Could not download update file "%s" via file_get_contents!', + $updateFile)); + + throw new DownloadException($updateFile); + } + } + + // Parse update file + $updateFileExtension = substr(strrchr($this->updateFile, '.'), 1); + switch ($updateFileExtension) { + case 'ini': + $versions = parse_ini_string($update, true); + if (!is_array($versions)) { + $this->log->error('Unable to parse ini update file!'); + + throw new ParserException(sprintf('Could not parse update ini file %s!', $this->updateFile)); + } + + $versions = array_map(static function ($block) { + return $block['url'] ?? false; + }, $versions); + + break; + case 'json': + $versions = (array) json_decode($update, false); + if (!is_array($versions)) { + $this->log->error('Unable to parse json update file!'); + + throw new ParserException(sprintf('Could not parse update json file %s!', $this->updateFile)); + } + + break; + default: + $this->log->error(sprintf('Unknown file extension "%s"', $updateFileExtension)); + + throw new ParserException(sprintf('Unknown file extension for update file %s!', $this->updateFile)); + } + + $this->cache->set('update-versions', $versions, $this->cacheTtl); + } else { + $this->log->debug('Got updates from cache'); + } + + if (!is_array($versions)) { + $this->log->error(sprintf('Could not read versions from server %s', $updateFile)); + + return false; + } + + // Check for latest version + foreach ($versions as $version => $updateUrl) { + if (Comparator::greaterThan($version, $this->currentVersion)) { + if (Comparator::greaterThan($version, $this->latestVersion)) { + $this->latestVersion = $version; + } + + $this->updates[] = [ + 'version' => $version, + 'url' => $updateUrl, + ]; + } + } + + // Sort versions to install + usort($this->updates, static function ($a, $b) { + if (Comparator::equalTo($a['version'], $b['version'])) { + return 0; + } + + return Comparator::lessThan($a['version'], $b['version']) ? - 1 : 1; + }); + + if ($this->newVersionAvailable()) { + $this->log->debug(sprintf('New version "%s" available', $this->latestVersion)); + + return true; + } + + $this->log->debug('No new version available'); + + return self::NO_UPDATE_AVAILABLE; + } + + /** + * Check if a new version is available. + * + * @return bool + */ + public function newVersionAvailable(): bool + { + return Comparator::greaterThan($this->latestVersion, $this->currentVersion); + } + + /** + * Check if url is valid. + * + * @param string $url + * @return bool + */ + protected function isValidUrl(string $url): bool + { + return (filter_var($url, FILTER_VALIDATE_URL) !== false); + } + + /** + * Download file via curl. + * + * @param string $url URL to file + * @param int $timeout + * @return string|false + */ + protected function downloadCurl(string $url, int $timeout = 10) + { + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->sslVerifyHost ? 2 : 0); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->sslVerifyHost); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); + $update = curl_exec($curl); + + $success = true; + if (curl_error($curl)) { + $success = false; + $this->log->error(sprintf( + 'Could not download update "%s" via curl: %s!', + $url, + curl_error($curl) + )); + } + curl_close($curl); + + return ($success === true) ? $update : false; + } + + /** + * Download the update + * + * @param string $updateUrl Url where to download from + * @param string $updateFile Path where to save the download + * @return bool + * @throws DownloadException + * @throws Exception + */ + protected function downloadUpdate(string $updateUrl, string $updateFile): bool + { + $this->log->info(sprintf('Downloading update "%s" to "%s"', $updateUrl, $updateFile)); + if (function_exists('curl_version') && $this->isValidUrl($updateUrl)) { + $update = $this->downloadCurl($updateUrl); + if ($update === false) { + return false; + } + } elseif (ini_get('allow_url_fopen')) { + $update = @file_get_contents($updateUrl, false, $this->useBasicAuth()); + + if ($update === false) { + $this->log->error(sprintf('Could not download update "%s"!', $updateUrl)); + + throw new DownloadException($updateUrl); + } + } else { + throw new RuntimeException('No valid download method found!'); + } + + $handle = fopen($updateFile, 'wb'); + if (!$handle) { + $this->log->error(sprintf('Could not open file handle to save update to "%s"!', $updateFile)); + + return false; + } + + if (!fwrite($handle, $update)) { + $this->log->error(sprintf('Could not write update to file "%s"!', $updateFile)); + fclose($handle); + + return false; + } + + fclose($handle); + + return true; + } + + /** + * Simulate update process. + * + * @param string $updateFile + * @return bool + */ + protected function simulateInstall(string $updateFile): bool + { + $this->log->notice('[SIMULATE] Install new version'); + clearstatcache(); + + // Check if zip file could be opened + $zip = new ZipArchive(); + $resource = $zip->open($updateFile); + if ($resource !== true) { + $this->log->error(sprintf('Could not open zip file "%s", error: %d', $updateFile, $resource)); + + return false; + } + + $files = []; + $simulateSuccess = true; + + for ($i = 0; $i < $zip->numFiles; $i++) { + $fileStats = $zip->statIndex($i); + $filename = $fileStats['name']; + $foldername = $this->installDir . dirname($filename); + $absoluteFilename = $this->installDir . $filename; + + $files[$i] = [ + 'filename' => $filename, + 'foldername' => $foldername, + 'absolute_filename' => $absoluteFilename, + ]; + + $this->log->debug(sprintf('[SIMULATE] Updating file "%s"', $filename)); + + // Check if parent directory is writable + if (!is_dir($foldername)) { + if (!mkdir($foldername) && !is_dir($foldername)) { + throw new RuntimeException(sprintf('Directory "%s" was not created', $foldername)); + } + $this->log->debug(sprintf('[SIMULATE] Create directory "%s"', $foldername)); + $files[$i]['parent_folder_exists'] = false; + + $parent = dirname($foldername); + if (!is_writable($parent)) { + $files[$i]['parent_folder_writable'] = false; + + $simulateSuccess = false; + $this->log->warning(sprintf('[SIMULATE] Directory "%s" has to be writeable!', $parent)); + } else { + $files[$i]['parent_folder_writable'] = true; + } + } + + // Skip if entry is a directory + if ($filename[strlen($filename) - 1] === DIRECTORY_SEPARATOR) { + continue; + } + + // Write to file + if (file_exists($absoluteFilename)) { + $files[$i]['file_exists'] = true; + if (!is_writable($absoluteFilename)) { + $files[$i]['file_writable'] = false; + + $simulateSuccess = false; + $this->log->warning(sprintf('[SIMULATE] Could not overwrite "%s"!', $absoluteFilename)); + } + } else { + $files[$i]['file_exists'] = false; + + if (is_dir($foldername)) { + if (!is_writable($foldername)) { + $files[$i]['file_writable'] = false; + + $simulateSuccess = false; + $this->log->warning(sprintf('[SIMULATE] The file "%s" could not be created!', + $absoluteFilename)); + } else { + $files[$i]['file_writable'] = true; + } + } else { + $files[$i]['file_writable'] = true; + + $this->log->debug(sprintf('[SIMULATE] The file "%s" could be created', $absoluteFilename)); + } + } + + if ($filename === $this->updateScriptName) { + $this->log->debug(sprintf('[SIMULATE] Update script "%s" found', $absoluteFilename)); + $files[$i]['update_script'] = true; + } else { + $files[$i]['update_script'] = false; + } + } + + $zip->close(); + + $this->simulationResults = $files; + + return $simulateSuccess; + } + + /** + * Install update. + * + * @param string $updateFile Path to the update file + * @param bool $simulateInstall Check for directory and file permissions instead of installing the update + * @param string $version + * @return bool + */ + protected function install(string $updateFile, bool $simulateInstall, string $version): bool + { + $this->log->notice(sprintf('Trying to install update "%s"', $updateFile)); + + // Check if install should be simulated + if ($simulateInstall) { + if ($this->simulateInstall($updateFile)) { + $this->log->notice(sprintf('Simulation of update "%s" process succeeded', $version)); + + return true; + } + + $this->log->critical(sprintf('Simulation of update "%s" process failed!', $version)); + + return self::ERROR_SIMULATE; + } + + clearstatcache(); + + // Install only if simulateInstall === false + + // Check if zip file could be opened + $zip = new ZipArchive(); + $resource = $zip->open($updateFile); + if ($resource !== true) { + $this->log->error(sprintf('Could not open zip file "%s", error: %d', $updateFile, $resource)); + + return false; + } + + // Read every file from archive + for ($i = 0; $i < $zip->numFiles; $i++) { + $fileStats = $zip->statIndex($i); + $filename = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $fileStats['name']); + $foldername = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, + $this->installDir . dirname($filename)); + $absoluteFilename = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->installDir . $filename); + $this->log->debug(sprintf('Updating file "%s"', $filename)); + + if (!is_dir($foldername) && !mkdir($foldername, $this->dirPermissions, true) && !is_dir($foldername)) { + $this->log->error(sprintf('Directory "%s" has to be writeable!', $foldername)); + + return false; + } + + // Skip if entry is a directory + if ($filename[strlen($filename) - 1] === DIRECTORY_SEPARATOR) { + continue; + } + + // Extract file + if ($zip->extractTo($this->installDir, $fileStats['name']) === false) { + $this->log->error(sprintf('Could not read zip entry "%s"', $fileStats['name'])); + continue; + } + + //If file is a update script, include + if ($filename === $this->updateScriptName) { + $this->log->debug(sprintf('Try to include update script "%s"', $absoluteFilename)); + require($absoluteFilename); + + $this->log->info(sprintf('Update script "%s" included!', $absoluteFilename)); + if (!unlink($absoluteFilename)) { + $this->log->warning(sprintf('Could not delete update script "%s"!', $absoluteFilename)); + } + } + } + + $zip->close(); + + $this->log->notice(sprintf('Update "%s" successfully installed', $version)); + + return true; + } + + /** + * Update to the latest version + * + * @param bool $simulateInstall Check for directory and file permissions before copying files (Default: true) + * @param bool $deleteDownload Delete download after update (Default: true) + * @return integer|bool + * @throws DownloadException + * @throws ParserException + * @throws InvalidArgumentException + */ + public function update(bool $simulateInstall = true, bool $deleteDownload = true) + { + $this->log->info('Trying to perform update'); + + // Check for latest version + if ($this->latestVersion === null || count($this->updates) === 0) { + $this->checkUpdate(); + } + + if ($this->latestVersion === null || count($this->updates) === 0) { + $this->log->error('Could not get latest version from server!'); + + return self::ERROR_VERSION_CHECK; + } + + // Check if current version is up-to-date + if (!$this->newVersionAvailable()) { + $this->log->warning('No update available!'); + + return self::NO_UPDATE_AVAILABLE; + } + + foreach ($this->updates as $update) { + $this->log->debug(sprintf('Update to version "%s"', $update['version'])); + + // Check for temp directory + if (empty($this->tempDir) || !is_dir($this->tempDir) || !is_writable($this->tempDir)) { + $this->log->critical(sprintf('Temporary directory "%s" does not exist or is not writeable!', + $this->tempDir)); + + return self::ERROR_TEMP_DIR; + } + + // Check for install directory + if (empty($this->installDir) || !is_dir($this->installDir) || !is_writable($this->installDir)) { + $this->log->critical(sprintf('Install directory "%s" does not exist or is not writeable!', + $this->installDir)); + + return self::ERROR_INSTALL_DIR; + } + + $updateFile = $this->tempDir . $update['version'] . '.zip'; + + // Download update + if (!is_file($updateFile)) { + if (!$this->downloadUpdate($update['url'], $updateFile)) { + $this->log->critical(sprintf('Failed to download update from "%s" to "%s"!', $update['url'], + $updateFile)); + + return self::ERROR_DOWNLOAD_UPDATE; + } + + $this->log->debug(sprintf('Latest update downloaded to "%s"', $updateFile)); + } else { + $this->log->info(sprintf('Latest update already downloaded to "%s"', $updateFile)); + } + + // Install update + $result = $this->install($updateFile, $simulateInstall, $update['version']); + if ($result === true) { + $this->runOnEachUpdateFinishCallbacks($update['version'], $simulateInstall); + if ($deleteDownload) { + $this->log->debug(sprintf('Trying to delete update file "%s" after successfull update', + $updateFile)); + if (unlink($updateFile)) { + $this->log->info(sprintf('Update file "%s" deleted after successfull update', $updateFile)); + } else { + $this->log->error(sprintf('Could not delete update file "%s" after successfull update!', + $updateFile)); + + return self::ERROR_DELETE_TEMP_UPDATE; + } + } + } else { + if ($deleteDownload) { + $this->log->debug(sprintf('Trying to delete update file "%s" after failed update', $updateFile)); + if (unlink($updateFile)) { + $this->log->info(sprintf('Update file "%s" deleted after failed update', $updateFile)); + } else { + $this->log->error(sprintf('Could not delete update file "%s" after failed update!', + $updateFile)); + } + } + + return false; + } + } + + $this->runOnAllUpdateFinishCallbacks($this->getVersionsToUpdate()); + + return true; + } + + /** + * Add slash at the end of the path. + * + * @param string $dir + * @return string + */ + public function addTrailingSlash(string $dir): string + { + if (substr($dir, - 1) !== DIRECTORY_SEPARATOR) { + $dir .= DIRECTORY_SEPARATOR; + } + + return $dir; + } + + /** + * Add callback which is executed after each update finished. + * + * @param callable $callback + * @return $this + */ + public function onEachUpdateFinish(callable $callback): self + { + $this->onEachUpdateFinishCallbacks[] = $callback; + + return $this; + } + + /** + * Add callback which is executed after all updates finished. + * + * @param callable $callback + * @return $this + */ + public function setOnAllUpdateFinishCallbacks(callable $callback): self + { + $this->onAllUpdateFinishCallbacks[] = $callback; + + return $this; + } + + /** + * Run callbacks after each update finished. + * + * @param string $updateVersion + * @param bool $simulate + * @return void + */ + private function runOnEachUpdateFinishCallbacks(string $updateVersion, bool $simulate): void + { + foreach ($this->onEachUpdateFinishCallbacks as $callback) { + $callback($updateVersion, $simulate); + } + } + + /** + * Run callbacks after all updates finished. + * + * @param array $updatedVersions + * @return void + */ + private function runOnAllUpdateFinishCallbacks(array $updatedVersions): void + { + foreach ($this->onAllUpdateFinishCallbacks as $callback) { + $callback($updatedVersions); + } + } +} diff --git a/msd/vendor/visualappeal/php-auto-update/src/Exceptions/DownloadException.php b/msd/vendor/visualappeal/php-auto-update/src/Exceptions/DownloadException.php new file mode 100644 index 0000000..b5fb70f --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/src/Exceptions/DownloadException.php @@ -0,0 +1,5 @@ +_update = new AutoUpdate(__DIR__ . DIRECTORY_SEPARATOR . 'temp', __DIR__ . DIRECTORY_SEPARATOR . 'install'); + $this->_update->setCurrentVersion('0.1.0'); + $this->_update->setUpdateUrl(__DIR__ . DIRECTORY_SEPARATOR . 'fixtures'); + $logger = new Monolog\Logger("default"); + $logger->pushHandler(new Monolog\Handler\StreamHandler(__DIR__ . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . 'update.log')); + $this->_update->setLogger($logger); + } + + /** + * Unset the auto update. + */ + protected function tearDown(): void + { + unset($this->_update); + $this->_update = null; + } + + /** + * Test creation of class instance. + */ + public function testInit(): void + { + self::assertInstanceOf(AutoUpdate::class, $this->_update); + } + + /** + * Test if errors get catched if no update file was found. + * + * @throws ParserException|\Psr\SimpleCache\InvalidArgumentException + */ + public function testErrorUpdateCheck(): void + { + $this->expectException(DownloadException::class); + + $this->_update->setUpdateFile('404.json'); + $this->_update->checkUpdate(); + + self::assertFalse($this->_update->newVersionAvailable()); + self::assertCount(0, $this->_update->getVersionsToUpdate()); + } + + /** + * Test if new update is available with a json file. + * + * @throws DownloadException + * @throws ParserException|\Psr\SimpleCache\InvalidArgumentException + */ + public function testJsonNewVersion(): void + { + $this->_update->setUpdateFile('updateAvailable.json'); + $response = $this->_update->checkUpdate(); + + self::assertTrue($response); + self::assertTrue($this->_update->newVersionAvailable()); + self::assertEquals('0.2.1', $this->_update->getLatestVersion()); + + $newVersions = $this->_update->getVersionsToUpdate(); + self::assertCount(2, $newVersions); + self::assertEquals('0.2.0', $newVersions[0]); + self::assertEquals('0.2.1', $newVersions[1]); + } + + /** + * Test if NO new update is available with a json file. + * + * @throws DownloadException + * @throws ParserException|\Psr\SimpleCache\InvalidArgumentException + */ + public function testJsonNoNewVersion(): void + { + $this->_update->setUpdateFile('noUpdateAvailable.json'); + $response = $this->_update->checkUpdate(); + + self::assertEquals(AutoUpdate::NO_UPDATE_AVAILABLE, $response); + self::assertFalse($this->_update->newVersionAvailable()); + self::assertCount(0, $this->_update->getVersionsToUpdate()); + } + + /** + * Test if new update is available with a ini file. + * + * @throws DownloadException + * @throws ParserException|\Psr\SimpleCache\InvalidArgumentException + */ + public function testIniNewVersion(): void + { + $this->_update->setUpdateFile('updateAvailable.ini'); + $response = $this->_update->checkUpdate(); + + self::assertTrue($response); + self::assertTrue($this->_update->newVersionAvailable()); + self::assertEquals('0.2.1', $this->_update->getLatestVersion()); + + $newVersions = $this->_update->getVersionsToUpdate(); + self::assertCount(2, $newVersions); + self::assertEquals('0.2.0', $newVersions[0]); + self::assertEquals('0.2.1', $newVersions[1]); + } + + /** + * Test if NO new update is available with a ini file. + * + * @throws DownloadException + * @throws ParserException|\Psr\SimpleCache\InvalidArgumentException + */ + public function testIniNoNewVersion(): void + { + $this->_update->setUpdateFile('noUpdateAvailable.ini'); + $response = $this->_update->checkUpdate(); + + self::assertEquals(AutoUpdate::NO_UPDATE_AVAILABLE, $response); + self::assertFalse($this->_update->newVersionAvailable()); + self::assertCount(0, $this->_update->getVersionsToUpdate()); + } + + /** + * Ensure that a new dev version is available. + * + * @throws DownloadException + * @throws ParserException|\Psr\SimpleCache\InvalidArgumentException + */ + public function testBranchDev(): void + { + $this->_update->setUpdateFile('updateAvailable.json'); + $this->_update->setBranch('dev'); + $response = $this->_update->checkUpdate(); + + self::assertTrue($response); + } + + /** + * Ensure that no new master version is available + * + * @throws DownloadException + * @throws ParserException|\Psr\SimpleCache\InvalidArgumentException + */ + public function testBranchMaster(): void + { + $this->_update->setUpdateFile('noUpdateAvailable.json'); + $this->_update->setBranch('master'); + $response = $this->_update->checkUpdate(); + + self::assertEquals(AutoUpdate::NO_UPDATE_AVAILABLE, $response); + } + + /** + * Test the trailing slash method. + */ + public function testTrailingSlashes(): void + { + $dir = DIRECTORY_SEPARATOR . 'test'; + self::assertEquals(DIRECTORY_SEPARATOR . 'test' . DIRECTORY_SEPARATOR, $this->_update->addTrailingSlash($dir)); + + $dir = DIRECTORY_SEPARATOR . 'test' . DIRECTORY_SEPARATOR; + self::assertEquals(DIRECTORY_SEPARATOR . 'test' . DIRECTORY_SEPARATOR, $this->_update->addTrailingSlash($dir)); + } +} diff --git a/msd/vendor/visualappeal/php-auto-update/tests/UnitTests.xml b/msd/vendor/visualappeal/php-auto-update/tests/UnitTests.xml new file mode 100644 index 0000000..dd210d4 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/tests/UnitTests.xml @@ -0,0 +1,7 @@ + + + + ./ + + + diff --git a/msd/vendor/visualappeal/php-auto-update/tests/fixtures/noUpdateAvailable.ini b/msd/vendor/visualappeal/php-auto-update/tests/fixtures/noUpdateAvailable.ini new file mode 100644 index 0000000..1a22752 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/tests/fixtures/noUpdateAvailable.ini @@ -0,0 +1,8 @@ +[0.1.0] +url = http://127.0.0.1:80/example/server/0.1.0.zip + +[0.0.1] +url = http://127.0.0.1:80/example/server/0.0.1.zip + +[0.0.4] +url = http://127.0.0.1:80/example/server/0.0.4.zip diff --git a/msd/vendor/visualappeal/php-auto-update/tests/fixtures/noUpdateAvailable.json b/msd/vendor/visualappeal/php-auto-update/tests/fixtures/noUpdateAvailable.json new file mode 100644 index 0000000..794a8a3 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/tests/fixtures/noUpdateAvailable.json @@ -0,0 +1,5 @@ +{ + "0.1.0": "http://127.0.0.1:80/example/server/0.1.0.zip", + "0.0.1": "http://127.0.0.1:80/example/server/0.0.1.zip", + "0.0.4": "http://127.0.0.1:80/example/server/0.0.4.zip" +} diff --git a/msd/vendor/visualappeal/php-auto-update/tests/fixtures/noUpdateAvailable.json.master b/msd/vendor/visualappeal/php-auto-update/tests/fixtures/noUpdateAvailable.json.master new file mode 100644 index 0000000..794a8a3 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/tests/fixtures/noUpdateAvailable.json.master @@ -0,0 +1,5 @@ +{ + "0.1.0": "http://127.0.0.1:80/example/server/0.1.0.zip", + "0.0.1": "http://127.0.0.1:80/example/server/0.0.1.zip", + "0.0.4": "http://127.0.0.1:80/example/server/0.0.4.zip" +} diff --git a/msd/vendor/visualappeal/php-auto-update/tests/fixtures/updateAvailable.ini b/msd/vendor/visualappeal/php-auto-update/tests/fixtures/updateAvailable.ini new file mode 100644 index 0000000..c63b84b --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/tests/fixtures/updateAvailable.ini @@ -0,0 +1,8 @@ +[0.1.0] +url = http://127.0.0.1:80/example/server/0.1.0.zip + +[0.2.0] +url = http://127.0.0.1:80/example/server/0.2.0.zip + +[0.2.1] +url = http://127.0.0.1:80/example/server/0.2.1.zip diff --git a/msd/vendor/visualappeal/php-auto-update/tests/fixtures/updateAvailable.json b/msd/vendor/visualappeal/php-auto-update/tests/fixtures/updateAvailable.json new file mode 100644 index 0000000..d478eb3 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/tests/fixtures/updateAvailable.json @@ -0,0 +1,5 @@ +{ + "0.1.0": "http://127.0.0.1:80/example/server/0.1.0.zip", + "0.2.0": "http://127.0.0.1:80/example/server/0.2.0.zip", + "0.2.1": "http://127.0.0.1:80/example/server/0.2.1.zip" +} diff --git a/msd/vendor/visualappeal/php-auto-update/tests/fixtures/updateAvailable.json.dev b/msd/vendor/visualappeal/php-auto-update/tests/fixtures/updateAvailable.json.dev new file mode 100644 index 0000000..d478eb3 --- /dev/null +++ b/msd/vendor/visualappeal/php-auto-update/tests/fixtures/updateAvailable.json.dev @@ -0,0 +1,5 @@ +{ + "0.1.0": "http://127.0.0.1:80/example/server/0.1.0.zip", + "0.2.0": "http://127.0.0.1:80/example/server/0.2.0.zip", + "0.2.1": "http://127.0.0.1:80/example/server/0.2.1.zip" +}